blob: 7ab74ff117a697fe1d89b0acb7c7c66f54d6eafe [file] [log] [blame]
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -07001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2019 Broadcom.
4 *
5 */
6
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -07007#include <dm.h>
8#include <errno.h>
9#include <malloc.h>
10#include <sdhci.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060011#include <asm/global_data.h>
Bharat Gooty67565bd2021-02-26 11:45:13 +053012#include "mmc_private.h"
Simon Glassdbd79542020-05-10 11:40:11 -060013#include <linux/delay.h>
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -070014
Bharat Gooty67565bd2021-02-26 11:45:13 +053015#define MAX_TUNING_LOOP 40
16
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -070017DECLARE_GLOBAL_DATA_PTR;
18
19struct sdhci_iproc_host {
20 struct sdhci_host host;
21 u32 shadow_cmd;
22 u32 shadow_blk;
23};
24
25#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
26
27static inline struct sdhci_iproc_host *to_iproc(struct sdhci_host *host)
28{
29 return (struct sdhci_iproc_host *)host;
30}
31
32#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
33static u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
34{
35 u32 val = readl(host->ioaddr + reg);
36#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
37 printf("%s %d: readl [0x%02x] 0x%08x\n",
38 host->name, host->index, reg, val);
39#endif
40 return val;
41}
42
43static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
44{
45 u32 val = sdhci_iproc_readl(host, (reg & ~3));
46 u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
47 return word;
48}
49
50static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
51{
52 u32 val = sdhci_iproc_readl(host, (reg & ~3));
53 u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
54 return byte;
55}
56
57static void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
58{
59 u32 clock = 0;
60#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
61 printf("%s %d: writel [0x%02x] 0x%08x\n",
62 host->name, host->index, reg, val);
63#endif
64 writel(val, host->ioaddr + reg);
65
66 if (host->mmc)
67 clock = host->mmc->clock;
68 if (clock <= 400000) {
69 /* Round up to micro-second four SD clock delay */
70 if (clock)
71 udelay((4 * 1000000 + clock - 1) / clock);
72 else
73 udelay(10);
74 }
75}
76
77/*
78 * The Arasan has a bugette whereby it may lose the content of successive
79 * writes to the same register that are within two SD-card clock cycles of
80 * each other (a clock domain crossing problem). The data
81 * register does not have this problem, which is just as well - otherwise we'd
82 * have to nobble the DMA engine too.
83 *
84 * This wouldn't be a problem with the code except that we can only write the
85 * controller with 32-bit writes. So two different 16-bit registers are
86 * written back to back creates the problem.
87 *
88 * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
89 * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
90 * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
91 * the work around can be further optimized. We can keep shadow values of
92 * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
93 * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
94 * by the TRANSFER+COMMAND in another 32-bit write.
95 */
96static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
97{
98 struct sdhci_iproc_host *iproc_host = to_iproc(host);
99 u32 word_shift = REG_OFFSET_IN_BITS(reg);
100 u32 mask = 0xffff << word_shift;
101 u32 oldval, newval;
102
103 if (reg == SDHCI_COMMAND) {
104 /* Write the block now as we are issuing a command */
105 if (iproc_host->shadow_blk != 0) {
106 sdhci_iproc_writel(host, iproc_host->shadow_blk,
107 SDHCI_BLOCK_SIZE);
108 iproc_host->shadow_blk = 0;
109 }
110 oldval = iproc_host->shadow_cmd;
111 } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
112 /* Block size and count are stored in shadow reg */
113 oldval = iproc_host->shadow_blk;
114 } else {
115 /* Read reg, all other registers are not shadowed */
116 oldval = sdhci_iproc_readl(host, (reg & ~3));
117 }
118 newval = (oldval & ~mask) | (val << word_shift);
119
120 if (reg == SDHCI_TRANSFER_MODE) {
121 /* Save the transfer mode until the command is issued */
122 iproc_host->shadow_cmd = newval;
123 } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
124 /* Save the block info until the command is issued */
125 iproc_host->shadow_blk = newval;
126 } else {
127 /* Command or other regular 32-bit write */
128 sdhci_iproc_writel(host, newval, reg & ~3);
129 }
130}
131
132static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
133{
134 u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
135 u32 byte_shift = REG_OFFSET_IN_BITS(reg);
136 u32 mask = 0xff << byte_shift;
137 u32 newval = (oldval & ~mask) | (val << byte_shift);
138
139 sdhci_iproc_writel(host, newval, reg & ~3);
140}
141#endif
142
Rayagonda Kokatanuree6ad592020-03-31 11:04:05 +0530143static int sdhci_iproc_set_ios_post(struct sdhci_host *host)
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700144{
Bharat Gooty67565bd2021-02-26 11:45:13 +0530145 struct mmc *mmc = (struct mmc *)host->mmc;
146 u32 ctrl;
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700147
Bharat Gooty67565bd2021-02-26 11:45:13 +0530148 if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
149 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
150 ctrl |= SDHCI_CTRL_VDD_180;
151 sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
152 }
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700153
Bharat Gooty67565bd2021-02-26 11:45:13 +0530154 sdhci_set_uhs_timing(host);
155 return 0;
156}
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700157
Bharat Gooty67565bd2021-02-26 11:45:13 +0530158static void sdhci_start_tuning(struct sdhci_host *host)
159{
160 u32 ctrl;
161
162 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
163 ctrl |= SDHCI_CTRL_EXEC_TUNING;
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700164 sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
Rayagonda Kokatanuree6ad592020-03-31 11:04:05 +0530165
Bharat Gooty67565bd2021-02-26 11:45:13 +0530166 sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
167 sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
168}
169
170static void sdhci_end_tuning(struct sdhci_host *host)
171{
172 /* Enable only interrupts served by the SD controller */
173 sdhci_writel(host, SDHCI_INT_DATA_MASK | SDHCI_INT_CMD_MASK,
174 SDHCI_INT_ENABLE);
175 /* Mask all sdhci interrupt sources */
176 sdhci_writel(host, 0x0, SDHCI_SIGNAL_ENABLE);
177}
178
179static int sdhci_iproc_execute_tuning(struct mmc *mmc, u8 opcode)
180{
181 struct mmc_cmd cmd;
182 u32 ctrl;
183 u32 blocksize = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 64);
184 struct sdhci_host *host = dev_get_priv(mmc->dev);
185 char tuning_loop_counter = MAX_TUNING_LOOP;
186 int ret = 0;
187
188 sdhci_start_tuning(host);
189
190 cmd.cmdidx = opcode;
191 cmd.resp_type = MMC_RSP_R1;
192 cmd.cmdarg = 0;
193
194 if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200 && mmc->bus_width == 8)
195 blocksize = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 128);
196
197 sdhci_writew(host, blocksize, SDHCI_BLOCK_SIZE);
198 sdhci_writew(host, 1, SDHCI_BLOCK_COUNT);
199 sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
200
201 do {
202 mmc_send_cmd(mmc, &cmd, NULL);
203 if (opcode == MMC_CMD_SEND_TUNING_BLOCK)
204 /*
205 * For tuning command, do not do busy loop. As tuning
206 * is happening (CLK-DATA latching for setup/hold time
207 * requirements), give time to complete
208 */
209 udelay(1);
210
211 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
212
213 if (tuning_loop_counter-- == 0)
214 break;
215
216 } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
217
218 if (tuning_loop_counter < 0 || (!(ctrl & SDHCI_CTRL_TUNED_CLK))) {
219 ctrl &= ~(SDHCI_CTRL_TUNED_CLK | SDHCI_CTRL_EXEC_TUNING);
220 sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL2);
221 printf("%s:Tuning failed, opcode = 0x%02x\n", __func__, opcode);
222 ret = -EIO;
223 }
224
225 sdhci_end_tuning(host);
226
227 return ret;
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700228}
229
230static struct sdhci_ops sdhci_platform_ops = {
231#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
232 .read_l = sdhci_iproc_readl,
233 .read_w = sdhci_iproc_readw,
234 .read_b = sdhci_iproc_readb,
235 .write_l = sdhci_iproc_writel,
236 .write_w = sdhci_iproc_writew,
237 .write_b = sdhci_iproc_writeb,
238#endif
239 .set_ios_post = sdhci_iproc_set_ios_post,
Bharat Gooty67565bd2021-02-26 11:45:13 +0530240 .platform_execute_tuning = sdhci_iproc_execute_tuning,
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700241};
242
243struct iproc_sdhci_plat {
244 struct mmc_config cfg;
245 struct mmc mmc;
246};
247
248static int iproc_sdhci_probe(struct udevice *dev)
249{
250 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
Simon Glassfa20e932020-12-03 16:55:20 -0700251 struct iproc_sdhci_plat *plat = dev_get_plat(dev);
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700252 struct sdhci_host *host = dev_get_priv(dev);
253 struct sdhci_iproc_host *iproc_host;
254 int node = dev_of_offset(dev);
255 u32 f_min_max[2];
256 int ret;
257
Bharat Kumar Reddy Gootyc9652402020-03-31 11:04:03 +0530258 iproc_host = malloc(sizeof(struct sdhci_iproc_host));
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700259 if (!iproc_host) {
260 printf("%s: sdhci host malloc fail!\n", __func__);
261 return -ENOMEM;
262 }
263 iproc_host->shadow_cmd = 0;
264 iproc_host->shadow_blk = 0;
265
266 host->name = dev->name;
Masahiro Yamada1096ae12020-07-17 14:36:46 +0900267 host->ioaddr = dev_read_addr_ptr(dev);
Bharat Gooty67565bd2021-02-26 11:45:13 +0530268 host->quirks = SDHCI_QUIRK_BROKEN_R1B;
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700269 host->host_caps = MMC_MODE_DDR_52MHz;
270 host->index = fdtdec_get_uint(gd->fdt_blob, node, "index", 0);
271 host->ops = &sdhci_platform_ops;
272 host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
273 ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
274 "clock-freq-min-max", f_min_max, 2);
275 if (ret) {
276 printf("sdhci: clock-freq-min-max not found\n");
Bharat Kumar Reddy Gootyc9652402020-03-31 11:04:03 +0530277 free(iproc_host);
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700278 return ret;
279 }
280 host->max_clk = f_min_max[1];
281 host->bus_width = fdtdec_get_int(gd->fdt_blob,
282 dev_of_offset(dev), "bus-width", 4);
283
284 /* Update host_caps for 8 bit bus width */
285 if (host->bus_width == 8)
286 host->host_caps |= MMC_MODE_8BIT;
287
288 memcpy(&iproc_host->host, host, sizeof(struct sdhci_host));
289
Rayagonda Kokatanur5b0dad92020-03-31 11:04:06 +0530290 iproc_host->host.mmc = &plat->mmc;
291 iproc_host->host.mmc->dev = dev;
292 iproc_host->host.mmc->priv = &iproc_host->host;
293 upriv->mmc = iproc_host->host.mmc;
294
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700295 ret = sdhci_setup_cfg(&plat->cfg, &iproc_host->host,
296 f_min_max[1], f_min_max[0]);
Bharat Kumar Reddy Gootyc9652402020-03-31 11:04:03 +0530297 if (ret) {
298 free(iproc_host);
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700299 return ret;
Bharat Kumar Reddy Gootyc9652402020-03-31 11:04:03 +0530300 }
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700301
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700302 return sdhci_probe(dev);
303}
304
305static int iproc_sdhci_bind(struct udevice *dev)
306{
Simon Glassfa20e932020-12-03 16:55:20 -0700307 struct iproc_sdhci_plat *plat = dev_get_plat(dev);
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700308
309 return sdhci_bind(dev, &plat->mmc, &plat->cfg);
310}
311
312static const struct udevice_id iproc_sdhci_ids[] = {
313 { .compatible = "brcm,iproc-sdhci" },
314 { }
315};
316
317U_BOOT_DRIVER(iproc_sdhci_drv) = {
318 .name = "iproc_sdhci",
319 .id = UCLASS_MMC,
320 .of_match = iproc_sdhci_ids,
321 .ops = &sdhci_ops,
322 .bind = iproc_sdhci_bind,
323 .probe = iproc_sdhci_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700324 .priv_auto = sizeof(struct sdhci_host),
Simon Glass71fa5b42020-12-03 16:55:18 -0700325 .plat_auto = sizeof(struct iproc_sdhci_plat),
Arun Parameswaran2f4f34a2019-09-12 11:06:08 -0700326};