MOXA linux-2.6.x / linux-2.6.19-uc1 from UC-7110-LX-BOOTLOADER-1.9_VERSION-4.2.tgz
[linux-2.6.19-moxart.git] / drivers / mmc / moxasd.c
blob1a94903c984a827e4e7fe738d4dfb22cfa7bb0bd
1 /*
2 * linux/drivers/mmc/moxasd.c - Moxa CPU SD/MMC driver
4 * Copyright (C) 2005 Moxa Tech., All Rights Reserved.
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.
11 #if 1 // add by Victor Yu. 02-09-2007
12 #include <linux/version.h>
13 #endif
14 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) // add by Victor Yu. 02-09-2007
15 #include <linux/config.h>
16 #endif
17 #include <asm/arch/moxa.h>
18 #include <asm/arch/cpe_int.h>
19 #include <linux/module.h>
20 #include <linux/init.h>
21 #include <linux/ioport.h>
22 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
23 #include <linux/platform_device.h>
24 #else
25 #include <linux/device.h>
26 #endif // LINUX_VERSION_CODE
27 #include <linux/delay.h>
28 #include <linux/interrupt.h>
29 #include <linux/blkdev.h>
30 #include <linux/dma-mapping.h>
31 #include <linux/mmc/host.h>
32 #include <linux/sched.h>
33 #include <linux/mmc/protocol.h>
35 #include <asm/dma.h>
36 #include <asm/io.h>
37 #include <asm/irq.h>
38 #include <asm/sizes.h>
39 #include <asm/arch/gpio.h>
41 #include "moxasd.h"
43 #if 0 // mask by Victor Yu. 03-19-2007
44 #define MSD_RETRY_COUNT 1000
45 #else
46 #define MSD_RETRY_COUNT 100
47 #endif
48 //#define CONFIG_MMC_DEBUG
49 #ifdef CONFIG_MMC_DEBUG
50 #define DBG(x...) printk(x)
51 #else
52 #define DBG(x...)
53 #endif
55 struct moxasd_host {
56 struct mmc_host *mmc;
57 spinlock_t lock;
58 moxasd_reg *reg;
59 apb_dma_priv *dma;
60 #ifdef MSD_SUPPORT_GET_CLOCK
61 unsigned int sysclk;
62 #endif
63 struct mmc_request *mrq;
64 struct mmc_data *data;
66 struct scatterlist *cur_sg; /* Current SG entry */
67 unsigned int num_sg; /* Number of entries left */
68 void *mapped_sg; /* vaddr of mapped sg */
69 unsigned int remain; /* Data left in curren entry */
70 int size; /* Total size of transfer */
72 struct tasklet_struct card_change_tasklet;
73 struct tasklet_struct fifo_run_tasklet;
76 static inline void moxasd_init_sg(struct moxasd_host* host, struct mmc_data* data)
79 * Get info. about SG list from data structure.
81 host->cur_sg = data->sg;
82 host->num_sg = data->sg_len;
84 host->remain = host->cur_sg->length;
85 #if 1 // add by Victor Yu. 07-04-2007
86 if ( host->remain > host->size )
87 host->remain = host->size;
88 host->mapped_sg = NULL;
89 #endif
90 data->error = MMC_ERR_NONE;
93 static inline int moxasd_next_sg(struct moxasd_host* host)
95 #if 1 // add by Victor Yu. 07-04-2007
96 struct mmc_data *data=host->data;
97 #endif
99 * Skip to next SG entry.
101 host->cur_sg++;
102 host->num_sg--;
105 * Any entries left?
107 if (host->num_sg > 0) {
108 host->remain = host->cur_sg->length;
109 #if 1 // add by Victor Yu. 07-04-2007
111 int remain;
112 remain = host->size - data->bytes_xfered;
113 if ( remain > 0 && remain < host->remain ) {
114 host->remain = remain;
117 #endif
120 return host->num_sg;
123 static inline char *moxasd_kmap_sg(struct moxasd_host* host)
125 host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ);
126 return host->mapped_sg + host->cur_sg->offset;
129 static void moxasd_do_fifo(struct moxasd_host *host, struct mmc_data *data)
131 char *buffer;
132 int wcnt, i;
134 #if 1 // add by Victor Yu. 07-06-2007
135 if ( host->mapped_sg ) {
136 kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
137 moxasd_next_sg(host);
139 #endif
140 if ( host->size == data->bytes_xfered ) {
141 return;
143 buffer = moxasd_kmap_sg(host);
144 if ( host->size > MSD_FIFO_LENB && host->dma ) {
145 apb_dma_conf_param param;
146 param.size = host->remain;
147 param.burst_mode = APB_DMAB_BURST_MODE;
148 param.data_width = APB_DMAB_DATA_WIDTH_4;
149 if ( data->flags & MMC_DATA_WRITE ) {
150 param.source_addr = (unsigned int)buffer;
151 param.dest_addr = (unsigned int)&host->reg->data_window;
152 param.dest_inc = APB_DMAB_DEST_INC_0;
153 param.source_inc = APB_DMAB_DEST_INC_4_16;
154 param.dest_sel = APB_DMAB_DEST_APB;
155 param.source_sel = APB_DMAB_SOURCE_AHB;
156 } else {
157 param.dest_addr = (unsigned int)buffer;
158 param.source_addr = (unsigned int)&host->reg->data_window;
159 param.source_inc = APB_DMAB_DEST_INC_0;
160 param.dest_inc = APB_DMAB_DEST_INC_4_16;
161 param.source_sel = APB_DMAB_DEST_APB;
162 param.dest_sel = APB_DMAB_SOURCE_AHB;
164 data->bytes_xfered += host->remain;
165 apb_dma_conf(host->dma, &param);
166 apb_dma_enable(host->dma);
167 } else {
168 wcnt = host->remain >> 2;
169 if ( data->flags & MMC_DATA_WRITE ) {
170 for ( i=0; i<wcnt; i++, buffer+=4 )
171 writel(*(unsigned int *)buffer, &host->reg->data_window);
172 } else {
173 for ( i=0; i<wcnt; i++, buffer+=4 )
174 *(unsigned int *)buffer = readl(&host->reg->data_window);
176 wcnt <<= 2;
177 host->remain -= wcnt;
178 data->bytes_xfered += wcnt;
182 static void moxasd_request_done(struct moxasd_host *host)
184 struct mmc_request *mrq=host->mrq;
186 if ( mrq == NULL ) {
187 return;
189 host->mrq = NULL;
190 host->data = NULL;
191 mmc_request_done(host->mmc, mrq);
194 static void moxasd_prepare_data(struct moxasd_host *host, struct mmc_data *data)
196 unsigned int timeout, datactrl;
197 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
198 int blksz_bits;
199 #endif // LINUX_VERSION_CODE
201 host->data = data;
202 // initialize the data size
203 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
204 host->size = data->blocks * data->blksz;
205 blksz_bits = ffs(data->blksz) - 1;
206 BUG_ON(1 << blksz_bits != data->blksz);
207 #else
208 host->size = data->blocks << data->blksz_bits;
209 #endif // LINUX_VERSION_CODE
210 moxasd_init_sg(host, data);
212 // initialize the timeout value
213 timeout = (host->mmc->f_max/1000) * (data->timeout_ns/1000);
214 timeout *= 2;
216 // initialize the data control
217 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
218 datactrl = (blksz_bits & MSD_BLK_SIZE_MASK) | MSD_DATA_EN;
219 #else
220 datactrl = (data->blksz_bits & MSD_BLK_SIZE_MASK) | MSD_DATA_EN;
221 #endif // LINUX_VERSION_CODE
222 if ( data->flags & MMC_DATA_WRITE ) {
223 datactrl |= MSD_DATA_WRITE;
225 if ( host->size > MSD_FIFO_LENB && host->dma ) {
226 datactrl |= MSD_DMA_EN;
228 writel(timeout, &host->reg->data_timer);
229 writel(host->size, &host->reg->data_length);
230 writel(datactrl, &host->reg->data_control);
232 if ( host->size > MSD_FIFO_LENB && host->dma ) {
233 writel(MSD_INT_CARD_CHANGE, &host->reg->interrupt_mask);
234 moxasd_do_fifo(host, data);
235 //tasklet_schedule(&host->fifo_run_tasklet);
236 } else {
237 writel(MSD_INT_FIFO_URUN|MSD_INT_FIFO_ORUN|MSD_INT_CARD_CHANGE, &host->reg->interrupt_mask);
241 static void moxasd_send_command(struct moxasd_host *host, struct mmc_command *cmd)
243 unsigned int status, cmdctrl;
244 int retry=0;
246 #if 1 // add by Victor Yu. 03-19-2007
247 cmd->error = MMC_ERR_TIMEOUT;
248 #endif
250 // first clear status
251 writel(MSD_CLR_RSP_TIMEOUT|MSD_CLR_RSP_CRC_OK|MSD_CLR_RSP_CRC_FAIL|MSD_CLR_CMD_SENT, &host->reg->clear);
253 // write argument
254 writel(cmd->arg, &host->reg->argument);
256 // write command
257 cmdctrl = cmd->opcode & MSD_CMD_IDX_MASK;
258 if ( cmdctrl == SD_APP_SET_BUS_WIDTH ||
259 cmdctrl == SD_APP_OP_COND ||
260 cmdctrl == SD_APP_SEND_SCR ) // this is SD application specific command
261 cmdctrl |= MSD_APP_CMD;
262 if ( cmd->flags & MMC_RSP_LONG )
263 cmdctrl |= (MSD_LONG_RSP|MSD_NEED_RSP);
264 if ( cmd->flags & MMC_RSP_SHORT )
265 cmdctrl |= MSD_NEED_RSP;
266 writel(cmdctrl|MSD_CMD_EN, &host->reg->command);
268 // wait response
269 while ( retry++ < MSD_RETRY_COUNT ) {
270 status = readl(&host->reg->status);
271 if ( status & MSD_CARD_DETECT ) { // card is removed
272 cmd->error = MMC_ERR_TIMEOUT;
273 break;
275 if ( cmdctrl & MSD_NEED_RSP ) {
276 if ( status & MSD_RSP_TIMEOUT ) {
277 writel(MSD_CLR_RSP_TIMEOUT, &host->reg->clear);
278 cmd->error = MMC_ERR_TIMEOUT;
279 break;
281 #if 0
282 if ( status & MSD_RSP_CRC_FAIL ) {
283 #else
284 if ( (cmd->flags&MMC_RSP_CRC) && (status&MSD_RSP_CRC_FAIL) ) {
285 #endif
286 writel(MSD_CLR_RSP_CRC_FAIL, &host->reg->clear);
287 cmd->error = MMC_ERR_BADCRC;
288 break;
290 if ( status & MSD_RSP_CRC_OK ) {
291 writel(MSD_CLR_RSP_CRC_OK, &host->reg->clear);
292 // read response
293 cmd->resp[0] = readl(&host->reg->response0);
294 cmd->resp[1] = readl(&host->reg->response1);
295 cmd->resp[2] = readl(&host->reg->response2);
296 cmd->resp[3] = readl(&host->reg->response3);
297 cmd->error = MMC_ERR_NONE;
298 break;
300 } else {
301 if ( status & MSD_CMD_SENT ) {
302 writel(MSD_CLR_CMD_SENT, &host->reg->clear);
303 cmd->error = MMC_ERR_NONE;
304 break;
310 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,12) // add by Victor Yu. 02-16-2007
311 static irqreturn_t moxasd_irq(int irq, void *devid)
312 #else
313 static irqreturn_t moxasd_irq(int irq, void *devid, struct pt_regs *regs)
314 #endif // LINUX_VERSION_CODE
316 struct moxasd_host *host=devid;
317 unsigned int status;
319 // get the interrupt status
320 status = readl(&host->reg->status);
322 // acknowledge the interurpt
323 if ( status & MSD_CARD_CHANGE ) { // has card inserted or removed
324 //writel(MSD_CLR_CARD_CHANGE, &host->reg->clear);
325 tasklet_schedule(&host->card_change_tasklet);
328 if ( status & (MSD_FIFO_ORUN|MSD_FIFO_URUN) ) {
329 writel(status&(MSD_FIFO_ORUN|MSD_FIFO_URUN), &host->reg->clear);
330 tasklet_schedule(&host->fifo_run_tasklet);
333 return IRQ_HANDLED;
336 static void moxasd_fifo_run(unsigned long param)
338 struct moxasd_host *host=(struct moxasd_host *)param;
339 struct mmc_data *data;
340 unsigned long flags;
342 spin_lock_irqsave(&host->lock, flags);
343 host = (struct moxasd_host *)param;
344 data = host->data;
345 if ( host->mrq == NULL || data == NULL ) {
346 spin_unlock_irqrestore(&host->lock, flags);
347 return;
349 moxasd_do_fifo(host, data);
350 if ( host->size == data->bytes_xfered ) {
351 #if 1 // mask by Victor Yu. 07-04-2007
352 unsigned int status;
353 while ( 1 ) {
354 status = readl(&host->reg->status);
355 if ( status & (MSD_DATA_CRC_OK|MSD_DATA_CRC_FAIL|MSD_DATA_END) )
356 break;
357 current->state = TASK_INTERRUPTIBLE;
358 schedule_timeout(5);
360 if ( status & MSD_DATA_CRC_OK ) {
361 writel(MSD_CLR_DATA_CRC_OK, &host->reg->clear);
363 if ( status & MSD_DATA_CRC_FAIL ) {
364 writel(MSD_CLR_DATA_CRC_FAIL, &host->reg->clear);
365 data->error = MMC_ERR_TIMEOUT;
367 if ( status & MSD_DATA_END ) {
368 writel(MSD_CLR_DATA_END, &host->reg->clear);
370 #endif
371 if ( data->stop ) {
372 moxasd_send_command(host, data->stop);
374 } else {
375 spin_unlock_irqrestore(&host->lock, flags);
376 return;
379 moxasd_request_done(host);
380 spin_unlock_irqrestore(&host->lock, flags);
383 static void moxasd_card_change(unsigned long param)
385 struct moxasd_host *host=(struct moxasd_host *)param;
386 unsigned int status;
387 int delay;
388 unsigned long flags;
390 spin_lock_irqsave(&host->lock, flags);
391 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
392 udelay(1000);
393 udelay(1000);
394 udelay(1000);
395 #else
396 udelay(3000);
397 #endif // LINUX_VERSION_CODE
398 status = readl(&host->reg->status);
399 if ( status & MSD_CARD_DETECT ) { // card removed
400 printk("Moxa CPU SD/MMC card is removed.\n");
401 delay = 0;
402 if ( host->data ) {
403 if ( host->dma && host->size > MSD_FIFO_LENB )
404 apb_dma_disable(host->dma);
405 host->size = host->data->bytes_xfered;
406 moxasd_fifo_run(*(unsigned long *)host);
407 host->data->error = MMC_ERR_TIMEOUT;
408 moxasd_request_done(host);
410 } else { // card inserted
411 printk("Moxa CPU SD/MMC card is inserted.\n");
412 if ( readl(&host->reg->clock_control) & MSD_CLK_SD ) { // SD
413 host->mmc->f_max = 25000000;
414 host->mmc->mode = MMC_MODE_SD;
415 } else {
416 host->mmc->f_max = 20000000;
417 host->mmc->mode = MMC_MODE_MMC;
419 delay = 500;
421 writel(MSD_CLR_CARD_CHANGE, &host->reg->clear);
422 spin_unlock_irqrestore(&host->lock, flags);
423 mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
426 static void moxasd_dma_irq(void *param)
428 struct moxasd_host *host=(struct moxasd_host *)param;
430 if ( host )
431 tasklet_schedule(&host->fifo_run_tasklet);
434 static void moxasd_request(struct mmc_host *mmc, struct mmc_request *mrq)
436 struct moxasd_host *host=mmc_priv(mmc);
437 struct mmc_command *cmd;
438 unsigned long flags;
440 spin_lock_irqsave(&host->lock, flags);
441 host->mrq = mrq;
442 cmd = mrq->cmd;
444 // if no card inserted, return timeout error
445 if ( readl(&host->reg->status) & MSD_CARD_DETECT ) { // card is removed
446 cmd->error = MMC_ERR_TIMEOUT;
447 goto request_done;
450 // request include data or not
451 if ( cmd->data ) {
452 moxasd_prepare_data(host, cmd->data);
455 // do request command
456 moxasd_send_command(host, cmd);
458 if ( cmd->data && cmd->error == MMC_ERR_NONE ) {
459 spin_unlock_irqrestore(&host->lock, flags);
460 return;
463 request_done:
464 moxasd_request_done(host);
465 spin_unlock_irqrestore(&host->lock, flags);
468 #define MIN_POWER (MMC_VDD_360 - MSD_SD_POWER_MASK)
469 static void moxasd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
471 struct moxasd_host *host=mmc_priv(mmc);
472 unsigned long flags;
474 spin_lock_irqsave(&host->lock, flags);
475 if (ios->clock) {
476 int div;
477 #ifdef MSD_SUPPORT_GET_CLOCK
478 div = (host->sysclk / (host->mmc->f_max * 2)) - 1;
479 #else
480 div = (APB_CLK / (host->mmc->f_max * 2)) - 1;
481 #endif
482 if ( div > MSD_CLK_DIV_MASK )
483 div = MSD_CLK_DIV_MASK;
484 else if ( div < 0 )
485 div = 0;
486 if ( host->mmc->mode == MMC_MODE_SD )
487 div |= MSD_CLK_SD;
488 writel(div, &host->reg->clock_control);
489 } else if ( !(readl(&host->reg->clock_control) & MSD_CLK_DIS) ) {
491 * Ensure that the clock is off.
493 writel(readl(&host->reg->clock_control)|MSD_CLK_DIS, &host->reg->clock_control);
496 if ( ios->power_mode == MMC_POWER_OFF ) {
497 writel(readl(&host->reg->power_control)&~MSD_SD_POWER_ON, &host->reg->power_control);
498 } else {
499 unsigned short power;
500 if ( ios->vdd < MIN_POWER )
501 power = 0;
502 else
503 power = ios->vdd - MIN_POWER;
504 writel(MSD_SD_POWER_ON|(unsigned int)power, &host->reg->power_control);
507 #if 1
508 if ( ios->bus_width == MMC_BUS_WIDTH_1 ) {
509 writel(MSD_SINGLE_BUS, &host->reg->bus_width);
510 } else {
511 writel(MSD_WIDE_BUS, &host->reg->bus_width);
513 #endif
514 spin_unlock_irqrestore(&host->lock, flags);
518 * To check write protect or not. Return 0 for none, 1 for write protect.
520 static int moxasd_get_ro(struct mmc_host *mmc)
522 struct moxasd_host *host=mmc_priv(mmc);
524 if ( readl(&host->reg->status) & MSD_WRITE_PROT )
525 return 1;
526 else
527 return 0;
530 static struct mmc_host_ops moxasd_ops = {
531 .request = moxasd_request,
532 .set_ios = moxasd_set_ios,
533 .get_ro = moxasd_get_ro,
536 static int moxasd_probe(struct device *dev)
538 struct mmc_host *mmc;
539 struct moxasd_host *host=NULL;
540 int ret;
542 mmc = mmc_alloc_host(sizeof(struct moxasd_host), dev);
543 if (!mmc) {
544 ret = -ENOMEM;
545 goto out;
548 mmc->ops = &moxasd_ops;
549 mmc->f_min = 400000;
550 mmc->f_max = 25000000;
551 mmc->mode = MMC_MODE_SD;
552 #if 1
553 mmc->ocr_avail = 0xffff00; // support 2.0v - 3.6v power
554 #else
555 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
556 mmc->caps = MMC_CAP_4_BIT_DATA;
557 mmc->max_hw_segs = 128;
558 mmc->max_phys_segs = 128;
559 mmc->max_sectors = 128;
560 mmc->max_seg_size = mmc->max_sectors * 512;
561 #endif
563 host = mmc_priv(mmc);
564 host->mmc = mmc;
565 spin_lock_init(&host->lock);
566 tasklet_init(&host->card_change_tasklet, moxasd_card_change, (unsigned long)host);
567 tasklet_init(&host->fifo_run_tasklet, moxasd_fifo_run, (unsigned long)host);
568 host->reg = (moxasd_reg *)CPE_SD_BASE;
569 host->dma = apb_dma_alloc(APB_DMA_SD_REQ_NO);
570 if ( host->dma ) {
571 apb_dma_set_irq(host->dma, moxasd_dma_irq, host);
574 #ifdef MSD_SUPPORT_GET_CLOCK
575 // get system clock
577 unsigned int mul, val, div;
578 mul = (*(volatile unsigned int *)(CPE_PMU_BASE+0x30) >> 3) & 0x1ff;
579 val = (*(volatile unsigned int *)(CPE_PMU_BASE+0x0c) >> 4) & 0x7;
580 switch ( val ) {
581 case 0 : div = 2; break;
582 case 1 : div = 3; break;
583 case 2 : div = 4; break;
584 case 3 : div = 6; break;
585 case 4 : div = 8; break;
586 default : div = 2; break;
588 host->sysclk = (38684*mul + 10000) / (div * 10000);
589 host->sysclk = (host->sysclk * 1000000) / 2;
591 #endif
593 // change I/O multiplexing to SD, so the GPIO 17-10 will be fail
594 mcpu_gpio_mp_clear(0xff<<10);
595 #ifdef CONFIG_ARCH_UC7101
596 #define SD_LED (1<<30)
597 mcpu_gpio_mp_set(SD_LED);
598 mcpu_gpio_inout(SD_LED, MCPU_GPIO_OUTPUT);
599 mcpu_gpio_set(SD_LED, MCPU_GPIO_HIGH);
600 #endif
603 * Ensure that the host controller is shut down, and setup
604 * with our defaults.
606 writel(0, &host->reg->interrupt_mask); // disable all interrupt
607 writel(MSD_SDC_RST, &host->reg->command); // reset chip
608 while ( readl(&host->reg->command) & MSD_SDC_RST); // wait for reset finished
609 writel(0, &host->reg->interrupt_mask); // disable all interrupt
611 // to check any card inserted or not
612 if ( !(readl(&host->reg->status) & MSD_CARD_DETECT) ) { // is inserted
613 if ( readl(&host->reg->clock_control) & MSD_CLK_SD ) { // is SD card
614 mmc->f_max = 25000000;
615 mmc->mode = MMC_MODE_SD;
616 } else { // is MMC card
617 mmc->f_max = 20000000;
618 mmc->mode = MMC_MODE_MMC;
622 mmc->caps = MMC_CAP_4_BIT_DATA;
623 writel(MSD_WIDE_BUS, &host->reg->bus_width);
625 cpe_int_set_irq(IRQ_SD, EDGE, H_ACTIVE);
626 ret = request_irq(IRQ_SD, moxasd_irq, SA_INTERRUPT, "MOXASD", host);
627 if (ret)
628 goto out;
630 //writel(MSD_INT_CARD_CHANGE|MSD_INT_FIFO_ORUN|MSD_INT_FIFO_URUN, &host->reg->interrupt_mask);
631 writel(MSD_INT_CARD_CHANGE, &host->reg->interrupt_mask);
632 dev_set_drvdata(dev, mmc);
633 mmc_add_host(mmc);
635 return 0;
637 out:
638 if (mmc)
639 mmc_free_host(mmc);
641 return ret;
644 static int moxasd_remove(struct device *dev)
646 struct mmc_host *mmc=dev_get_drvdata(dev);
648 dev_set_drvdata(dev, NULL);
650 if (mmc) {
651 struct moxasd_host *host=mmc_priv(mmc);
653 mmc_remove_host(mmc);
655 // stop SD/MMC
656 if ( host->dma ) {
657 apb_dma_disable(host->dma);
658 apb_dma_release_irq(host->dma);
659 apb_dma_release(host->dma);
661 writel(0, &host->reg->interrupt_mask);
662 writel(0, &host->reg->power_control);
663 writel(readl(&host->reg->clock_control)|MSD_CLK_DIS, &host->reg->clock_control);
665 free_irq(IRQ_SD, host);
666 tasklet_kill(&host->card_change_tasklet);
667 tasklet_kill(&host->fifo_run_tasklet);
669 mmc_free_host(mmc);
671 return 0;
674 static struct platform_device moxasd_device = {
675 .name = "moxart-sd",
676 .id = -1,
679 static struct device_driver moxasd_driver = {
680 .name = "moxart-sd",
681 .bus = &platform_bus_type,
682 .probe = moxasd_probe,
683 .remove = moxasd_remove,
686 #if 1 // add by Victor Yu. 03-08-2007
687 extern int moxa_gpio_sd_used_flag; // define on arch/arm/kernel/armksyms.c
688 #endif
689 static int __init moxasd_init(void)
691 int ret;
693 printk("Moxa CPU SD/MMC Device Driver V1.0 initialize ");
694 #if 1 // add by Victor Yu. 03-08-2007
696 unsigned long flags;
697 local_irq_save(flags);
698 if ( moxa_gpio_sd_used_flag ) {
699 printk("The IO has used by other device driver !\n");
700 local_irq_restore(flags);
701 return -ENODEV;
703 moxa_gpio_sd_used_flag = 1;
704 local_irq_restore(flags);
706 #endif
707 platform_device_register(&moxasd_device);
708 ret = driver_register(&moxasd_driver);
709 if ( ret ) {
710 printk("Modules load fail !\n");
711 platform_device_unregister(&moxasd_device);
712 } else {
713 printk("Modules load OK.\n");
715 return ret;
718 static void __exit moxasd_exit(void)
720 platform_device_unregister(&moxasd_device);
721 driver_unregister(&moxasd_driver);
722 #if 1 // add by Victor Yu. 12-05-2007
724 unsigned long flags;
725 local_irq_save(flags);
726 moxa_gpio_sd_used_flag = 0;
727 local_irq_restore(flags);
729 #endif
732 module_init(moxasd_init);
733 module_exit(moxasd_exit);
735 MODULE_DESCRIPTION("Moxa CPU SD/Multimedia Card Interface Driver");
736 MODULE_LICENSE("GPL");