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> |
88b47679 | 24 | #include <linux/module.h> |
a87d5638 | 25 | #include <linux/platform_device.h> |
3c49e810 | 26 | #include <linux/mmc/host.h> |
42051e8a | 27 | #include <linux/mmc/sh_mobile_sdhi.h> |
a87d5638 | 28 | #include <linux/mfd/tmio.h> |
056676da | 29 | #include <linux/sh_dma.h> |
973ed3af | 30 | #include <linux/delay.h> |
a87d5638 | 31 | |
42051e8a GL |
32 | #include "tmio_mmc.h" |
33 | ||
a87d5638 MD |
34 | struct sh_mobile_sdhi { |
35 | struct clk *clk; | |
36 | struct tmio_mmc_data mmc_data; | |
056676da GL |
37 | struct sh_dmae_slave param_tx; |
38 | struct sh_dmae_slave param_rx; | |
39 | struct tmio_mmc_dma dma_priv; | |
a87d5638 MD |
40 | }; |
41 | ||
42051e8a | 42 | static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) |
be9cd7b6 | 43 | { |
be9cd7b6 MD |
44 | struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; |
45 | ||
46 | if (p && p->set_pwr) | |
47 | p->set_pwr(pdev, state); | |
48 | } | |
49 | ||
42051e8a | 50 | static int sh_mobile_sdhi_get_cd(struct platform_device *pdev) |
998283e2 | 51 | { |
998283e2 AH |
52 | struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; |
53 | ||
54 | if (p && p->get_cd) | |
55 | return p->get_cd(pdev); | |
56 | else | |
57 | return -ENOSYS; | |
58 | } | |
59 | ||
973ed3af SH |
60 | static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) |
61 | { | |
62 | int timeout = 1000; | |
63 | ||
64 | while (--timeout && !(sd_ctrl_read16(host, CTL_STATUS2) & (1 << 13))) | |
65 | udelay(1); | |
66 | ||
67 | if (!timeout) { | |
68 | dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n"); | |
69 | return -EBUSY; | |
70 | } | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr) | |
76 | { | |
77 | switch (addr) | |
78 | { | |
79 | case CTL_SD_CMD: | |
80 | case CTL_STOP_INTERNAL_ACTION: | |
81 | case CTL_XFER_BLK_COUNT: | |
82 | case CTL_SD_CARD_CLK_CTL: | |
83 | case CTL_SD_XFER_LEN: | |
84 | case CTL_SD_MEM_CARD_OPT: | |
85 | case CTL_TRANSACTION_CTL: | |
86 | case CTL_DMA_ENABLE: | |
87 | return sh_mobile_sdhi_wait_idle(host); | |
88 | } | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
25ab998e | 93 | static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) |
a87d5638 MD |
94 | { |
95 | struct sh_mobile_sdhi *priv; | |
056676da GL |
96 | struct tmio_mmc_data *mmc_data; |
97 | struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; | |
42051e8a | 98 | struct tmio_mmc_host *host; |
a87d5638 | 99 | char clk_name[8]; |
d5098cb6 SH |
100 | int irq, ret, i = 0; |
101 | bool multiplexed_isr = true; | |
a87d5638 MD |
102 | |
103 | priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL); | |
104 | if (priv == NULL) { | |
105 | dev_err(&pdev->dev, "kzalloc failed\n"); | |
106 | return -ENOMEM; | |
107 | } | |
108 | ||
056676da | 109 | mmc_data = &priv->mmc_data; |
25958804 | 110 | p->pdata = mmc_data; |
056676da | 111 | |
a87d5638 MD |
112 | snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); |
113 | priv->clk = clk_get(&pdev->dev, clk_name); | |
114 | if (IS_ERR(priv->clk)) { | |
115 | dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); | |
116 | ret = PTR_ERR(priv->clk); | |
42051e8a | 117 | goto eclkget; |
a87d5638 MD |
118 | } |
119 | ||
056676da GL |
120 | mmc_data->hclk = clk_get_rate(priv->clk); |
121 | mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; | |
998283e2 | 122 | mmc_data->get_cd = sh_mobile_sdhi_get_cd; |
056676da | 123 | mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; |
bb0fe533 | 124 | if (p) { |
f87c20a9 | 125 | mmc_data->flags = p->tmio_flags; |
b91df159 SH |
126 | if (mmc_data->flags & TMIO_MMC_HAS_IDLE_WAIT) |
127 | mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; | |
bb0fe533 | 128 | mmc_data->ocr_mask = p->tmio_ocr_mask; |
998283e2 | 129 | mmc_data->capabilities |= p->tmio_caps; |
58126c87 | 130 | mmc_data->cd_gpio = p->cd_gpio; |
42051e8a | 131 | |
3e713373 | 132 | if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { |
42051e8a GL |
133 | priv->param_tx.slave_id = p->dma_slave_tx; |
134 | priv->param_rx.slave_id = p->dma_slave_rx; | |
135 | priv->dma_priv.chan_priv_tx = &priv->param_tx; | |
136 | priv->dma_priv.chan_priv_rx = &priv->param_rx; | |
137 | priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */ | |
138 | mmc_data->dma = &priv->dma_priv; | |
139 | } | |
bb0fe533 | 140 | } |
056676da | 141 | |
f1334fb3 YG |
142 | /* |
143 | * All SDHI blocks support 2-byte and larger block sizes in 4-bit | |
144 | * bus width mode. | |
145 | */ | |
146 | mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES; | |
147 | ||
23b66071 AH |
148 | /* |
149 | * All SDHI blocks support SDIO IRQ signalling. | |
150 | */ | |
151 | mmc_data->flags |= TMIO_MMC_SDIO_IRQ; | |
152 | ||
42051e8a GL |
153 | ret = tmio_mmc_host_probe(&host, pdev, mmc_data); |
154 | if (ret < 0) | |
155 | goto eprobe; | |
a87d5638 | 156 | |
d5098cb6 SH |
157 | /* |
158 | * Allow one or more specific (named) ISRs or | |
159 | * one or more multiplexed (un-named) ISRs. | |
160 | */ | |
161 | ||
162 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT); | |
163 | if (irq >= 0) { | |
164 | multiplexed_isr = false; | |
165 | ret = request_irq(irq, tmio_mmc_card_detect_irq, 0, | |
166 | dev_name(&pdev->dev), host); | |
167 | if (ret) | |
168 | goto eirq_card_detect; | |
169 | } | |
170 | ||
171 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO); | |
172 | if (irq >= 0) { | |
173 | multiplexed_isr = false; | |
174 | ret = request_irq(irq, tmio_mmc_sdio_irq, 0, | |
175 | dev_name(&pdev->dev), host); | |
176 | if (ret) | |
177 | goto eirq_sdio; | |
178 | } | |
179 | ||
180 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD); | |
181 | if (irq >= 0) { | |
182 | multiplexed_isr = false; | |
183 | ret = request_irq(irq, tmio_mmc_sdcard_irq, 0, | |
d6a1f863 | 184 | dev_name(&pdev->dev), host); |
d5098cb6 SH |
185 | if (ret) |
186 | goto eirq_sdcard; | |
187 | } else if (!multiplexed_isr) { | |
188 | dev_err(&pdev->dev, | |
189 | "Principal SD-card IRQ is missing among named interrupts\n"); | |
190 | ret = irq; | |
191 | goto eirq_sdcard; | |
192 | } | |
193 | ||
194 | if (multiplexed_isr) { | |
195 | while (1) { | |
196 | irq = platform_get_irq(pdev, i); | |
197 | if (irq < 0) | |
198 | break; | |
199 | i++; | |
200 | ret = request_irq(irq, tmio_mmc_irq, 0, | |
201 | dev_name(&pdev->dev), host); | |
202 | if (ret) | |
203 | goto eirq_multiplexed; | |
d6a1f863 | 204 | } |
d5098cb6 SH |
205 | |
206 | /* There must be at least one IRQ source */ | |
207 | if (!i) | |
208 | goto eirq_multiplexed; | |
8e7bfdb3 | 209 | } |
d5098cb6 | 210 | |
1f7d6819 MD |
211 | dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", |
212 | mmc_hostname(host->mmc), (unsigned long) | |
58126c87 | 213 | (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start), |
1f7d6819 | 214 | mmc_data->hclk / 1000000); |
a87d5638 | 215 | |
42051e8a | 216 | return ret; |
a87d5638 | 217 | |
d5098cb6 SH |
218 | eirq_multiplexed: |
219 | while (i--) { | |
220 | irq = platform_get_irq(pdev, i); | |
221 | free_irq(irq, host); | |
222 | } | |
223 | eirq_sdcard: | |
224 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO); | |
225 | if (irq >= 0) | |
226 | free_irq(irq, host); | |
227 | eirq_sdio: | |
228 | irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT); | |
229 | if (irq >= 0) | |
230 | free_irq(irq, host); | |
231 | eirq_card_detect: | |
8e7bfdb3 | 232 | tmio_mmc_host_remove(host); |
42051e8a | 233 | eprobe: |
42051e8a GL |
234 | clk_put(priv->clk); |
235 | eclkget: | |
236 | kfree(priv); | |
a87d5638 MD |
237 | return ret; |
238 | } | |
239 | ||
240 | static int sh_mobile_sdhi_remove(struct platform_device *pdev) | |
241 | { | |
42051e8a GL |
242 | struct mmc_host *mmc = platform_get_drvdata(pdev); |
243 | struct tmio_mmc_host *host = mmc_priv(mmc); | |
244 | struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); | |
25958804 | 245 | struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; |
d5098cb6 | 246 | int i = 0, irq; |
d6a1f863 | 247 | |
25958804 GL |
248 | p->pdata = NULL; |
249 | ||
742a0c7c GL |
250 | tmio_mmc_host_remove(host); |
251 | ||
d5098cb6 SH |
252 | while (1) { |
253 | irq = platform_get_irq(pdev, i++); | |
254 | if (irq < 0) | |
255 | break; | |
256 | free_irq(irq, host); | |
d6a1f863 | 257 | } |
a87d5638 | 258 | |
a87d5638 MD |
259 | clk_put(priv->clk); |
260 | kfree(priv); | |
261 | ||
262 | return 0; | |
263 | } | |
264 | ||
e6ee7182 GL |
265 | static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { |
266 | .suspend = tmio_mmc_host_suspend, | |
267 | .resume = tmio_mmc_host_resume, | |
25958804 GL |
268 | .runtime_suspend = tmio_mmc_host_runtime_suspend, |
269 | .runtime_resume = tmio_mmc_host_runtime_resume, | |
e6ee7182 GL |
270 | }; |
271 | ||
a87d5638 MD |
272 | static struct platform_driver sh_mobile_sdhi_driver = { |
273 | .driver = { | |
274 | .name = "sh_mobile_sdhi", | |
275 | .owner = THIS_MODULE, | |
e6ee7182 | 276 | .pm = &tmio_mmc_dev_pm_ops, |
a87d5638 MD |
277 | }, |
278 | .probe = sh_mobile_sdhi_probe, | |
279 | .remove = __devexit_p(sh_mobile_sdhi_remove), | |
280 | }; | |
281 | ||
d1f81a64 | 282 | module_platform_driver(sh_mobile_sdhi_driver); |
a87d5638 MD |
283 | |
284 | MODULE_DESCRIPTION("SuperH Mobile SDHI driver"); | |
285 | MODULE_AUTHOR("Magnus Damm"); | |
286 | MODULE_LICENSE("GPL v2"); | |
42051e8a | 287 | MODULE_ALIAS("platform:sh_mobile_sdhi"); |