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