blob: a5ed87321ab2bfebec81cf8b336a00312f758d2c [file] [log] [blame]
Kuan Lim Lee93986cb2023-11-28 14:38:30 +08001// SPDX-License-Identifier: GPL-2.0-or-platform_driver
2/*
3 * Copyright (C) 2023 Starfive.
4 * Author: Kuan Lim Lee <kuanlim.lee@starfivetech.com>
5 */
6
7#include <dm.h>
8#include <asm/global_data.h>
9#include <dm/device_compat.h>
10#include <linux/bitfield.h>
11#include <linux/bitops.h>
12#include <linux/bug.h>
13#include <linux/io.h>
14#include <linux/iopoll.h>
15#include <linux/sizes.h>
16#include <linux/libfdt.h>
17#include <mmc.h>
18#include <sdhci.h>
19#include "sdhci-cadence.h"
20
21/* IO Delay Information */
22#define SDHCI_CDNS_HRS07 0X1C
23#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20, 16)
24#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4, 0)
25
26/* PHY Control and Status */
27#define SDHCI_CDNS_HRS09 0x24
28#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16)
29#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15)
30#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3)
31#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2)
32#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1)
33#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0)
34
35/* SDCLK adjustment */
36#define SDHCI_CDNS_HRS10 0x28
37#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19, 16)
38
39/* CMD/DAT output delay */
40#define SDHCI_CDNS_HRS16 0x40
41
42/* PHY Special Function Registers */
43/* register to control the DQ related timing */
44#define PHY_DQ_TIMING_REG_ADDR 0x2000
45
46/* register to control the DQS related timing */
47#define PHY_DQS_TIMING_REG_ADDR 0x2004
48
49/* register to control the gate and loopback control related timing */
50#define PHY_GATE_LPBK_CTRL_REG_ADDR 0x2008
51
52/* register to control the Master DLL logic */
53#define PHY_DLL_MASTER_CTRL_REG_ADDR 0x200C
54
55/* register to control the Slave DLL logic */
56#define PHY_DLL_SLAVE_CTRL_REG_ADDR 0x2010
57#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY GENMASK(31, 24)
58#define PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY GENMASK(7, 0)
59
60#define SDHCI_CDNS6_PHY_CFG_NUM 4
61#define SDHCI_CDNS6_CTRL_CFG_NUM 4
62
63struct sdhci_cdns6_phy_cfg {
64 const char *property;
65 u32 val;
66};
67
68struct sdhci_cdns6_ctrl_cfg {
69 const char *property;
70 u32 val;
71};
72
73static struct sdhci_cdns6_phy_cfg sd_ds_phy_cfgs[] = {
74 { "cdns,phy-dqs-timing-delay-sd-ds", 0x00380004, },
75 { "cdns,phy-gate-lpbk_ctrl-delay-sd-ds", 0x01A00040, },
76 { "cdns,phy-dll-slave-ctrl-sd-ds", 0x00000000, },
77 { "cdns,phy-dq-timing-delay-sd-ds", 0x00000001, },
78};
79
80static struct sdhci_cdns6_phy_cfg emmc_sdr_phy_cfgs[] = {
81 { "cdns,phy-dqs-timing-delay-semmc-sdr", 0x00380004, },
82 { "cdns,phy-gate-lpbk_ctrl-delay-emmc-sdr", 0x01A00040, },
83 { "cdns,phy-dll-slave-ctrl-emmc-sdr", 0x00000000, },
84 { "cdns,phy-dq-timing-delay-emmc-sdr", 0x00000001, },
85};
86
87static struct sdhci_cdns6_phy_cfg emmc_ddr_phy_cfgs[] = {
88 { "cdns,phy-dqs-timing-delay-emmc-ddr", 0x00380004, },
89 { "cdns,phy-gate-lpbk_ctrl-delay-emmc-ddr", 0x01A00040, },
90 { "cdns,phy-dll-slave-ctrl-emmc-ddr", 0x00000000, },
91 { "cdns,phy-dq-timing-delay-emmc-ddr", 0x10000001, },
92};
93
94static struct sdhci_cdns6_phy_cfg emmc_hs200_phy_cfgs[] = {
95 { "cdns,phy-dqs-timing-delay-emmc-hs200", 0x00380004, },
96 { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs200", 0x01A00040, },
97 { "cdns,phy-dll-slave-ctrl-emmc-hs200", 0x00DADA00, },
98 { "cdns,phy-dq-timing-delay-emmc-hs200", 0x00000001, },
99};
100
101static struct sdhci_cdns6_phy_cfg emmc_hs400_phy_cfgs[] = {
102 { "cdns,phy-dqs-timing-delay-emmc-hs400", 0x00280004, },
103 { "cdns,phy-gate-lpbk_ctrl-delay-emmc-hs400", 0x01A00040, },
104 { "cdns,phy-dll-slave-ctrl-emmc-hs400", 0x00DAD800, },
105 { "cdns,phy-dq-timing-delay-emmc-hs400", 0x00000001, },
106};
107
108static struct sdhci_cdns6_ctrl_cfg sd_ds_ctrl_cfgs[] = {
109 { "cdns,ctrl-hrs09-timing-delay-sd-ds", 0x0001800C, },
110 { "cdns,ctrl-hrs10-lpbk_ctrl-delay-sd-ds", 0x00020000, },
111 { "cdns,ctrl-hrs16-slave-ctrl-sd-ds", 0x00000000, },
112 { "cdns,ctrl-hrs07-timing-delay-sd-ds", 0x00080000, },
113};
114
115static struct sdhci_cdns6_ctrl_cfg emmc_sdr_ctrl_cfgs[] = {
116 { "cdns,ctrl-hrs09-timing-delay-emmc-sdr", 0x0001800C, },
117 { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-sdr", 0x00030000, },
118 { "cdns,ctrl-hrs16-slave-ctrl-emmc-sdr", 0x00000000, },
119 { "cdns,ctrl-hrs07-timing-delay-emmc-sdr", 0x00080000, },
120};
121
122static struct sdhci_cdns6_ctrl_cfg emmc_ddr_ctrl_cfgs[] = {
123 { "cdns,ctrl-hrs09-timing-delay-emmc-ddr", 0x0001800C, },
124 { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-ddr", 0x00020000, },
125 { "cdns,ctrl-hrs16-slave-ctrl-emmc-ddr", 0x11000001, },
126 { "cdns,ctrl-hrs07-timing-delay-emmc-ddr", 0x00090001, },
127};
128
129static struct sdhci_cdns6_ctrl_cfg emmc_hs200_ctrl_cfgs[] = {
130 { "cdns,ctrl-hrs09-timing-delay-emmc-hs200", 0x00018000, },
131 { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs200", 0x00080000, },
132 { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs200", 0x00000000, },
133 { "cdns,ctrl-hrs07-timing-delay-emmc-hs200", 0x00090000, },
134};
135
136static struct sdhci_cdns6_ctrl_cfg emmc_hs400_ctrl_cfgs[] = {
137 { "cdns,ctrl-hrs09-timing-delay-emmc-hs400", 0x00018000, },
138 { "cdns,ctrl-hrs10-lpbk_ctrl-delay-emmc-hs400", 0x00080000, },
139 { "cdns,ctrl-hrs16-slave-ctrl-emmc-hs400", 0x11000000, },
140 { "cdns,ctrl-hrs07-timing-delay-emmc-hs400", 0x00080000, },
141};
142
143static u32 sdhci_cdns6_read_phy_reg(struct sdhci_cdns_plat *plat, u32 addr)
144{
145 writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
146 return readl(plat->hrs_addr + SDHCI_CDNS_HRS05);
147}
148
149static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_plat *plat, u32 addr, u32 val)
150{
151 writel(addr, plat->hrs_addr + SDHCI_CDNS_HRS04);
152 writel(val, plat->hrs_addr + SDHCI_CDNS_HRS05);
153}
154
155static int sdhci_cdns6_reset_phy_dll(struct sdhci_cdns_plat *plat, bool reset)
156{
157 void __iomem *reg = plat->hrs_addr + SDHCI_CDNS_HRS09;
158 u32 tmp;
159 int ret;
160
161 tmp = readl(reg);
162 tmp &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET;
163
164 /* Switch On DLL Reset */
165 if (reset)
166 tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 0);
167 else
168 tmp |= FIELD_PREP(SDHCI_CDNS_HRS09_PHY_SW_RESET, 1);
169
170 writel(tmp, reg);
171
172 /* After reset, wait until HRS09.PHY_INIT_COMPLETE is set to 1 within 3000us*/
173 if (!reset) {
174 ret = readl_poll_timeout(reg, tmp, (tmp & SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE),
175 3000);
176 }
177
178 return ret;
179}
180
181int sdhci_cdns6_phy_adj(struct udevice *dev, struct sdhci_cdns_plat *plat, u32 mode)
182{
183 DECLARE_GLOBAL_DATA_PTR;
184 struct sdhci_cdns6_phy_cfg *sdhci_cdns6_phy_cfgs;
185 struct sdhci_cdns6_ctrl_cfg *sdhci_cdns6_ctrl_cfgs;
186 const fdt32_t *prop;
187 u32 tmp;
188 int i, ret;
189
190 switch (mode) {
191 case SDHCI_CDNS_HRS06_MODE_SD:
192 sdhci_cdns6_phy_cfgs = sd_ds_phy_cfgs;
193 sdhci_cdns6_ctrl_cfgs = sd_ds_ctrl_cfgs;
194 break;
195
196 case SDHCI_CDNS_HRS06_MODE_MMC_SDR:
197 sdhci_cdns6_phy_cfgs = emmc_sdr_phy_cfgs;
198 sdhci_cdns6_ctrl_cfgs = emmc_sdr_ctrl_cfgs;
199 break;
200
201 case SDHCI_CDNS_HRS06_MODE_MMC_DDR:
202 sdhci_cdns6_phy_cfgs = emmc_ddr_phy_cfgs;
203 sdhci_cdns6_ctrl_cfgs = emmc_ddr_ctrl_cfgs;
204 break;
205
206 case SDHCI_CDNS_HRS06_MODE_MMC_HS200:
207 sdhci_cdns6_phy_cfgs = emmc_hs200_phy_cfgs;
208 sdhci_cdns6_ctrl_cfgs = emmc_hs200_ctrl_cfgs;
209 break;
210
211 case SDHCI_CDNS_HRS06_MODE_MMC_HS400:
212 sdhci_cdns6_phy_cfgs = emmc_hs400_phy_cfgs;
213 sdhci_cdns6_ctrl_cfgs = emmc_hs400_ctrl_cfgs;
214 break;
215 default:
216 return -EINVAL;
217 }
218
219 for (i = 0; i < SDHCI_CDNS6_PHY_CFG_NUM; i++) {
220 prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
221 sdhci_cdns6_phy_cfgs[i].property, NULL);
222 if (prop)
223 sdhci_cdns6_phy_cfgs[i].val = *prop;
224 }
225
226 for (i = 0; i < SDHCI_CDNS6_CTRL_CFG_NUM; i++) {
227 prop = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
228 sdhci_cdns6_ctrl_cfgs[i].property, NULL);
229 if (prop)
230 sdhci_cdns6_ctrl_cfgs[i].val = *prop;
231 }
232
233 /* Switch On the DLL Reset */
234 sdhci_cdns6_reset_phy_dll(plat, true);
235
236 sdhci_cdns6_write_phy_reg(plat, PHY_DQS_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[0].val);
237 sdhci_cdns6_write_phy_reg(plat, PHY_GATE_LPBK_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[1].val);
238 sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, sdhci_cdns6_phy_cfgs[2].val);
239
240 /* Switch Off the DLL Reset */
241 ret = sdhci_cdns6_reset_phy_dll(plat, false);
242 if (ret) {
243 printf("sdhci_cdns6_reset_phy is not completed\n");
244 return ret;
245 }
246
247 /* Set PHY DQ TIMING control register */
248 sdhci_cdns6_write_phy_reg(plat, PHY_DQ_TIMING_REG_ADDR, sdhci_cdns6_phy_cfgs[3].val);
249
250 /* Set HRS09 register */
251 tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS09);
252 tmp &= ~(SDHCI_CDNS_HRS09_EXTENDED_WR_MODE |
253 SDHCI_CDNS_HRS09_EXTENDED_RD_MODE |
254 SDHCI_CDNS_HRS09_RDDATA_EN |
255 SDHCI_CDNS_HRS09_RDCMD_EN);
256 tmp |= sdhci_cdns6_ctrl_cfgs[0].val;
257 writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS09);
258
259 /* Set HRS10 register */
260 tmp = readl(plat->hrs_addr + SDHCI_CDNS_HRS10);
261 tmp &= ~SDHCI_CDNS_HRS10_HCSDCLKADJ;
262 tmp |= sdhci_cdns6_ctrl_cfgs[1].val;
263 writel(tmp, plat->hrs_addr + SDHCI_CDNS_HRS10);
264
265 /* Set HRS16 register */
266 writel(sdhci_cdns6_ctrl_cfgs[2].val, plat->hrs_addr + SDHCI_CDNS_HRS16);
267
268 /* Set HRS07 register */
269 writel(sdhci_cdns6_ctrl_cfgs[3].val, plat->hrs_addr + SDHCI_CDNS_HRS07);
270
271 return 0;
272}
273
274int sdhci_cdns6_phy_init(struct udevice *dev, struct sdhci_cdns_plat *plat)
275{
276 return sdhci_cdns6_phy_adj(dev, plat, SDHCI_CDNS_HRS06_MODE_SD);
277}
278
279int sdhci_cdns6_set_tune_val(struct sdhci_cdns_plat *plat, unsigned int val)
280{
281 u32 tmp, tuneval;
282
283 tuneval = (val * 256) / SDHCI_CDNS_MAX_TUNING_LOOP;
284
285 tmp = sdhci_cdns6_read_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR);
286 tmp &= ~(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY |
287 PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY);
288 tmp |= FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_CMD_DELAY, tuneval) |
289 FIELD_PREP(PHY_DLL_SLAVE_CTRL_REG_READ_DQS_DELAY, tuneval);
290 sdhci_cdns6_write_phy_reg(plat, PHY_DLL_SLAVE_CTRL_REG_ADDR, tmp);
291
292 return 0;
293}