diff options
author | Tony Prisk <linux@prisktech.co.nz> | 2012-11-17 21:33:06 -0500 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-12-06 13:54:50 -0500 |
commit | 3a96dff0f828ae9dfb43efd49a9b67a74c6dc360 (patch) | |
tree | 09b8703228c83e6828e8d3d64bbce048b69e32a7 /drivers/mmc | |
parent | 8d1e977da82ebd1defd44d085edc2c0140aecc2a (diff) |
mmc: SD/MMC Host Controller for Wondermedia WM8505/WM8650
This patch adds support for the SD/MMC host controller found
on Wondermedia 8xxx series SoCs, currently supported under
arm/arch-vt8500.
A binding document is also included, based on mmc.txt with
additional properties.
Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mmc/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/host/wmt-sdmmc.c | 1029 |
3 files changed, 1041 insertions, 0 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 5c7c846e03ef..83eb1e06ff76 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig | |||
@@ -601,3 +601,14 @@ config MMC_USHC | |||
601 | 601 | ||
602 | Note: These controllers only support SDIO cards and do not | 602 | Note: These controllers only support SDIO cards and do not |
603 | support MMC or SD memory cards. | 603 | support MMC or SD memory cards. |
604 | |||
605 | config MMC_WMT | ||
606 | tristate "Wondermedia SD/MMC Host Controller support" | ||
607 | depends on ARCH_VT8500 | ||
608 | default y | ||
609 | help | ||
610 | This selects support for the SD/MMC Host Controller on | ||
611 | Wondermedia WM8505/WM8650 based SoCs. | ||
612 | |||
613 | To compile this driver as a module, choose M here: the | ||
614 | module will be called wmt-sdmmc. | ||
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index cd64b5df9703..39d5e1234709 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile | |||
@@ -44,6 +44,7 @@ obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o | |||
44 | obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o | 44 | obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o |
45 | obj-$(CONFIG_MMC_VUB300) += vub300.o | 45 | obj-$(CONFIG_MMC_VUB300) += vub300.o |
46 | obj-$(CONFIG_MMC_USHC) += ushc.o | 46 | obj-$(CONFIG_MMC_USHC) += ushc.o |
47 | obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o | ||
47 | 48 | ||
48 | obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o | 49 | obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o |
49 | obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o | 50 | obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o |
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c new file mode 100644 index 000000000000..5ba4605e4f80 --- /dev/null +++ b/drivers/mmc/host/wmt-sdmmc.c | |||
@@ -0,0 +1,1029 @@ | |||
1 | /* | ||
2 | * WM8505/WM8650 SD/MMC Host Controller | ||
3 | * | ||
4 | * Copyright (C) 2010 Tony Prisk | ||
5 | * Copyright (C) 2008 WonderMedia Technologies, Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/ioport.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/dma-mapping.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/irq.h> | ||
21 | #include <linux/clk.h> | ||
22 | #include <linux/gpio.h> | ||
23 | |||
24 | #include <linux/of.h> | ||
25 | #include <linux/of_address.h> | ||
26 | #include <linux/of_irq.h> | ||
27 | #include <linux/of_device.h> | ||
28 | |||
29 | #include <linux/mmc/host.h> | ||
30 | #include <linux/mmc/mmc.h> | ||
31 | #include <linux/mmc/sd.h> | ||
32 | |||
33 | #include <asm/byteorder.h> | ||
34 | |||
35 | |||
36 | #define DRIVER_NAME "wmt-sdhc" | ||
37 | |||
38 | |||
39 | /* MMC/SD controller registers */ | ||
40 | #define SDMMC_CTLR 0x00 | ||
41 | #define SDMMC_CMD 0x01 | ||
42 | #define SDMMC_RSPTYPE 0x02 | ||
43 | #define SDMMC_ARG 0x04 | ||
44 | #define SDMMC_BUSMODE 0x08 | ||
45 | #define SDMMC_BLKLEN 0x0C | ||
46 | #define SDMMC_BLKCNT 0x0E | ||
47 | #define SDMMC_RSP 0x10 | ||
48 | #define SDMMC_CBCR 0x20 | ||
49 | #define SDMMC_INTMASK0 0x24 | ||
50 | #define SDMMC_INTMASK1 0x25 | ||
51 | #define SDMMC_STS0 0x28 | ||
52 | #define SDMMC_STS1 0x29 | ||
53 | #define SDMMC_STS2 0x2A | ||
54 | #define SDMMC_STS3 0x2B | ||
55 | #define SDMMC_RSPTIMEOUT 0x2C | ||
56 | #define SDMMC_CLK 0x30 /* VT8500 only */ | ||
57 | #define SDMMC_EXTCTRL 0x34 | ||
58 | #define SDMMC_SBLKLEN 0x38 | ||
59 | #define SDMMC_DMATIMEOUT 0x3C | ||
60 | |||
61 | |||
62 | /* SDMMC_CTLR bit fields */ | ||
63 | #define CTLR_CMD_START 0x01 | ||
64 | #define CTLR_CMD_WRITE 0x04 | ||
65 | #define CTLR_FIFO_RESET 0x08 | ||
66 | |||
67 | /* SDMMC_BUSMODE bit fields */ | ||
68 | #define BM_SPI_MODE 0x01 | ||
69 | #define BM_FOURBIT_MODE 0x02 | ||
70 | #define BM_EIGHTBIT_MODE 0x04 | ||
71 | #define BM_SD_OFF 0x10 | ||
72 | #define BM_SPI_CS 0x20 | ||
73 | #define BM_SD_POWER 0x40 | ||
74 | #define BM_SOFT_RESET 0x80 | ||
75 | #define BM_ONEBIT_MASK 0xFD | ||
76 | |||
77 | /* SDMMC_BLKLEN bit fields */ | ||
78 | #define BLKL_CRCERR_ABORT 0x0800 | ||
79 | #define BLKL_CD_POL_HIGH 0x1000 | ||
80 | #define BLKL_GPI_CD 0x2000 | ||
81 | #define BLKL_DATA3_CD 0x4000 | ||
82 | #define BLKL_INT_ENABLE 0x8000 | ||
83 | |||
84 | /* SDMMC_INTMASK0 bit fields */ | ||
85 | #define INT0_MBLK_TRAN_DONE_INT_EN 0x10 | ||
86 | #define INT0_BLK_TRAN_DONE_INT_EN 0x20 | ||
87 | #define INT0_CD_INT_EN 0x40 | ||
88 | #define INT0_DI_INT_EN 0x80 | ||
89 | |||
90 | /* SDMMC_INTMASK1 bit fields */ | ||
91 | #define INT1_CMD_RES_TRAN_DONE_INT_EN 0x02 | ||
92 | #define INT1_CMD_RES_TOUT_INT_EN 0x04 | ||
93 | #define INT1_MBLK_AUTO_STOP_INT_EN 0x08 | ||
94 | #define INT1_DATA_TOUT_INT_EN 0x10 | ||
95 | #define INT1_RESCRC_ERR_INT_EN 0x20 | ||
96 | #define INT1_RCRC_ERR_INT_EN 0x40 | ||
97 | #define INT1_WCRC_ERR_INT_EN 0x80 | ||
98 | |||
99 | /* SDMMC_STS0 bit fields */ | ||
100 | #define STS0_WRITE_PROTECT 0x02 | ||
101 | #define STS0_CD_DATA3 0x04 | ||
102 | #define STS0_CD_GPI 0x08 | ||
103 | #define STS0_MBLK_DONE 0x10 | ||
104 | #define STS0_BLK_DONE 0x20 | ||
105 | #define STS0_CARD_DETECT 0x40 | ||
106 | #define STS0_DEVICE_INS 0x80 | ||
107 | |||
108 | /* SDMMC_STS1 bit fields */ | ||
109 | #define STS1_SDIO_INT 0x01 | ||
110 | #define STS1_CMDRSP_DONE 0x02 | ||
111 | #define STS1_RSP_TIMEOUT 0x04 | ||
112 | #define STS1_AUTOSTOP_DONE 0x08 | ||
113 | #define STS1_DATA_TIMEOUT 0x10 | ||
114 | #define STS1_RSP_CRC_ERR 0x20 | ||
115 | #define STS1_RCRC_ERR 0x40 | ||
116 | #define STS1_WCRC_ERR 0x80 | ||
117 | |||
118 | /* SDMMC_STS2 bit fields */ | ||
119 | #define STS2_CMD_RES_BUSY 0x10 | ||
120 | #define STS2_DATARSP_BUSY 0x20 | ||
121 | #define STS2_DIS_FORCECLK 0x80 | ||
122 | |||
123 | |||
124 | /* MMC/SD DMA Controller Registers */ | ||
125 | #define SDDMA_GCR 0x100 | ||
126 | #define SDDMA_IER 0x104 | ||
127 | #define SDDMA_ISR 0x108 | ||
128 | #define SDDMA_DESPR 0x10C | ||
129 | #define SDDMA_RBR 0x110 | ||
130 | #define SDDMA_DAR 0x114 | ||
131 | #define SDDMA_BAR 0x118 | ||
132 | #define SDDMA_CPR 0x11C | ||
133 | #define SDDMA_CCR 0x120 | ||
134 | |||
135 | |||
136 | /* SDDMA_GCR bit fields */ | ||
137 | #define DMA_GCR_DMA_EN 0x00000001 | ||
138 | #define DMA_GCR_SOFT_RESET 0x00000100 | ||
139 | |||
140 | /* SDDMA_IER bit fields */ | ||
141 | #define DMA_IER_INT_EN 0x00000001 | ||
142 | |||
143 | /* SDDMA_ISR bit fields */ | ||
144 | #define DMA_ISR_INT_STS 0x00000001 | ||
145 | |||
146 | /* SDDMA_RBR bit fields */ | ||
147 | #define DMA_RBR_FORMAT 0x40000000 | ||
148 | #define DMA_RBR_END 0x80000000 | ||
149 | |||
150 | /* SDDMA_CCR bit fields */ | ||
151 | #define DMA_CCR_RUN 0x00000080 | ||
152 | #define DMA_CCR_IF_TO_PERIPHERAL 0x00000000 | ||
153 | #define DMA_CCR_PERIPHERAL_TO_IF 0x00400000 | ||
154 | |||
155 | /* SDDMA_CCR event status */ | ||
156 | #define DMA_CCR_EVT_NO_STATUS 0x00000000 | ||
157 | #define DMA_CCR_EVT_UNDERRUN 0x00000001 | ||
158 | #define DMA_CCR_EVT_OVERRUN 0x00000002 | ||
159 | #define DMA_CCR_EVT_DESP_READ 0x00000003 | ||
160 | #define DMA_CCR_EVT_DATA_RW 0x00000004 | ||
161 | #define DMA_CCR_EVT_EARLY_END 0x00000005 | ||
162 | #define DMA_CCR_EVT_SUCCESS 0x0000000F | ||
163 | |||
164 | #define PDMA_READ 0x00 | ||
165 | #define PDMA_WRITE 0x01 | ||
166 | |||
167 | #define WMT_SD_POWER_OFF 0 | ||
168 | #define WMT_SD_POWER_ON 1 | ||
169 | |||
170 | struct wmt_dma_descriptor { | ||
171 | u32 flags; | ||
172 | u32 data_buffer_addr; | ||
173 | u32 branch_addr; | ||
174 | u32 reserved1; | ||
175 | }; | ||
176 | |||
177 | struct wmt_mci_caps { | ||
178 | unsigned int f_min; | ||
179 | unsigned int f_max; | ||
180 | u32 ocr_avail; | ||
181 | u32 caps; | ||
182 | u32 max_seg_size; | ||
183 | u32 max_segs; | ||
184 | u32 max_blk_size; | ||
185 | }; | ||
186 | |||
187 | struct wmt_mci_priv { | ||
188 | struct mmc_host *mmc; | ||
189 | void __iomem *sdmmc_base; | ||
190 | |||
191 | int irq_regular; | ||
192 | int irq_dma; | ||
193 | |||
194 | void *dma_desc_buffer; | ||
195 | dma_addr_t dma_desc_device_addr; | ||
196 | |||
197 | struct completion cmdcomp; | ||
198 | struct completion datacomp; | ||
199 | |||
200 | struct completion *comp_cmd; | ||
201 | struct completion *comp_dma; | ||
202 | |||
203 | struct mmc_request *req; | ||
204 | struct mmc_command *cmd; | ||
205 | |||
206 | struct clk *clk_sdmmc; | ||
207 | struct device *dev; | ||
208 | |||
209 | u8 power_inverted; | ||
210 | u8 cd_inverted; | ||
211 | }; | ||
212 | |||
213 | static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) | ||
214 | { | ||
215 | u32 reg_tmp; | ||
216 | if (enable) { | ||
217 | if (priv->power_inverted) { | ||
218 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
219 | writeb(reg_tmp | BM_SD_OFF, | ||
220 | priv->sdmmc_base + SDMMC_BUSMODE); | ||
221 | } else { | ||
222 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
223 | writeb(reg_tmp & (~BM_SD_OFF), | ||
224 | priv->sdmmc_base + SDMMC_BUSMODE); | ||
225 | } | ||
226 | } else { | ||
227 | if (priv->power_inverted) { | ||
228 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
229 | writeb(reg_tmp & (~BM_SD_OFF), | ||
230 | priv->sdmmc_base + SDMMC_BUSMODE); | ||
231 | } else { | ||
232 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
233 | writeb(reg_tmp | BM_SD_OFF, | ||
234 | priv->sdmmc_base + SDMMC_BUSMODE); | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | static void wmt_mci_read_response(struct mmc_host *mmc) | ||
240 | { | ||
241 | struct wmt_mci_priv *priv; | ||
242 | int idx1, idx2; | ||
243 | u8 tmp_resp; | ||
244 | u32 response; | ||
245 | |||
246 | priv = mmc_priv(mmc); | ||
247 | |||
248 | for (idx1 = 0; idx1 < 4; idx1++) { | ||
249 | response = 0; | ||
250 | for (idx2 = 0; idx2 < 4; idx2++) { | ||
251 | if ((idx1 == 3) && (idx2 == 3)) | ||
252 | tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP); | ||
253 | else | ||
254 | tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP + | ||
255 | (idx1*4) + idx2 + 1); | ||
256 | response |= (tmp_resp << (idx2 * 8)); | ||
257 | } | ||
258 | priv->cmd->resp[idx1] = cpu_to_be32(response); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | static void wmt_mci_start_command(struct wmt_mci_priv *priv) | ||
263 | { | ||
264 | u32 reg_tmp; | ||
265 | |||
266 | reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); | ||
267 | writeb(reg_tmp | CTLR_CMD_START, priv->sdmmc_base + SDMMC_CTLR); | ||
268 | } | ||
269 | |||
270 | static int wmt_mci_send_command(struct mmc_host *mmc, u8 command, u8 cmdtype, | ||
271 | u32 arg, u8 rsptype) | ||
272 | { | ||
273 | struct wmt_mci_priv *priv; | ||
274 | u32 reg_tmp; | ||
275 | |||
276 | priv = mmc_priv(mmc); | ||
277 | |||
278 | /* write command, arg, resptype registers */ | ||
279 | writeb(command, priv->sdmmc_base + SDMMC_CMD); | ||
280 | writel(arg, priv->sdmmc_base + SDMMC_ARG); | ||
281 | writeb(rsptype, priv->sdmmc_base + SDMMC_RSPTYPE); | ||
282 | |||
283 | /* reset response FIFO */ | ||
284 | reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); | ||
285 | writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); | ||
286 | |||
287 | /* ensure clock enabled - VT3465 */ | ||
288 | wmt_set_sd_power(priv, WMT_SD_POWER_ON); | ||
289 | |||
290 | /* clear status bits */ | ||
291 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); | ||
292 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); | ||
293 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS2); | ||
294 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS3); | ||
295 | |||
296 | /* set command type */ | ||
297 | reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); | ||
298 | writeb((reg_tmp & 0x0F) | (cmdtype << 4), | ||
299 | priv->sdmmc_base + SDMMC_CTLR); | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static void wmt_mci_disable_dma(struct wmt_mci_priv *priv) | ||
305 | { | ||
306 | writel(DMA_ISR_INT_STS, priv->sdmmc_base + SDDMA_ISR); | ||
307 | writel(0, priv->sdmmc_base + SDDMA_IER); | ||
308 | } | ||
309 | |||
310 | static void wmt_complete_data_request(struct wmt_mci_priv *priv) | ||
311 | { | ||
312 | struct mmc_request *req; | ||
313 | req = priv->req; | ||
314 | |||
315 | req->data->bytes_xfered = req->data->blksz * req->data->blocks; | ||
316 | |||
317 | /* unmap the DMA pages used for write data */ | ||
318 | if (req->data->flags & MMC_DATA_WRITE) | ||
319 | dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, | ||
320 | req->data->sg_len, DMA_TO_DEVICE); | ||
321 | else | ||
322 | dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, | ||
323 | req->data->sg_len, DMA_FROM_DEVICE); | ||
324 | |||
325 | /* Check if the DMA ISR returned a data error */ | ||
326 | if ((req->cmd->error) || (req->data->error)) | ||
327 | mmc_request_done(priv->mmc, req); | ||
328 | else { | ||
329 | wmt_mci_read_response(priv->mmc); | ||
330 | if (!req->data->stop) { | ||
331 | /* single-block read/write requests end here */ | ||
332 | mmc_request_done(priv->mmc, req); | ||
333 | } else { | ||
334 | /* | ||
335 | * we change the priv->cmd variable so the response is | ||
336 | * stored in the stop struct rather than the original | ||
337 | * calling command struct | ||
338 | */ | ||
339 | priv->comp_cmd = &priv->cmdcomp; | ||
340 | init_completion(priv->comp_cmd); | ||
341 | priv->cmd = req->data->stop; | ||
342 | wmt_mci_send_command(priv->mmc, req->data->stop->opcode, | ||
343 | 7, req->data->stop->arg, 9); | ||
344 | wmt_mci_start_command(priv); | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | |||
349 | static irqreturn_t wmt_mci_dma_isr(int irq_num, void *data) | ||
350 | { | ||
351 | struct mmc_host *mmc; | ||
352 | struct wmt_mci_priv *priv; | ||
353 | |||
354 | int status; | ||
355 | |||
356 | priv = (struct wmt_mci_priv *)data; | ||
357 | mmc = priv->mmc; | ||
358 | |||
359 | status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F; | ||
360 | |||
361 | if (status != DMA_CCR_EVT_SUCCESS) { | ||
362 | dev_err(priv->dev, "DMA Error: Status = %d\n", status); | ||
363 | priv->req->data->error = -ETIMEDOUT; | ||
364 | complete(priv->comp_dma); | ||
365 | return IRQ_HANDLED; | ||
366 | } | ||
367 | |||
368 | priv->req->data->error = 0; | ||
369 | |||
370 | wmt_mci_disable_dma(priv); | ||
371 | |||
372 | complete(priv->comp_dma); | ||
373 | |||
374 | if (priv->comp_cmd) { | ||
375 | if (completion_done(priv->comp_cmd)) { | ||
376 | /* | ||
377 | * if the command (regular) interrupt has already | ||
378 | * completed, finish off the request otherwise we wait | ||
379 | * for the command interrupt and finish from there. | ||
380 | */ | ||
381 | wmt_complete_data_request(priv); | ||
382 | } | ||
383 | } | ||
384 | |||
385 | return IRQ_HANDLED; | ||
386 | } | ||
387 | |||
388 | static irqreturn_t wmt_mci_regular_isr(int irq_num, void *data) | ||
389 | { | ||
390 | struct wmt_mci_priv *priv; | ||
391 | u32 status0; | ||
392 | u32 status1; | ||
393 | u32 status2; | ||
394 | u32 reg_tmp; | ||
395 | int cmd_done; | ||
396 | |||
397 | priv = (struct wmt_mci_priv *)data; | ||
398 | cmd_done = 0; | ||
399 | status0 = readb(priv->sdmmc_base + SDMMC_STS0); | ||
400 | status1 = readb(priv->sdmmc_base + SDMMC_STS1); | ||
401 | status2 = readb(priv->sdmmc_base + SDMMC_STS2); | ||
402 | |||
403 | /* Check for card insertion */ | ||
404 | reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); | ||
405 | if ((reg_tmp & INT0_DI_INT_EN) && (status0 & STS0_DEVICE_INS)) { | ||
406 | mmc_detect_change(priv->mmc, 0); | ||
407 | if (priv->cmd) | ||
408 | priv->cmd->error = -ETIMEDOUT; | ||
409 | if (priv->comp_cmd) | ||
410 | complete(priv->comp_cmd); | ||
411 | if (priv->comp_dma) { | ||
412 | wmt_mci_disable_dma(priv); | ||
413 | complete(priv->comp_dma); | ||
414 | } | ||
415 | writeb(STS0_DEVICE_INS, priv->sdmmc_base + SDMMC_STS0); | ||
416 | return IRQ_HANDLED; | ||
417 | } | ||
418 | |||
419 | if ((!priv->req->data) || | ||
420 | ((priv->req->data->stop) && (priv->cmd == priv->req->data->stop))) { | ||
421 | /* handle non-data & stop_transmission requests */ | ||
422 | if (status1 & STS1_CMDRSP_DONE) { | ||
423 | priv->cmd->error = 0; | ||
424 | cmd_done = 1; | ||
425 | } else if ((status1 & STS1_RSP_TIMEOUT) || | ||
426 | (status1 & STS1_DATA_TIMEOUT)) { | ||
427 | priv->cmd->error = -ETIMEDOUT; | ||
428 | cmd_done = 1; | ||
429 | } | ||
430 | |||
431 | if (cmd_done) { | ||
432 | priv->comp_cmd = NULL; | ||
433 | |||
434 | if (!priv->cmd->error) | ||
435 | wmt_mci_read_response(priv->mmc); | ||
436 | |||
437 | priv->cmd = NULL; | ||
438 | |||
439 | mmc_request_done(priv->mmc, priv->req); | ||
440 | } | ||
441 | } else { | ||
442 | /* handle data requests */ | ||
443 | if (status1 & STS1_CMDRSP_DONE) { | ||
444 | if (priv->cmd) | ||
445 | priv->cmd->error = 0; | ||
446 | if (priv->comp_cmd) | ||
447 | complete(priv->comp_cmd); | ||
448 | } | ||
449 | |||
450 | if ((status1 & STS1_RSP_TIMEOUT) || | ||
451 | (status1 & STS1_DATA_TIMEOUT)) { | ||
452 | if (priv->cmd) | ||
453 | priv->cmd->error = -ETIMEDOUT; | ||
454 | if (priv->comp_cmd) | ||
455 | complete(priv->comp_cmd); | ||
456 | if (priv->comp_dma) { | ||
457 | wmt_mci_disable_dma(priv); | ||
458 | complete(priv->comp_dma); | ||
459 | } | ||
460 | } | ||
461 | |||
462 | if (priv->comp_dma) { | ||
463 | /* | ||
464 | * If the dma interrupt has already completed, finish | ||
465 | * off the request; otherwise we wait for the DMA | ||
466 | * interrupt and finish from there. | ||
467 | */ | ||
468 | if (completion_done(priv->comp_dma)) | ||
469 | wmt_complete_data_request(priv); | ||
470 | } | ||
471 | } | ||
472 | |||
473 | writeb(status0, priv->sdmmc_base + SDMMC_STS0); | ||
474 | writeb(status1, priv->sdmmc_base + SDMMC_STS1); | ||
475 | writeb(status2, priv->sdmmc_base + SDMMC_STS2); | ||
476 | |||
477 | return IRQ_HANDLED; | ||
478 | } | ||
479 | |||
480 | static void wmt_reset_hardware(struct mmc_host *mmc) | ||
481 | { | ||
482 | struct wmt_mci_priv *priv; | ||
483 | u32 reg_tmp; | ||
484 | |||
485 | priv = mmc_priv(mmc); | ||
486 | |||
487 | /* reset controller */ | ||
488 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
489 | writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); | ||
490 | |||
491 | /* reset response FIFO */ | ||
492 | reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); | ||
493 | writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); | ||
494 | |||
495 | /* enable GPI pin to detect card */ | ||
496 | writew(BLKL_INT_ENABLE | BLKL_GPI_CD, priv->sdmmc_base + SDMMC_BLKLEN); | ||
497 | |||
498 | /* clear interrupt status */ | ||
499 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); | ||
500 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); | ||
501 | |||
502 | /* setup interrupts */ | ||
503 | writeb(INT0_CD_INT_EN | INT0_DI_INT_EN, priv->sdmmc_base + | ||
504 | SDMMC_INTMASK0); | ||
505 | writeb(INT1_DATA_TOUT_INT_EN | INT1_CMD_RES_TRAN_DONE_INT_EN | | ||
506 | INT1_CMD_RES_TOUT_INT_EN, priv->sdmmc_base + SDMMC_INTMASK1); | ||
507 | |||
508 | /* set the DMA timeout */ | ||
509 | writew(8191, priv->sdmmc_base + SDMMC_DMATIMEOUT); | ||
510 | |||
511 | /* auto clock freezing enable */ | ||
512 | reg_tmp = readb(priv->sdmmc_base + SDMMC_STS2); | ||
513 | writeb(reg_tmp | STS2_DIS_FORCECLK, priv->sdmmc_base + SDMMC_STS2); | ||
514 | |||
515 | /* set a default clock speed of 400Khz */ | ||
516 | clk_set_rate(priv->clk_sdmmc, 400000); | ||
517 | } | ||
518 | |||
519 | static int wmt_dma_init(struct mmc_host *mmc) | ||
520 | { | ||
521 | struct wmt_mci_priv *priv; | ||
522 | |||
523 | priv = mmc_priv(mmc); | ||
524 | |||
525 | writel(DMA_GCR_SOFT_RESET, priv->sdmmc_base + SDDMA_GCR); | ||
526 | writel(DMA_GCR_DMA_EN, priv->sdmmc_base + SDDMA_GCR); | ||
527 | if ((readl(priv->sdmmc_base + SDDMA_GCR) & DMA_GCR_DMA_EN) != 0) | ||
528 | return 0; | ||
529 | else | ||
530 | return 1; | ||
531 | } | ||
532 | |||
533 | static void wmt_dma_init_descriptor(struct wmt_dma_descriptor *desc, | ||
534 | u16 req_count, u32 buffer_addr, u32 branch_addr, int end) | ||
535 | { | ||
536 | desc->flags = 0x40000000 | req_count; | ||
537 | if (end) | ||
538 | desc->flags |= 0x80000000; | ||
539 | desc->data_buffer_addr = buffer_addr; | ||
540 | desc->branch_addr = branch_addr; | ||
541 | } | ||
542 | |||
543 | static void wmt_dma_config(struct mmc_host *mmc, u32 descaddr, u8 dir) | ||
544 | { | ||
545 | struct wmt_mci_priv *priv; | ||
546 | u32 reg_tmp; | ||
547 | |||
548 | priv = mmc_priv(mmc); | ||
549 | |||
550 | /* Enable DMA Interrupts */ | ||
551 | writel(DMA_IER_INT_EN, priv->sdmmc_base + SDDMA_IER); | ||
552 | |||
553 | /* Write DMA Descriptor Pointer Register */ | ||
554 | writel(descaddr, priv->sdmmc_base + SDDMA_DESPR); | ||
555 | |||
556 | writel(0x00, priv->sdmmc_base + SDDMA_CCR); | ||
557 | |||
558 | if (dir == PDMA_WRITE) { | ||
559 | reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); | ||
560 | writel(reg_tmp & DMA_CCR_IF_TO_PERIPHERAL, priv->sdmmc_base + | ||
561 | SDDMA_CCR); | ||
562 | } else { | ||
563 | reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); | ||
564 | writel(reg_tmp | DMA_CCR_PERIPHERAL_TO_IF, priv->sdmmc_base + | ||
565 | SDDMA_CCR); | ||
566 | } | ||
567 | } | ||
568 | |||
569 | static void wmt_dma_start(struct wmt_mci_priv *priv) | ||
570 | { | ||
571 | u32 reg_tmp; | ||
572 | |||
573 | reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); | ||
574 | writel(reg_tmp | DMA_CCR_RUN, priv->sdmmc_base + SDDMA_CCR); | ||
575 | } | ||
576 | |||
577 | static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req) | ||
578 | { | ||
579 | struct wmt_mci_priv *priv; | ||
580 | struct wmt_dma_descriptor *desc; | ||
581 | u8 command; | ||
582 | u8 cmdtype; | ||
583 | u32 arg; | ||
584 | u8 rsptype; | ||
585 | u32 reg_tmp; | ||
586 | |||
587 | struct scatterlist *sg; | ||
588 | int i; | ||
589 | int sg_cnt; | ||
590 | int offset; | ||
591 | u32 dma_address; | ||
592 | int desc_cnt; | ||
593 | |||
594 | priv = mmc_priv(mmc); | ||
595 | priv->req = req; | ||
596 | |||
597 | /* | ||
598 | * Use the cmd variable to pass a pointer to the resp[] structure | ||
599 | * This is required on multi-block requests to pass the pointer to the | ||
600 | * stop command | ||
601 | */ | ||
602 | priv->cmd = req->cmd; | ||
603 | |||
604 | command = req->cmd->opcode; | ||
605 | arg = req->cmd->arg; | ||
606 | rsptype = mmc_resp_type(req->cmd); | ||
607 | cmdtype = 0; | ||
608 | |||
609 | /* rsptype=7 only valid for SPI commands - should be =2 for SD */ | ||
610 | if (rsptype == 7) | ||
611 | rsptype = 2; | ||
612 | /* rsptype=21 is R1B, convert for controller */ | ||
613 | if (rsptype == 21) | ||
614 | rsptype = 9; | ||
615 | |||
616 | if (!req->data) { | ||
617 | wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); | ||
618 | wmt_mci_start_command(priv); | ||
619 | /* completion is now handled in the regular_isr() */ | ||
620 | } | ||
621 | if (req->data) { | ||
622 | priv->comp_cmd = &priv->cmdcomp; | ||
623 | init_completion(priv->comp_cmd); | ||
624 | |||
625 | wmt_dma_init(mmc); | ||
626 | |||
627 | /* set controller data length */ | ||
628 | reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); | ||
629 | writew((reg_tmp & 0xF800) | (req->data->blksz - 1), | ||
630 | priv->sdmmc_base + SDMMC_BLKLEN); | ||
631 | |||
632 | /* set controller block count */ | ||
633 | writew(req->data->blocks, priv->sdmmc_base + SDMMC_BLKCNT); | ||
634 | |||
635 | desc = (struct wmt_dma_descriptor *)priv->dma_desc_buffer; | ||
636 | |||
637 | if (req->data->flags & MMC_DATA_WRITE) { | ||
638 | sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, | ||
639 | req->data->sg_len, DMA_TO_DEVICE); | ||
640 | cmdtype = 1; | ||
641 | if (req->data->blocks > 1) | ||
642 | cmdtype = 3; | ||
643 | } else { | ||
644 | sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, | ||
645 | req->data->sg_len, DMA_FROM_DEVICE); | ||
646 | cmdtype = 2; | ||
647 | if (req->data->blocks > 1) | ||
648 | cmdtype = 4; | ||
649 | } | ||
650 | |||
651 | dma_address = priv->dma_desc_device_addr + 16; | ||
652 | desc_cnt = 0; | ||
653 | |||
654 | for_each_sg(req->data->sg, sg, sg_cnt, i) { | ||
655 | offset = 0; | ||
656 | while (offset < sg_dma_len(sg)) { | ||
657 | wmt_dma_init_descriptor(desc, req->data->blksz, | ||
658 | sg_dma_address(sg)+offset, | ||
659 | dma_address, 0); | ||
660 | desc++; | ||
661 | desc_cnt++; | ||
662 | offset += req->data->blksz; | ||
663 | dma_address += 16; | ||
664 | if (desc_cnt == req->data->blocks) | ||
665 | break; | ||
666 | } | ||
667 | } | ||
668 | desc--; | ||
669 | desc->flags |= 0x80000000; | ||
670 | |||
671 | if (req->data->flags & MMC_DATA_WRITE) | ||
672 | wmt_dma_config(mmc, priv->dma_desc_device_addr, | ||
673 | PDMA_WRITE); | ||
674 | else | ||
675 | wmt_dma_config(mmc, priv->dma_desc_device_addr, | ||
676 | PDMA_READ); | ||
677 | |||
678 | wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); | ||
679 | |||
680 | priv->comp_dma = &priv->datacomp; | ||
681 | init_completion(priv->comp_dma); | ||
682 | |||
683 | wmt_dma_start(priv); | ||
684 | wmt_mci_start_command(priv); | ||
685 | } | ||
686 | } | ||
687 | |||
688 | static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | ||
689 | { | ||
690 | struct wmt_mci_priv *priv; | ||
691 | u32 reg_tmp; | ||
692 | |||
693 | priv = mmc_priv(mmc); | ||
694 | |||
695 | if (ios->power_mode == MMC_POWER_UP) { | ||
696 | wmt_reset_hardware(mmc); | ||
697 | |||
698 | wmt_set_sd_power(priv, WMT_SD_POWER_ON); | ||
699 | } | ||
700 | if (ios->power_mode == MMC_POWER_OFF) | ||
701 | wmt_set_sd_power(priv, WMT_SD_POWER_OFF); | ||
702 | |||
703 | if (ios->clock != 0) | ||
704 | clk_set_rate(priv->clk_sdmmc, ios->clock); | ||
705 | |||
706 | switch (ios->bus_width) { | ||
707 | case MMC_BUS_WIDTH_8: | ||
708 | reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); | ||
709 | writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL); | ||
710 | break; | ||
711 | case MMC_BUS_WIDTH_4: | ||
712 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
713 | writeb(reg_tmp | BM_FOURBIT_MODE, priv->sdmmc_base + | ||
714 | SDMMC_BUSMODE); | ||
715 | |||
716 | reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); | ||
717 | writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); | ||
718 | break; | ||
719 | case MMC_BUS_WIDTH_1: | ||
720 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
721 | writeb(reg_tmp & BM_ONEBIT_MASK, priv->sdmmc_base + | ||
722 | SDMMC_BUSMODE); | ||
723 | |||
724 | reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); | ||
725 | writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); | ||
726 | break; | ||
727 | } | ||
728 | } | ||
729 | |||
730 | static int wmt_mci_get_ro(struct mmc_host *mmc) | ||
731 | { | ||
732 | struct wmt_mci_priv *priv = mmc_priv(mmc); | ||
733 | |||
734 | return !(readb(priv->sdmmc_base + SDMMC_STS0) & STS0_WRITE_PROTECT); | ||
735 | } | ||
736 | |||
737 | static int wmt_mci_get_cd(struct mmc_host *mmc) | ||
738 | { | ||
739 | struct wmt_mci_priv *priv = mmc_priv(mmc); | ||
740 | u32 cd = (readb(priv->sdmmc_base + SDMMC_STS0) & STS0_CD_GPI) >> 3; | ||
741 | |||
742 | return !(cd ^ priv->cd_inverted); | ||
743 | } | ||
744 | |||
745 | static struct mmc_host_ops wmt_mci_ops = { | ||
746 | .request = wmt_mci_request, | ||
747 | .set_ios = wmt_mci_set_ios, | ||
748 | .get_ro = wmt_mci_get_ro, | ||
749 | .get_cd = wmt_mci_get_cd, | ||
750 | }; | ||
751 | |||
752 | /* Controller capabilities */ | ||
753 | static struct wmt_mci_caps wm8505_caps = { | ||
754 | .f_min = 390425, | ||
755 | .f_max = 50000000, | ||
756 | .ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34, | ||
757 | .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | | ||
758 | MMC_CAP_SD_HIGHSPEED, | ||
759 | .max_seg_size = 65024, | ||
760 | .max_segs = 128, | ||
761 | .max_blk_size = 2048, | ||
762 | }; | ||
763 | |||
764 | static struct of_device_id wmt_mci_dt_ids[] = { | ||
765 | { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps }, | ||
766 | { /* Sentinel */ }, | ||
767 | }; | ||
768 | |||
769 | static int __devinit wmt_mci_probe(struct platform_device *pdev) | ||
770 | { | ||
771 | struct mmc_host *mmc; | ||
772 | struct wmt_mci_priv *priv; | ||
773 | struct device_node *np = pdev->dev.of_node; | ||
774 | const struct of_device_id *of_id = | ||
775 | of_match_device(wmt_mci_dt_ids, &pdev->dev); | ||
776 | const struct wmt_mci_caps *wmt_caps = of_id->data; | ||
777 | int ret; | ||
778 | int regular_irq, dma_irq; | ||
779 | |||
780 | if (!of_id || !of_id->data) { | ||
781 | dev_err(&pdev->dev, "Controller capabilities data missing\n"); | ||
782 | return -EFAULT; | ||
783 | } | ||
784 | |||
785 | if (!np) { | ||
786 | dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n"); | ||
787 | return -EFAULT; | ||
788 | } | ||
789 | |||
790 | regular_irq = irq_of_parse_and_map(np, 0); | ||
791 | dma_irq = irq_of_parse_and_map(np, 1); | ||
792 | |||
793 | if (!regular_irq || !dma_irq) { | ||
794 | dev_err(&pdev->dev, "Getting IRQs failed!\n"); | ||
795 | ret = -ENXIO; | ||
796 | goto fail1; | ||
797 | } | ||
798 | |||
799 | mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev); | ||
800 | if (!mmc) { | ||
801 | dev_err(&pdev->dev, "Failed to allocate mmc_host\n"); | ||
802 | ret = -ENOMEM; | ||
803 | goto fail1; | ||
804 | } | ||
805 | |||
806 | mmc->ops = &wmt_mci_ops; | ||
807 | mmc->f_min = wmt_caps->f_min; | ||
808 | mmc->f_max = wmt_caps->f_max; | ||
809 | mmc->ocr_avail = wmt_caps->ocr_avail; | ||
810 | mmc->caps = wmt_caps->caps; | ||
811 | |||
812 | mmc->max_seg_size = wmt_caps->max_seg_size; | ||
813 | mmc->max_segs = wmt_caps->max_segs; | ||
814 | mmc->max_blk_size = wmt_caps->max_blk_size; | ||
815 | |||
816 | mmc->max_req_size = (16*512*mmc->max_segs); | ||
817 | mmc->max_blk_count = mmc->max_req_size / 512; | ||
818 | |||
819 | priv = mmc_priv(mmc); | ||
820 | priv->mmc = mmc; | ||
821 | priv->dev = &pdev->dev; | ||
822 | |||
823 | priv->power_inverted = 0; | ||
824 | priv->cd_inverted = 0; | ||
825 | |||
826 | if (of_get_property(np, "sdon-inverted", NULL)) | ||
827 | priv->power_inverted = 1; | ||
828 | if (of_get_property(np, "cd-inverted", NULL)) | ||
829 | priv->cd_inverted = 1; | ||
830 | |||
831 | priv->sdmmc_base = of_iomap(np, 0); | ||
832 | if (!priv->sdmmc_base) { | ||
833 | dev_err(&pdev->dev, "Failed to map IO space\n"); | ||
834 | ret = -ENOMEM; | ||
835 | goto fail2; | ||
836 | } | ||
837 | |||
838 | priv->irq_regular = regular_irq; | ||
839 | priv->irq_dma = dma_irq; | ||
840 | |||
841 | ret = request_irq(regular_irq, wmt_mci_regular_isr, 0, "sdmmc", priv); | ||
842 | if (ret) { | ||
843 | dev_err(&pdev->dev, "Register regular IRQ fail\n"); | ||
844 | goto fail3; | ||
845 | } | ||
846 | |||
847 | ret = request_irq(dma_irq, wmt_mci_dma_isr, 32, "sdmmc", priv); | ||
848 | if (ret) { | ||
849 | dev_err(&pdev->dev, "Register DMA IRQ fail\n"); | ||
850 | goto fail4; | ||
851 | } | ||
852 | |||
853 | /* alloc some DMA buffers for descriptors/transfers */ | ||
854 | priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev, | ||
855 | mmc->max_blk_count * 16, | ||
856 | &priv->dma_desc_device_addr, | ||
857 | 208); | ||
858 | if (!priv->dma_desc_buffer) { | ||
859 | dev_err(&pdev->dev, "DMA alloc fail\n"); | ||
860 | ret = -EPERM; | ||
861 | goto fail5; | ||
862 | } | ||
863 | |||
864 | platform_set_drvdata(pdev, mmc); | ||
865 | |||
866 | priv->clk_sdmmc = of_clk_get(np, 0); | ||
867 | if (IS_ERR(priv->clk_sdmmc)) { | ||
868 | dev_err(&pdev->dev, "Error getting clock\n"); | ||
869 | ret = PTR_ERR(priv->clk_sdmmc); | ||
870 | goto fail5; | ||
871 | } | ||
872 | |||
873 | clk_prepare_enable(priv->clk_sdmmc); | ||
874 | |||
875 | /* configure the controller to a known 'ready' state */ | ||
876 | wmt_reset_hardware(mmc); | ||
877 | |||
878 | mmc_add_host(mmc); | ||
879 | |||
880 | dev_info(&pdev->dev, "WMT SDHC Controller initialized\n"); | ||
881 | |||
882 | return 0; | ||
883 | fail5: | ||
884 | free_irq(dma_irq, priv); | ||
885 | fail4: | ||
886 | free_irq(regular_irq, priv); | ||
887 | fail3: | ||
888 | iounmap(priv->sdmmc_base); | ||
889 | fail2: | ||
890 | mmc_free_host(mmc); | ||
891 | fail1: | ||
892 | return ret; | ||
893 | } | ||
894 | |||
895 | static int __devexit wmt_mci_remove(struct platform_device *pdev) | ||
896 | { | ||
897 | struct mmc_host *mmc; | ||
898 | struct wmt_mci_priv *priv; | ||
899 | struct resource *res; | ||
900 | u32 reg_tmp; | ||
901 | |||
902 | mmc = platform_get_drvdata(pdev); | ||
903 | priv = mmc_priv(mmc); | ||
904 | |||
905 | /* reset SD controller */ | ||
906 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
907 | writel(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); | ||
908 | reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); | ||
909 | writew(reg_tmp & ~(0xA000), priv->sdmmc_base + SDMMC_BLKLEN); | ||
910 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); | ||
911 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); | ||
912 | |||
913 | /* release the dma buffers */ | ||
914 | dma_free_coherent(&pdev->dev, priv->mmc->max_blk_count * 16, | ||
915 | priv->dma_desc_buffer, priv->dma_desc_device_addr); | ||
916 | |||
917 | mmc_remove_host(mmc); | ||
918 | |||
919 | free_irq(priv->irq_regular, priv); | ||
920 | free_irq(priv->irq_dma, priv); | ||
921 | |||
922 | iounmap(priv->sdmmc_base); | ||
923 | |||
924 | clk_disable_unprepare(priv->clk_sdmmc); | ||
925 | clk_put(priv->clk_sdmmc); | ||
926 | |||
927 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
928 | release_mem_region(res->start, res->end - res->start + 1); | ||
929 | |||
930 | mmc_free_host(mmc); | ||
931 | |||
932 | platform_set_drvdata(pdev, NULL); | ||
933 | |||
934 | dev_info(&pdev->dev, "WMT MCI device removed\n"); | ||
935 | |||
936 | return 0; | ||
937 | } | ||
938 | |||
939 | #ifdef CONFIG_PM | ||
940 | static int wmt_mci_suspend(struct device *dev) | ||
941 | { | ||
942 | u32 reg_tmp; | ||
943 | struct platform_device *pdev = to_platform_device(dev); | ||
944 | struct mmc_host *mmc = platform_get_drvdata(pdev); | ||
945 | struct wmt_mci_priv *priv; | ||
946 | int ret; | ||
947 | |||
948 | if (!mmc) | ||
949 | return 0; | ||
950 | |||
951 | priv = mmc_priv(mmc); | ||
952 | ret = mmc_suspend_host(mmc); | ||
953 | |||
954 | if (!ret) { | ||
955 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
956 | writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + | ||
957 | SDMMC_BUSMODE); | ||
958 | |||
959 | reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); | ||
960 | writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); | ||
961 | |||
962 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); | ||
963 | writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); | ||
964 | |||
965 | clk_disable(priv->clk_sdmmc); | ||
966 | } | ||
967 | return ret; | ||
968 | } | ||
969 | |||
970 | static int wmt_mci_resume(struct device *dev) | ||
971 | { | ||
972 | u32 reg_tmp; | ||
973 | struct platform_device *pdev = to_platform_device(dev); | ||
974 | struct mmc_host *mmc = platform_get_drvdata(pdev); | ||
975 | struct wmt_mci_priv *priv; | ||
976 | int ret = 0; | ||
977 | |||
978 | if (mmc) { | ||
979 | priv = mmc_priv(mmc); | ||
980 | clk_enable(priv->clk_sdmmc); | ||
981 | |||
982 | reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); | ||
983 | writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + | ||
984 | SDMMC_BUSMODE); | ||
985 | |||
986 | reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); | ||
987 | writew(reg_tmp | (BLKL_GPI_CD | BLKL_INT_ENABLE), | ||
988 | priv->sdmmc_base + SDMMC_BLKLEN); | ||
989 | |||
990 | reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); | ||
991 | writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base + | ||
992 | SDMMC_INTMASK0); | ||
993 | |||
994 | ret = mmc_resume_host(mmc); | ||
995 | } | ||
996 | |||
997 | return ret; | ||
998 | } | ||
999 | |||
1000 | static const struct dev_pm_ops wmt_mci_pm = { | ||
1001 | .suspend = wmt_mci_suspend, | ||
1002 | .resume = wmt_mci_resume, | ||
1003 | }; | ||
1004 | |||
1005 | #define wmt_mci_pm_ops (&wmt_mci_pm) | ||
1006 | |||
1007 | #else /* !CONFIG_PM */ | ||
1008 | |||
1009 | #define wmt_mci_pm_ops NULL | ||
1010 | |||
1011 | #endif | ||
1012 | |||
1013 | static struct platform_driver wmt_mci_driver = { | ||
1014 | .probe = wmt_mci_probe, | ||
1015 | .remove = __exit_p(wmt_mci_remove), | ||
1016 | .driver = { | ||
1017 | .name = DRIVER_NAME, | ||
1018 | .owner = THIS_MODULE, | ||
1019 | .pm = wmt_mci_pm_ops, | ||
1020 | .of_match_table = wmt_mci_dt_ids, | ||
1021 | }, | ||
1022 | }; | ||
1023 | |||
1024 | module_platform_driver(wmt_mci_driver); | ||
1025 | |||
1026 | MODULE_DESCRIPTION("Wondermedia MMC/SD Driver"); | ||
1027 | MODULE_AUTHOR("Tony Prisk"); | ||
1028 | MODULE_LICENSE("GPL v2"); | ||
1029 | MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids); | ||