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