blob: 91e2e3f0b87200461ebade9e04059485b25b8203 [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 Glassc05ed002020-05-10 11:40:11 -060012#include <linux/delay.h>
Arun Parameswaran36645f42019-09-12 11:06:08 -070013
14DECLARE_GLOBAL_DATA_PTR;
15
16struct sdhci_iproc_host {
17 struct sdhci_host host;
18 u32 shadow_cmd;
19 u32 shadow_blk;
20};
21
22#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
23
24static inline struct sdhci_iproc_host *to_iproc(struct sdhci_host *host)
25{
26 return (struct sdhci_iproc_host *)host;
27}
28
29#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
30static u32 sdhci_iproc_readl(struct sdhci_host *host, int reg)
31{
32 u32 val = readl(host->ioaddr + reg);
33#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
34 printf("%s %d: readl [0x%02x] 0x%08x\n",
35 host->name, host->index, reg, val);
36#endif
37 return val;
38}
39
40static u16 sdhci_iproc_readw(struct sdhci_host *host, int reg)
41{
42 u32 val = sdhci_iproc_readl(host, (reg & ~3));
43 u16 word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
44 return word;
45}
46
47static u8 sdhci_iproc_readb(struct sdhci_host *host, int reg)
48{
49 u32 val = sdhci_iproc_readl(host, (reg & ~3));
50 u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
51 return byte;
52}
53
54static void sdhci_iproc_writel(struct sdhci_host *host, u32 val, int reg)
55{
56 u32 clock = 0;
57#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS_TRACE
58 printf("%s %d: writel [0x%02x] 0x%08x\n",
59 host->name, host->index, reg, val);
60#endif
61 writel(val, host->ioaddr + reg);
62
63 if (host->mmc)
64 clock = host->mmc->clock;
65 if (clock <= 400000) {
66 /* Round up to micro-second four SD clock delay */
67 if (clock)
68 udelay((4 * 1000000 + clock - 1) / clock);
69 else
70 udelay(10);
71 }
72}
73
74/*
75 * The Arasan has a bugette whereby it may lose the content of successive
76 * writes to the same register that are within two SD-card clock cycles of
77 * each other (a clock domain crossing problem). The data
78 * register does not have this problem, which is just as well - otherwise we'd
79 * have to nobble the DMA engine too.
80 *
81 * This wouldn't be a problem with the code except that we can only write the
82 * controller with 32-bit writes. So two different 16-bit registers are
83 * written back to back creates the problem.
84 *
85 * In reality, this only happens when SDHCI_BLOCK_SIZE and SDHCI_BLOCK_COUNT
86 * are written followed by SDHCI_TRANSFER_MODE and SDHCI_COMMAND.
87 * The BLOCK_SIZE and BLOCK_COUNT are meaningless until a command issued so
88 * the work around can be further optimized. We can keep shadow values of
89 * BLOCK_SIZE, BLOCK_COUNT, and TRANSFER_MODE until a COMMAND is issued.
90 * Then, write the BLOCK_SIZE+BLOCK_COUNT in a single 32-bit write followed
91 * by the TRANSFER+COMMAND in another 32-bit write.
92 */
93static void sdhci_iproc_writew(struct sdhci_host *host, u16 val, int reg)
94{
95 struct sdhci_iproc_host *iproc_host = to_iproc(host);
96 u32 word_shift = REG_OFFSET_IN_BITS(reg);
97 u32 mask = 0xffff << word_shift;
98 u32 oldval, newval;
99
100 if (reg == SDHCI_COMMAND) {
101 /* Write the block now as we are issuing a command */
102 if (iproc_host->shadow_blk != 0) {
103 sdhci_iproc_writel(host, iproc_host->shadow_blk,
104 SDHCI_BLOCK_SIZE);
105 iproc_host->shadow_blk = 0;
106 }
107 oldval = iproc_host->shadow_cmd;
108 } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
109 /* Block size and count are stored in shadow reg */
110 oldval = iproc_host->shadow_blk;
111 } else {
112 /* Read reg, all other registers are not shadowed */
113 oldval = sdhci_iproc_readl(host, (reg & ~3));
114 }
115 newval = (oldval & ~mask) | (val << word_shift);
116
117 if (reg == SDHCI_TRANSFER_MODE) {
118 /* Save the transfer mode until the command is issued */
119 iproc_host->shadow_cmd = newval;
120 } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
121 /* Save the block info until the command is issued */
122 iproc_host->shadow_blk = newval;
123 } else {
124 /* Command or other regular 32-bit write */
125 sdhci_iproc_writel(host, newval, reg & ~3);
126 }
127}
128
129static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
130{
131 u32 oldval = sdhci_iproc_readl(host, (reg & ~3));
132 u32 byte_shift = REG_OFFSET_IN_BITS(reg);
133 u32 mask = 0xff << byte_shift;
134 u32 newval = (oldval & ~mask) | (val << byte_shift);
135
136 sdhci_iproc_writel(host, newval, reg & ~3);
137}
138#endif
139
Rayagonda Kokatanur7a65b8b2020-03-31 11:04:05 +0530140static int sdhci_iproc_set_ios_post(struct sdhci_host *host)
Arun Parameswaran36645f42019-09-12 11:06:08 -0700141{
142 u32 ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
143
144 /* Reset UHS mode bits */
145 ctrl &= ~SDHCI_CTRL_UHS_MASK;
146
147 if (host->mmc->ddr_mode)
148 ctrl |= UHS_DDR50_BUS_SPEED;
149
150 sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
Rayagonda Kokatanur7a65b8b2020-03-31 11:04:05 +0530151
152 return 0;
Arun Parameswaran36645f42019-09-12 11:06:08 -0700153}
154
155static struct sdhci_ops sdhci_platform_ops = {
156#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
157 .read_l = sdhci_iproc_readl,
158 .read_w = sdhci_iproc_readw,
159 .read_b = sdhci_iproc_readb,
160 .write_l = sdhci_iproc_writel,
161 .write_w = sdhci_iproc_writew,
162 .write_b = sdhci_iproc_writeb,
163#endif
164 .set_ios_post = sdhci_iproc_set_ios_post,
165};
166
167struct iproc_sdhci_plat {
168 struct mmc_config cfg;
169 struct mmc mmc;
170};
171
172static int iproc_sdhci_probe(struct udevice *dev)
173{
174 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
175 struct iproc_sdhci_plat *plat = dev_get_platdata(dev);
176 struct sdhci_host *host = dev_get_priv(dev);
177 struct sdhci_iproc_host *iproc_host;
178 int node = dev_of_offset(dev);
179 u32 f_min_max[2];
180 int ret;
181
Bharat Kumar Reddy Gootyd5b85002020-03-31 11:04:03 +0530182 iproc_host = malloc(sizeof(struct sdhci_iproc_host));
Arun Parameswaran36645f42019-09-12 11:06:08 -0700183 if (!iproc_host) {
184 printf("%s: sdhci host malloc fail!\n", __func__);
185 return -ENOMEM;
186 }
187 iproc_host->shadow_cmd = 0;
188 iproc_host->shadow_blk = 0;
189
190 host->name = dev->name;
191 host->ioaddr = (void *)devfdt_get_addr(dev);
192 host->voltages = MMC_VDD_165_195 |
193 MMC_VDD_32_33 | MMC_VDD_33_34;
Bharat Kumar Reddy Gooty2bb02b12020-03-31 11:04:04 +0530194 host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B;
Arun Parameswaran36645f42019-09-12 11:06:08 -0700195 host->host_caps = MMC_MODE_DDR_52MHz;
196 host->index = fdtdec_get_uint(gd->fdt_blob, node, "index", 0);
197 host->ops = &sdhci_platform_ops;
198 host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
199 ret = fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
200 "clock-freq-min-max", f_min_max, 2);
201 if (ret) {
202 printf("sdhci: clock-freq-min-max not found\n");
Bharat Kumar Reddy Gootyd5b85002020-03-31 11:04:03 +0530203 free(iproc_host);
Arun Parameswaran36645f42019-09-12 11:06:08 -0700204 return ret;
205 }
206 host->max_clk = f_min_max[1];
207 host->bus_width = fdtdec_get_int(gd->fdt_blob,
208 dev_of_offset(dev), "bus-width", 4);
209
210 /* Update host_caps for 8 bit bus width */
211 if (host->bus_width == 8)
212 host->host_caps |= MMC_MODE_8BIT;
213
214 memcpy(&iproc_host->host, host, sizeof(struct sdhci_host));
215
Rayagonda Kokatanur29617ca2020-03-31 11:04:06 +0530216 iproc_host->host.mmc = &plat->mmc;
217 iproc_host->host.mmc->dev = dev;
218 iproc_host->host.mmc->priv = &iproc_host->host;
219 upriv->mmc = iproc_host->host.mmc;
220
Arun Parameswaran36645f42019-09-12 11:06:08 -0700221 ret = sdhci_setup_cfg(&plat->cfg, &iproc_host->host,
222 f_min_max[1], f_min_max[0]);
Bharat Kumar Reddy Gootyd5b85002020-03-31 11:04:03 +0530223 if (ret) {
224 free(iproc_host);
Arun Parameswaran36645f42019-09-12 11:06:08 -0700225 return ret;
Bharat Kumar Reddy Gootyd5b85002020-03-31 11:04:03 +0530226 }
Arun Parameswaran36645f42019-09-12 11:06:08 -0700227
Arun Parameswaran36645f42019-09-12 11:06:08 -0700228 return sdhci_probe(dev);
229}
230
231static int iproc_sdhci_bind(struct udevice *dev)
232{
233 struct iproc_sdhci_plat *plat = dev_get_platdata(dev);
234
235 return sdhci_bind(dev, &plat->mmc, &plat->cfg);
236}
237
238static const struct udevice_id iproc_sdhci_ids[] = {
239 { .compatible = "brcm,iproc-sdhci" },
240 { }
241};
242
243U_BOOT_DRIVER(iproc_sdhci_drv) = {
244 .name = "iproc_sdhci",
245 .id = UCLASS_MMC,
246 .of_match = iproc_sdhci_ids,
247 .ops = &sdhci_ops,
248 .bind = iproc_sdhci_bind,
249 .probe = iproc_sdhci_probe,
250 .priv_auto_alloc_size = sizeof(struct sdhci_host),
251 .platdata_auto_alloc_size = sizeof(struct iproc_sdhci_plat),
252};