Commit | Line | Data |
---|---|---|
a87d5638 MD |
1 | /* |
2 | * SuperH Mobile SDHI | |
3 | * | |
4 | * Copyright (C) 2009 Magnus Damm | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * Based on "Compaq ASIC3 support": | |
11 | * | |
12 | * Copyright 2001 Compaq Computer Corporation. | |
13 | * Copyright 2004-2005 Phil Blundell | |
14 | * Copyright 2007-2008 OpenedHand Ltd. | |
15 | * | |
16 | * Authors: Phil Blundell <pb@handhelds.org>, | |
17 | * Samuel Ortiz <sameo@openedhand.com> | |
18 | * | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/clk.h> | |
5a0e3ad6 | 23 | #include <linux/slab.h> |
c7bb4487 | 24 | #include <linux/mod_devicetable.h> |
88b47679 | 25 | #include <linux/module.h> |
5a00a971 | 26 | #include <linux/of_device.h> |
a87d5638 | 27 | #include <linux/platform_device.h> |
3c49e810 | 28 | #include <linux/mmc/host.h> |
42051e8a | 29 | #include <linux/mmc/sh_mobile_sdhi.h> |
a87d5638 | 30 | #include <linux/mfd/tmio.h> |
056676da | 31 | #include <linux/sh_dma.h> |
973ed3af | 32 | #include <linux/delay.h> |
a87d5638 | 33 | |
42051e8a GL |
34 | #include "tmio_mmc.h" |
35 | ||
e3c418f1 KM |
36 | #define EXT_ACC 0xe4 |
37 | ||
16935250 KM |
38 | #define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data) |
39 | ||
5a00a971 GL |
40 | struct sh_mobile_sdhi_of_data { |
41 | unsigned long tmio_flags; | |
b3a5d4ce | 42 | unsigned long capabilities; |
423f6c2e | 43 | unsigned long capabilities2; |
f45394d5 | 44 | enum dma_slave_buswidth dma_buswidth; |
384b2cbd | 45 | dma_addr_t dma_rx_offset; |
5a00a971 GL |
46 | }; |
47 | ||
48 | static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = { | |
49 | { | |
50 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, | |
51 | }, | |
52 | }; | |
53 | ||
b3a5d4ce | 54 | static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = { |
da29fe2b SU |
55 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | |
56 | TMIO_MMC_CLK_ACTUAL, | |
b3a5d4ce KM |
57 | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, |
58 | }; | |
59 | ||
423f6c2e | 60 | static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { |
da29fe2b SU |
61 | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | |
62 | TMIO_MMC_CLK_ACTUAL, | |
423f6c2e | 63 | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, |
f45394d5 | 64 | .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, |
384b2cbd | 65 | .dma_rx_offset = 0x2000, |
423f6c2e KM |
66 | }; |
67 | ||
2772ef30 KM |
68 | static const struct of_device_id sh_mobile_sdhi_of_match[] = { |
69 | { .compatible = "renesas,sdhi-shmobile" }, | |
70 | { .compatible = "renesas,sdhi-sh7372" }, | |
71 | { .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], }, | |
72 | { .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], }, | |
73 | { .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], }, | |
b3a5d4ce | 74 | { .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, }, |
81bbbc72 | 75 | { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, }, |
423f6c2e | 76 | { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, }, |
81918d25 | 77 | { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, }, |
a6386403 GU |
78 | { .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, }, |
79 | { .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, }, | |
80 | { .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, }, | |
2772ef30 KM |
81 | {}, |
82 | }; | |
83 | MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); | |
84 | ||
a87d5638 MD |
85 | struct sh_mobile_sdhi { |
86 | struct clk *clk; | |
87 | struct tmio_mmc_data mmc_data; | |
056676da | 88 | struct tmio_mmc_dma dma_priv; |
a87d5638 MD |
89 | }; |
90 | ||
f45394d5 KM |
91 | static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width) |
92 | { | |
93 | u32 val; | |
94 | ||
95 | /* | |
96 | * see also | |
97 | * sh_mobile_sdhi_of_data :: dma_buswidth | |
98 | */ | |
99 | switch (sd_ctrl_read16(host, CTL_VERSION)) { | |
100 | case 0x490C: | |
101 | val = (width == 32) ? 0x0001 : 0x0000; | |
102 | break; | |
103 | case 0xCB0D: | |
104 | val = (width == 32) ? 0x0000 : 0x0001; | |
105 | break; | |
106 | default: | |
107 | /* nothing to do */ | |
108 | return; | |
109 | } | |
110 | ||
111 | sd_ctrl_write16(host, EXT_ACC, val); | |
112 | } | |
113 | ||
56c49287 GL |
114 | static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f) |
115 | { | |
6e2c0f3f | 116 | struct mmc_host *mmc = platform_get_drvdata(pdev); |
56c49287 | 117 | struct tmio_mmc_host *host = mmc_priv(mmc); |
16935250 | 118 | struct sh_mobile_sdhi *priv = host_to_priv(host); |
00fb3d2a | 119 | int ret = clk_prepare_enable(priv->clk); |
56c49287 GL |
120 | if (ret < 0) |
121 | return ret; | |
122 | ||
123 | *f = clk_get_rate(priv->clk); | |
f45394d5 KM |
124 | |
125 | /* enable 16bit data access on SDBUF as default */ | |
126 | sh_mobile_sdhi_sdbuf_width(host, 16); | |
127 | ||
56c49287 GL |
128 | return 0; |
129 | } | |
130 | ||
131 | static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev) | |
132 | { | |
6e2c0f3f | 133 | struct mmc_host *mmc = platform_get_drvdata(pdev); |
56c49287 | 134 | struct tmio_mmc_host *host = mmc_priv(mmc); |
16935250 | 135 | struct sh_mobile_sdhi *priv = host_to_priv(host); |
00fb3d2a | 136 | clk_disable_unprepare(priv->clk); |
56c49287 GL |
137 | } |
138 | ||
973ed3af SH |
139 | static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) |
140 | { | |
141 | int timeout = 1000; | |
142 | ||
143 | while (--timeout && !(sd_ctrl_read16(host, CTL_STATUS2) & (1 << 13))) | |
144 | udelay(1); | |
145 | ||
146 | if (!timeout) { | |
94b110af | 147 | dev_warn(&host->pdev->dev, "timeout waiting for SD bus idle\n"); |
973ed3af SH |
148 | return -EBUSY; |
149 | } | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr) | |
155 | { | |
156 | switch (addr) | |
157 | { | |
158 | case CTL_SD_CMD: | |
159 | case CTL_STOP_INTERNAL_ACTION: | |
160 | case CTL_XFER_BLK_COUNT: | |
161 | case CTL_SD_CARD_CLK_CTL: | |
162 | case CTL_SD_XFER_LEN: | |
163 | case CTL_SD_MEM_CARD_OPT: | |
164 | case CTL_TRANSACTION_CTL: | |
165 | case CTL_DMA_ENABLE: | |
166 | return sh_mobile_sdhi_wait_idle(host); | |
167 | } | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
8b4efe2f KM |
172 | static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, |
173 | unsigned int direction, int blk_size) | |
174 | { | |
175 | /* | |
176 | * In Renesas controllers, when performing a | |
177 | * multiple block read of one or two blocks, | |
178 | * depending on the timing with which the | |
179 | * response register is read, the response | |
180 | * value may not be read properly. | |
181 | * Use single block read for this HW bug | |
182 | */ | |
183 | if ((direction == MMC_DATA_READ) && | |
184 | blk_size == 2) | |
185 | return 1; | |
186 | ||
187 | return blk_size; | |
188 | } | |
189 | ||
0c47f6ae KM |
190 | static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) |
191 | { | |
192 | sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); | |
f45394d5 KM |
193 | |
194 | /* enable 32bit access if DMA mode if possibile */ | |
195 | sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16); | |
0c47f6ae KM |
196 | } |
197 | ||
c3be1efd | 198 | static int sh_mobile_sdhi_probe(struct platform_device *pdev) |
a87d5638 | 199 | { |
5a00a971 GL |
200 | const struct of_device_id *of_id = |
201 | of_match_device(sh_mobile_sdhi_of_match, &pdev->dev); | |
a87d5638 | 202 | struct sh_mobile_sdhi *priv; |
056676da | 203 | struct tmio_mmc_data *mmc_data; |
f33c9d65 | 204 | struct tmio_mmc_data *mmd = pdev->dev.platform_data; |
42051e8a | 205 | struct tmio_mmc_host *host; |
3b159a6e | 206 | struct resource *res; |
d5098cb6 SH |
207 | int irq, ret, i = 0; |
208 | bool multiplexed_isr = true; | |
87ae7bbe | 209 | struct tmio_mmc_dma *dma_priv; |
a87d5638 | 210 | |
3b159a6e KM |
211 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
212 | if (!res) | |
213 | return -EINVAL; | |
214 | ||
ac51b961 | 215 | priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL); |
a87d5638 MD |
216 | if (priv == NULL) { |
217 | dev_err(&pdev->dev, "kzalloc failed\n"); | |
218 | return -ENOMEM; | |
219 | } | |
220 | ||
056676da | 221 | mmc_data = &priv->mmc_data; |
87ae7bbe | 222 | dma_priv = &priv->dma_priv; |
056676da | 223 | |
ac51b961 | 224 | priv->clk = devm_clk_get(&pdev->dev, NULL); |
a87d5638 | 225 | if (IS_ERR(priv->clk)) { |
a87d5638 | 226 | ret = PTR_ERR(priv->clk); |
56ae1adc | 227 | dev_err(&pdev->dev, "cannot get clock: %d\n", ret); |
010f4aa7 | 228 | goto eprobe; |
a87d5638 MD |
229 | } |
230 | ||
94b110af KM |
231 | host = tmio_mmc_host_alloc(pdev); |
232 | if (!host) { | |
233 | ret = -ENOMEM; | |
234 | goto eprobe; | |
235 | } | |
236 | ||
7ecc09ba | 237 | host->dma = dma_priv; |
dfe9a229 | 238 | host->write16_hook = sh_mobile_sdhi_write16_hook; |
4fe2ec57 | 239 | host->clk_enable = sh_mobile_sdhi_clk_enable; |
00452c11 | 240 | host->clk_disable = sh_mobile_sdhi_clk_disable; |
85c02ddd | 241 | host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; |
7445bf9e | 242 | /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ |
95a7dc36 KM |
243 | if (resource_size(res) > 0x100) |
244 | host->bus_shift = 1; | |
245 | else | |
246 | host->bus_shift = 0; | |
7ecc09ba | 247 | |
84f11d5b | 248 | if (mmd) |
f33c9d65 | 249 | *mmc_data = *mmd; |
84f11d5b | 250 | |
87ae7bbe | 251 | dma_priv->filter = shdma_chan_filter; |
0c47f6ae | 252 | dma_priv->enable = sh_mobile_sdhi_enable_dma; |
87ae7bbe | 253 | |
e471df0b | 254 | mmc_data->alignment_shift = 1; /* 2-byte alignment */ |
f33c9d65 | 255 | mmc_data->capabilities |= MMC_CAP_MMC_HIGHSPEED; |
e471df0b | 256 | |
f1334fb3 YG |
257 | /* |
258 | * All SDHI blocks support 2-byte and larger block sizes in 4-bit | |
259 | * bus width mode. | |
260 | */ | |
261 | mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES; | |
262 | ||
23b66071 AH |
263 | /* |
264 | * All SDHI blocks support SDIO IRQ signalling. | |
265 | */ | |
266 | mmc_data->flags |= TMIO_MMC_SDIO_IRQ; | |
267 | ||
b8d11962 SU |
268 | /* |
269 | * All SDHI have CMD12 controll bit | |
270 | */ | |
271 | mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL; | |
272 | ||
6b98757e SU |
273 | /* |
274 | * All SDHI need SDIO_INFO1 reserved bit | |
275 | */ | |
276 | mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK; | |
277 | ||
5a00a971 GL |
278 | if (of_id && of_id->data) { |
279 | const struct sh_mobile_sdhi_of_data *of_data = of_id->data; | |
280 | mmc_data->flags |= of_data->tmio_flags; | |
b3a5d4ce | 281 | mmc_data->capabilities |= of_data->capabilities; |
423f6c2e | 282 | mmc_data->capabilities2 |= of_data->capabilities2; |
8b4c8f32 | 283 | mmc_data->dma_rx_offset = of_data->dma_rx_offset; |
f45394d5 | 284 | dma_priv->dma_buswidth = of_data->dma_buswidth; |
5a00a971 GL |
285 | } |
286 | ||
94b110af | 287 | ret = tmio_mmc_host_probe(host, mmc_data); |
42051e8a | 288 | if (ret < 0) |
94b110af | 289 | goto efree; |
a87d5638 | 290 | |
d5098cb6 SH |
291 | /* |
292 | * Allow one or more specific (named) ISRs or | |
293 | * one or more multiplexed (un-named) ISRs. | |
294 | */ | |
295 | ||
296 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT); | |
297 | if (irq >= 0) { | |
298 | multiplexed_isr = false; | |
ac51b961 | 299 | ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_card_detect_irq, 0, |
d5098cb6 SH |
300 | dev_name(&pdev->dev), host); |
301 | if (ret) | |
ac51b961 | 302 | goto eirq; |
d5098cb6 SH |
303 | } |
304 | ||
305 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO); | |
306 | if (irq >= 0) { | |
307 | multiplexed_isr = false; | |
ac51b961 | 308 | ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdio_irq, 0, |
d5098cb6 SH |
309 | dev_name(&pdev->dev), host); |
310 | if (ret) | |
ac51b961 | 311 | goto eirq; |
d5098cb6 SH |
312 | } |
313 | ||
314 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD); | |
315 | if (irq >= 0) { | |
316 | multiplexed_isr = false; | |
ac51b961 | 317 | ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdcard_irq, 0, |
d6a1f863 | 318 | dev_name(&pdev->dev), host); |
d5098cb6 | 319 | if (ret) |
ac51b961 | 320 | goto eirq; |
d5098cb6 SH |
321 | } else if (!multiplexed_isr) { |
322 | dev_err(&pdev->dev, | |
323 | "Principal SD-card IRQ is missing among named interrupts\n"); | |
324 | ret = irq; | |
ac51b961 | 325 | goto eirq; |
d5098cb6 SH |
326 | } |
327 | ||
328 | if (multiplexed_isr) { | |
329 | while (1) { | |
330 | irq = platform_get_irq(pdev, i); | |
331 | if (irq < 0) | |
332 | break; | |
333 | i++; | |
ac51b961 | 334 | ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0, |
d5098cb6 SH |
335 | dev_name(&pdev->dev), host); |
336 | if (ret) | |
ac51b961 | 337 | goto eirq; |
d6a1f863 | 338 | } |
d5098cb6 SH |
339 | |
340 | /* There must be at least one IRQ source */ | |
7913ae7d WY |
341 | if (!i) { |
342 | ret = irq; | |
ac51b961 | 343 | goto eirq; |
7913ae7d | 344 | } |
8e7bfdb3 | 345 | } |
d5098cb6 | 346 | |
1f7d6819 MD |
347 | dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", |
348 | mmc_hostname(host->mmc), (unsigned long) | |
58126c87 | 349 | (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start), |
369213bd | 350 | host->mmc->f_max / 1000000); |
a87d5638 | 351 | |
42051e8a | 352 | return ret; |
a87d5638 | 353 | |
ac51b961 | 354 | eirq: |
8e7bfdb3 | 355 | tmio_mmc_host_remove(host); |
94b110af KM |
356 | efree: |
357 | tmio_mmc_host_free(host); | |
42051e8a | 358 | eprobe: |
a87d5638 MD |
359 | return ret; |
360 | } | |
361 | ||
362 | static int sh_mobile_sdhi_remove(struct platform_device *pdev) | |
363 | { | |
42051e8a GL |
364 | struct mmc_host *mmc = platform_get_drvdata(pdev); |
365 | struct tmio_mmc_host *host = mmc_priv(mmc); | |
d6a1f863 | 366 | |
742a0c7c GL |
367 | tmio_mmc_host_remove(host); |
368 | ||
a87d5638 MD |
369 | return 0; |
370 | } | |
371 | ||
e6ee7182 | 372 | static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { |
753a688c UH |
373 | SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, |
374 | pm_runtime_force_resume) | |
6ed23b80 | 375 | SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, |
4e262d7f UH |
376 | tmio_mmc_host_runtime_resume, |
377 | NULL) | |
e6ee7182 GL |
378 | }; |
379 | ||
a87d5638 MD |
380 | static struct platform_driver sh_mobile_sdhi_driver = { |
381 | .driver = { | |
382 | .name = "sh_mobile_sdhi", | |
e6ee7182 | 383 | .pm = &tmio_mmc_dev_pm_ops, |
c7bb4487 | 384 | .of_match_table = sh_mobile_sdhi_of_match, |
a87d5638 MD |
385 | }, |
386 | .probe = sh_mobile_sdhi_probe, | |
0433c143 | 387 | .remove = sh_mobile_sdhi_remove, |
a87d5638 MD |
388 | }; |
389 | ||
d1f81a64 | 390 | module_platform_driver(sh_mobile_sdhi_driver); |
a87d5638 MD |
391 | |
392 | MODULE_DESCRIPTION("SuperH Mobile SDHI driver"); | |
393 | MODULE_AUTHOR("Magnus Damm"); | |
394 | MODULE_LICENSE("GPL v2"); | |
42051e8a | 395 | MODULE_ALIAS("platform:sh_mobile_sdhi"); |