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