]> Git Repo - J-u-boot.git/blob - drivers/mmc/sdhci-cadence6.c
Merge patch series "Fix various bugs"
[J-u-boot.git] / drivers / mmc / sdhci-cadence6.c
1 // SPDX-License-Identifier: GPL-2.0-or-platform_driver
2 /*
3  * Copyright (C) 2023 Starfive.
4  *   Author: Kuan Lim Lee <[email protected]>
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
63 struct sdhci_cdns6_phy_cfg {
64         const char *property;
65         u32 val;
66 };
67
68 struct sdhci_cdns6_ctrl_cfg {
69         const char *property;
70         u32 val;
71 };
72
73 static 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
80 static 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
87 static 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
94 static 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
101 static 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
108 static 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
115 static 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
122 static 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
129 static 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
136 static 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
143 static 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
149 static 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
155 static 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
181 int 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
274 int 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
279 int 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 }
This page took 0.042465 seconds and 4 git commands to generate.