1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2010 Marvell International Ltd. |
4 | * Zhangfei Gao <zhangfei.gao@marvell.com> |
5 | * Kevin Wang <dwang4@marvell.com> |
6 | * Jun Nie <njun@marvell.com> |
7 | * Qiming Wu <wuqm@marvell.com> |
8 | * Philip Rakity <prakity@marvell.com> |
9 | */ |
10 | |
11 | #include <linux/err.h> |
12 | #include <linux/init.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/module.h> |
16 | #include <linux/io.h> |
17 | #include <linux/mmc/card.h> |
18 | #include <linux/mmc/host.h> |
19 | #include <linux/platform_data/pxa_sdhci.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/of.h> |
22 | #include <linux/mmc/sdio.h> |
23 | #include <linux/mmc/mmc.h> |
24 | #include <linux/pinctrl/consumer.h> |
25 | |
26 | #include "sdhci.h" |
27 | #include "sdhci-pltfm.h" |
28 | |
29 | #define SD_FIFO_PARAM 0xe0 |
30 | #define DIS_PAD_SD_CLK_GATE 0x0400 /* Turn on/off Dynamic SD Clock Gating */ |
31 | #define CLK_GATE_ON 0x0200 /* Disable/enable Clock Gate */ |
32 | #define CLK_GATE_CTL 0x0100 /* Clock Gate Control */ |
33 | #define CLK_GATE_SETTING_BITS (DIS_PAD_SD_CLK_GATE | \ |
34 | CLK_GATE_ON | CLK_GATE_CTL) |
35 | |
36 | #define SD_CLOCK_BURST_SIZE_SETUP 0xe6 |
37 | #define SDCLK_SEL_SHIFT 8 |
38 | #define SDCLK_SEL_MASK 0x3 |
39 | #define SDCLK_DELAY_SHIFT 10 |
40 | #define SDCLK_DELAY_MASK 0x3c |
41 | |
42 | #define SD_CE_ATA_2 0xea |
43 | #define MMC_CARD 0x1000 |
44 | #define MMC_WIDTH 0x0100 |
45 | |
46 | struct sdhci_pxav2_host { |
47 | struct mmc_request *sdio_mrq; |
48 | struct pinctrl *pinctrl; |
49 | struct pinctrl_state *pins_default; |
50 | struct pinctrl_state *pins_cmd_gpio; |
51 | }; |
52 | |
53 | static void pxav2_reset(struct sdhci_host *host, u8 mask) |
54 | { |
55 | struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); |
56 | struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; |
57 | |
58 | sdhci_reset(host, mask); |
59 | |
60 | if (mask == SDHCI_RESET_ALL) { |
61 | u16 tmp = 0; |
62 | |
63 | /* |
64 | * tune timing of read data/command when crc error happen |
65 | * no performance impact |
66 | */ |
67 | if (pdata && pdata->clk_delay_sel == 1) { |
68 | tmp = readw(addr: host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); |
69 | |
70 | tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); |
71 | tmp |= (pdata->clk_delay_cycles & SDCLK_DELAY_MASK) |
72 | << SDCLK_DELAY_SHIFT; |
73 | tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); |
74 | tmp |= (1 & SDCLK_SEL_MASK) << SDCLK_SEL_SHIFT; |
75 | |
76 | writew(val: tmp, addr: host->ioaddr + SD_CLOCK_BURST_SIZE_SETUP); |
77 | } |
78 | |
79 | if (pdata && (pdata->flags & PXA_FLAG_ENABLE_CLOCK_GATING)) { |
80 | tmp = readw(addr: host->ioaddr + SD_FIFO_PARAM); |
81 | tmp &= ~CLK_GATE_SETTING_BITS; |
82 | writew(val: tmp, addr: host->ioaddr + SD_FIFO_PARAM); |
83 | } else { |
84 | tmp = readw(addr: host->ioaddr + SD_FIFO_PARAM); |
85 | tmp &= ~CLK_GATE_SETTING_BITS; |
86 | tmp |= CLK_GATE_SETTING_BITS; |
87 | writew(val: tmp, addr: host->ioaddr + SD_FIFO_PARAM); |
88 | } |
89 | } |
90 | } |
91 | |
92 | static u16 pxav1_readw(struct sdhci_host *host, int reg) |
93 | { |
94 | /* Workaround for data abort exception on SDH2 and SDH4 on PXA168 */ |
95 | if (reg == SDHCI_HOST_VERSION) |
96 | return readl(addr: host->ioaddr + SDHCI_HOST_VERSION - 2) >> 16; |
97 | |
98 | return readw(addr: host->ioaddr + reg); |
99 | } |
100 | |
101 | static u32 pxav1_irq(struct sdhci_host *host, u32 intmask) |
102 | { |
103 | struct sdhci_pxav2_host *pxav2_host = sdhci_pltfm_priv(host: sdhci_priv(host)); |
104 | struct mmc_request *sdio_mrq; |
105 | |
106 | if (pxav2_host->sdio_mrq && (intmask & SDHCI_INT_CMD_MASK)) { |
107 | /* The dummy CMD0 for the SDIO workaround just completed */ |
108 | sdhci_writel(host, val: intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS); |
109 | intmask &= ~SDHCI_INT_CMD_MASK; |
110 | |
111 | /* Restore MMC function to CMD pin */ |
112 | if (pxav2_host->pinctrl && pxav2_host->pins_default) |
113 | pinctrl_select_state(p: pxav2_host->pinctrl, s: pxav2_host->pins_default); |
114 | |
115 | sdio_mrq = pxav2_host->sdio_mrq; |
116 | pxav2_host->sdio_mrq = NULL; |
117 | mmc_request_done(host->mmc, sdio_mrq); |
118 | } |
119 | |
120 | return intmask; |
121 | } |
122 | |
123 | static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq) |
124 | { |
125 | u16 tmp; |
126 | struct sdhci_pxav2_host *pxav2_host; |
127 | |
128 | /* If this is an SDIO command, perform errata workaround for silicon bug */ |
129 | if (mrq->cmd && !mrq->cmd->error && |
130 | (mrq->cmd->opcode == SD_IO_RW_DIRECT || |
131 | mrq->cmd->opcode == SD_IO_RW_EXTENDED)) { |
132 | /* Reset data port */ |
133 | tmp = readw(addr: host->ioaddr + SDHCI_TIMEOUT_CONTROL); |
134 | tmp |= 0x400; |
135 | writew(val: tmp, addr: host->ioaddr + SDHCI_TIMEOUT_CONTROL); |
136 | |
137 | /* Clock is now stopped, so restart it by sending a dummy CMD0 */ |
138 | pxav2_host = sdhci_pltfm_priv(host: sdhci_priv(host)); |
139 | pxav2_host->sdio_mrq = mrq; |
140 | |
141 | /* Set CMD as high output rather than MMC function while we do CMD0 */ |
142 | if (pxav2_host->pinctrl && pxav2_host->pins_cmd_gpio) |
143 | pinctrl_select_state(p: pxav2_host->pinctrl, s: pxav2_host->pins_cmd_gpio); |
144 | |
145 | sdhci_writel(host, val: 0, SDHCI_ARGUMENT); |
146 | sdhci_writew(host, val: 0, SDHCI_TRANSFER_MODE); |
147 | sdhci_writew(host, SDHCI_MAKE_CMD(MMC_GO_IDLE_STATE, SDHCI_CMD_RESP_NONE), |
148 | SDHCI_COMMAND); |
149 | |
150 | /* Don't finish this request until the dummy CMD0 finishes */ |
151 | return; |
152 | } |
153 | |
154 | mmc_request_done(host->mmc, mrq); |
155 | } |
156 | |
157 | static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) |
158 | { |
159 | u8 ctrl; |
160 | u16 tmp; |
161 | |
162 | ctrl = readb(addr: host->ioaddr + SDHCI_HOST_CONTROL); |
163 | tmp = readw(addr: host->ioaddr + SD_CE_ATA_2); |
164 | if (width == MMC_BUS_WIDTH_8) { |
165 | ctrl &= ~SDHCI_CTRL_4BITBUS; |
166 | tmp |= MMC_CARD | MMC_WIDTH; |
167 | } else { |
168 | tmp &= ~(MMC_CARD | MMC_WIDTH); |
169 | if (width == MMC_BUS_WIDTH_4) |
170 | ctrl |= SDHCI_CTRL_4BITBUS; |
171 | else |
172 | ctrl &= ~SDHCI_CTRL_4BITBUS; |
173 | } |
174 | writew(val: tmp, addr: host->ioaddr + SD_CE_ATA_2); |
175 | writeb(val: ctrl, addr: host->ioaddr + SDHCI_HOST_CONTROL); |
176 | } |
177 | |
178 | struct sdhci_pxa_variant { |
179 | const struct sdhci_ops *ops; |
180 | unsigned int ; |
181 | }; |
182 | |
183 | static const struct sdhci_ops pxav1_sdhci_ops = { |
184 | .read_w = pxav1_readw, |
185 | .set_clock = sdhci_set_clock, |
186 | .irq = pxav1_irq, |
187 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
188 | .set_bus_width = pxav2_mmc_set_bus_width, |
189 | .reset = pxav2_reset, |
190 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
191 | .request_done = pxav1_request_done, |
192 | }; |
193 | |
194 | static const struct sdhci_pxa_variant __maybe_unused pxav1_variant = { |
195 | .ops = &pxav1_sdhci_ops, |
196 | .extra_quirks = SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_32BIT_DMA_SIZE, |
197 | }; |
198 | |
199 | static const struct sdhci_ops pxav2_sdhci_ops = { |
200 | .set_clock = sdhci_set_clock, |
201 | .get_max_clock = sdhci_pltfm_clk_get_max_clock, |
202 | .set_bus_width = pxav2_mmc_set_bus_width, |
203 | .reset = pxav2_reset, |
204 | .set_uhs_signaling = sdhci_set_uhs_signaling, |
205 | }; |
206 | |
207 | static const struct sdhci_pxa_variant pxav2_variant = { |
208 | .ops = &pxav2_sdhci_ops, |
209 | }; |
210 | |
211 | #ifdef CONFIG_OF |
212 | static const struct of_device_id sdhci_pxav2_of_match[] = { |
213 | { .compatible = "mrvl,pxav1-mmc" , .data = &pxav1_variant, }, |
214 | { .compatible = "mrvl,pxav2-mmc" , .data = &pxav2_variant, }, |
215 | {}, |
216 | }; |
217 | MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match); |
218 | |
219 | static struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev) |
220 | { |
221 | struct sdhci_pxa_platdata *pdata; |
222 | struct device_node *np = dev->of_node; |
223 | u32 bus_width; |
224 | u32 clk_delay_cycles; |
225 | |
226 | pdata = devm_kzalloc(dev, size: sizeof(*pdata), GFP_KERNEL); |
227 | if (!pdata) |
228 | return NULL; |
229 | |
230 | if (of_property_read_bool(np, propname: "non-removable" )) |
231 | pdata->flags |= PXA_FLAG_CARD_PERMANENT; |
232 | |
233 | of_property_read_u32(np, propname: "bus-width" , out_value: &bus_width); |
234 | if (bus_width == 8) |
235 | pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT; |
236 | |
237 | of_property_read_u32(np, propname: "mrvl,clk-delay-cycles" , out_value: &clk_delay_cycles); |
238 | if (clk_delay_cycles > 0) { |
239 | pdata->clk_delay_sel = 1; |
240 | pdata->clk_delay_cycles = clk_delay_cycles; |
241 | } |
242 | |
243 | return pdata; |
244 | } |
245 | #else |
246 | static inline struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev) |
247 | { |
248 | return NULL; |
249 | } |
250 | #endif |
251 | |
252 | static int sdhci_pxav2_probe(struct platform_device *pdev) |
253 | { |
254 | struct sdhci_pltfm_host *pltfm_host; |
255 | struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; |
256 | struct sdhci_pxav2_host *pxav2_host; |
257 | struct device *dev = &pdev->dev; |
258 | struct sdhci_host *host = NULL; |
259 | const struct sdhci_pxa_variant *variant; |
260 | |
261 | int ret; |
262 | struct clk *clk, *clk_core; |
263 | |
264 | host = sdhci_pltfm_init(pdev, NULL, priv_size: sizeof(*pxav2_host)); |
265 | if (IS_ERR(ptr: host)) |
266 | return PTR_ERR(ptr: host); |
267 | |
268 | pltfm_host = sdhci_priv(host); |
269 | pxav2_host = sdhci_pltfm_priv(host: pltfm_host); |
270 | |
271 | clk = devm_clk_get_optional_enabled(dev, id: "io" ); |
272 | if (!clk) |
273 | clk = devm_clk_get_enabled(dev, NULL); |
274 | if (IS_ERR(ptr: clk)) { |
275 | ret = PTR_ERR(ptr: clk); |
276 | dev_err_probe(dev, err: ret, fmt: "failed to get io clock\n" ); |
277 | goto free; |
278 | } |
279 | pltfm_host->clk = clk; |
280 | |
281 | clk_core = devm_clk_get_optional_enabled(dev, id: "core" ); |
282 | if (IS_ERR(ptr: clk_core)) { |
283 | ret = PTR_ERR(ptr: clk_core); |
284 | dev_err_probe(dev, err: ret, fmt: "failed to enable core clock\n" ); |
285 | goto free; |
286 | } |
287 | |
288 | host->quirks = SDHCI_QUIRK_BROKEN_ADMA |
289 | | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
290 | | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; |
291 | |
292 | variant = of_device_get_match_data(dev); |
293 | if (variant) |
294 | pdata = pxav2_get_mmc_pdata(dev); |
295 | else |
296 | variant = &pxav2_variant; |
297 | |
298 | if (pdata) { |
299 | if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { |
300 | /* on-chip device */ |
301 | host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; |
302 | host->mmc->caps |= MMC_CAP_NONREMOVABLE; |
303 | } |
304 | |
305 | /* If slot design supports 8 bit data, indicate this to MMC. */ |
306 | if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT) |
307 | host->mmc->caps |= MMC_CAP_8_BIT_DATA; |
308 | |
309 | if (pdata->quirks) |
310 | host->quirks |= pdata->quirks; |
311 | if (pdata->host_caps) |
312 | host->mmc->caps |= pdata->host_caps; |
313 | if (pdata->pm_caps) |
314 | host->mmc->pm_caps |= pdata->pm_caps; |
315 | } |
316 | |
317 | host->quirks |= variant->extra_quirks; |
318 | host->ops = variant->ops; |
319 | |
320 | /* Set up optional pinctrl for PXA168 SDIO IRQ fix */ |
321 | pxav2_host->pinctrl = devm_pinctrl_get(dev); |
322 | if (!IS_ERR(ptr: pxav2_host->pinctrl)) { |
323 | pxav2_host->pins_cmd_gpio = pinctrl_lookup_state(p: pxav2_host->pinctrl, |
324 | name: "state_cmd_gpio" ); |
325 | if (IS_ERR(ptr: pxav2_host->pins_cmd_gpio)) |
326 | pxav2_host->pins_cmd_gpio = NULL; |
327 | pxav2_host->pins_default = pinctrl_lookup_state(p: pxav2_host->pinctrl, |
328 | name: "default" ); |
329 | if (IS_ERR(ptr: pxav2_host->pins_default)) |
330 | pxav2_host->pins_default = NULL; |
331 | } else { |
332 | pxav2_host->pinctrl = NULL; |
333 | } |
334 | |
335 | ret = sdhci_add_host(host); |
336 | if (ret) |
337 | goto free; |
338 | |
339 | return 0; |
340 | |
341 | free: |
342 | sdhci_pltfm_free(pdev); |
343 | return ret; |
344 | } |
345 | |
346 | static struct platform_driver sdhci_pxav2_driver = { |
347 | .driver = { |
348 | .name = "sdhci-pxav2" , |
349 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
350 | .of_match_table = of_match_ptr(sdhci_pxav2_of_match), |
351 | .pm = &sdhci_pltfm_pmops, |
352 | }, |
353 | .probe = sdhci_pxav2_probe, |
354 | .remove_new = sdhci_pltfm_remove, |
355 | }; |
356 | |
357 | module_platform_driver(sdhci_pxav2_driver); |
358 | |
359 | MODULE_DESCRIPTION("SDHCI driver for pxav2" ); |
360 | MODULE_AUTHOR("Marvell International Ltd." ); |
361 | MODULE_LICENSE("GPL v2" ); |
362 | |
363 | |