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>
14 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,12) // add by Victor Yu. 02-09-2007
15 #include <linux/config.h>
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>
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>
38 #include <asm/sizes.h>
42 #if 0 // mask by Victor Yu. 03-19-2007
43 #define MSD_RETRY_COUNT 1000
45 #define MSD_RETRY_COUNT 100
47 //#define CONFIG_MMC_DEBUG
48 #ifdef CONFIG_MMC_DEBUG
49 #define DBG(x...) printk(x)
59 #ifdef MSD_SUPPORT_GET_CLOCK
62 struct mmc_request *mrq;
63 struct mmc_data *data;
65 struct scatterlist *cur_sg; /* Current SG entry */
66 unsigned int num_sg; /* Number of entries left */
67 void *mapped_sg; /* vaddr of mapped sg */
68 unsigned int offset; /* Offset into current entry */
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;
85 host->remain = host->cur_sg->length;
86 data->error = MMC_ERR_NONE;
89 static inline int moxasd_next_sg(struct moxasd_host* host)
92 * Skip to next SG entry.
100 if (host->num_sg > 0) {
102 host->remain = host->cur_sg->length;
108 static inline char *moxasd_kmap_sg(struct moxasd_host* host)
110 host->mapped_sg = kmap_atomic(host->cur_sg->page, KM_BIO_SRC_IRQ) +
111 host->cur_sg->offset;
112 return host->mapped_sg;
115 #if 0 // mask by Victor Yu. 03-19-2007, No used.
116 static inline void moxasd_check_data_crc(struct moxasd_host *host, struct mmc_data *data)
120 status = readl(&host->reg->status);
121 if ( status & MSD_DATA_CRC_OK ) {
122 writel(MSD_CLR_DATA_CRC_OK, &host->reg->clear);
124 if ( status & MSD_DATA_CRC_FAIL ) {
125 writel(MSD_CLR_DATA_CRC_FAIL, &host->reg->clear);
126 data->error = MMC_ERR_TIMEOUT;
128 if ( status & MSD_DATA_END ) {
129 writel(MSD_CLR_DATA_END, &host->reg->clear);
133 static inline int moxasd_check_fifo_ready(struct moxasd_host *host)
137 status = readl(&host->reg->status);
138 if ( status & MSD_CARD_DETECT ) { // card is removed
141 if ( status & (MSD_FIFO_URUN|MSD_FIFO_ORUN) ) {
142 writel(status&(MSD_FIFO_URUN|MSD_FIFO_ORUN), &host->reg->clear);
144 if ( status & MSD_DATA_TIMEOUT ) {
145 writel(MSD_CLR_DATA_TIMEOUT, &host->reg->clear);
152 static void moxasd_do_fifo(struct moxasd_host *host, struct mmc_data *data)
157 if ( host->size == data->bytes_xfered ) {
160 //buffer = moxasd_kmap_sg(host) + host->offset;
161 buffer = moxasd_kmap_sg(host);
162 if ( host->size > MSD_FIFO_LENB && host->dma ) {
163 apb_dma_conf_param param;
164 param.size = host->remain;
165 param.burst_mode = APB_DMAB_BURST_MODE;
166 param.data_width = APB_DMAB_DATA_WIDTH_4;
167 if ( data->flags & MMC_DATA_WRITE ) {
168 param.source_addr = (unsigned int)buffer;
169 param.dest_addr = (unsigned int)&host->reg->data_window;
170 param.dest_inc = APB_DMAB_DEST_INC_0;
171 param.source_inc = APB_DMAB_DEST_INC_4_16;
172 param.dest_sel = APB_DMAB_DEST_APB;
173 param.source_sel = APB_DMAB_SOURCE_AHB;
175 param.dest_addr = (unsigned int)buffer;
176 param.source_addr = (unsigned int)&host->reg->data_window;
177 param.source_inc = APB_DMAB_DEST_INC_0;
178 param.dest_inc = APB_DMAB_DEST_INC_4_16;
179 param.source_sel = APB_DMAB_DEST_APB;
180 param.dest_sel = APB_DMAB_SOURCE_AHB;
182 data->bytes_xfered += host->remain;
183 #if 0 // don't need to do this
184 host->offset = host->remain;
187 apb_dma_conf(host->dma, ¶m);
188 kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
189 moxasd_next_sg(host);
190 apb_dma_enable(host->dma);
193 if ( host->remain >= MSD_FIFO_LENB )
194 wcnt = MSD_FIFO_LENW;
197 wcnt = host->remain >> 2;
198 if ( data->flags & MMC_DATA_WRITE ) {
199 for ( i=0; i<wcnt; i++, buffer+=4 )
200 writel(*(unsigned int *)buffer, &host->reg->data_window);
202 for ( i=0; i<wcnt; i++, buffer+=4 )
203 *(unsigned int *)buffer = readl(&host->reg->data_window);
206 host->offset += wcnt;
207 host->remain -= wcnt;
208 data->bytes_xfered += wcnt;
209 kunmap_atomic(host->mapped_sg, KM_BIO_SRC_IRQ);
210 /* because this will be just one time
211 if ( host->remain <= 0 )
212 moxasd_next_sg(host);
217 static void moxasd_request_done(struct moxasd_host *host)
219 struct mmc_request *mrq=host->mrq;
226 mmc_request_done(host->mmc, mrq);
229 static void moxasd_prepare_data(struct moxasd_host *host, struct mmc_data *data)
231 unsigned int timeout, datactrl;
232 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
234 #endif // LINUX_VERSION_CODE
237 moxasd_init_sg(host, data);
239 // initialize the timeout value
240 timeout = (host->mmc->f_max/1000000) * (data->timeout_ns/1000);
241 timeout += data->timeout_clks;
242 writel(timeout, &host->reg->data_timer);
244 // initialize the data size
245 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
246 host->size = data->blocks * data->blksz;
247 blksz_bits = ffs(data->blksz) - 1;
248 BUG_ON(1 << blksz_bits != data->blksz);
250 host->size = data->blocks << data->blksz_bits;
251 #endif // LINUX_VERSION_CODE
252 writel(host->size, &host->reg->data_length);
254 // initialize the data control
255 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
256 datactrl = (blksz_bits & MSD_BLK_SIZE_MASK) | MSD_DATA_EN;
258 datactrl = (data->blksz_bits & MSD_BLK_SIZE_MASK) | MSD_DATA_EN;
259 #endif // LINUX_VERSION_CODE
260 if ( data->flags & MMC_DATA_WRITE ) {
261 datactrl |= MSD_DATA_WRITE;
263 if ( host->size > MSD_FIFO_LENB && host->dma ) {
264 datactrl |= MSD_DMA_EN;
266 writel(datactrl, &host->reg->data_control);
269 //if ( host->size > MSD_FIFO_LENB && (data->flags & MMC_DATA_READ) ) {
270 if ( host->size > MSD_FIFO_LENB ) {
271 // disable the overrun & underrun interrupt
273 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
274 local_irq_save(flags);
278 #endif // LINUX_VERSION_CODE
279 writel(MSD_INT_CARD_CHANGE, &host->reg->interrupt_mask);
280 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
281 local_irq_restore(flags);
283 restore_flags(flags);
284 #endif // LINUX_VERSION_CODE
285 moxasd_do_fifo(host, data);
287 // enable the overrun & underrun interrupt
289 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
290 local_irq_save(flags);
294 #endif // LINUX_VERSION_CODE
295 writel(MSD_INT_FIFO_URUN|MSD_INT_FIFO_ORUN|MSD_INT_CARD_CHANGE, &host->reg->interrupt_mask);
296 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 02-09-2007
297 local_irq_restore(flags);
299 restore_flags(flags);
300 #endif // LINUX_VERSION_CODE
305 static void moxasd_send_command(struct moxasd_host *host, struct mmc_command *cmd)
307 unsigned int status, cmdctrl;
310 #if 1 // add by Victor Yu. 03-19-2007
311 cmd->error = MMC_ERR_NONE;
314 // first clear status
315 writel(MSD_CLR_RSP_TIMEOUT|MSD_CLR_RSP_CRC_OK|MSD_CLR_RSP_CRC_FAIL|MSD_CLR_CMD_SENT, &host->reg->clear);
318 writel(cmd->arg, &host->reg->argument);
321 cmdctrl = cmd->opcode & MSD_CMD_IDX_MASK;
322 if ( cmdctrl == SD_APP_SET_BUS_WIDTH ||
323 cmdctrl == SD_APP_OP_COND ||
324 cmdctrl == SD_APP_SEND_SCR ) // this is SD application specific command
325 cmdctrl |= MSD_APP_CMD;
326 if ( cmd->flags & MMC_RSP_LONG )
327 cmdctrl |= (MSD_LONG_RSP|MSD_NEED_RSP);
328 if ( cmd->flags & MMC_RSP_SHORT )
329 cmdctrl |= MSD_NEED_RSP;
330 writel(cmdctrl|MSD_CMD_EN, &host->reg->command);
333 while ( retry++ < MSD_RETRY_COUNT ) {
334 status = readl(&host->reg->status);
335 if ( status & MSD_CARD_DETECT ) { // card is removed
336 cmd->error = MMC_ERR_TIMEOUT;
337 #if 0 // mask by Victor Yu. 03-19-2007
343 if ( cmdctrl & MSD_NEED_RSP ) {
344 if ( status & MSD_RSP_TIMEOUT ) {
345 writel(MSD_CLR_RSP_TIMEOUT, &host->reg->clear);
346 cmd->error = MMC_ERR_TIMEOUT;
347 #if 0 // mask by Victor Yu. 03-19-2007
354 if ( status & MSD_RSP_CRC_FAIL ) {
356 if ( (cmd->flags&MMC_RSP_CRC) && (status&MSD_RSP_CRC_FAIL) ) {
358 writel(MSD_CLR_RSP_CRC_FAIL, &host->reg->clear);
359 cmd->error = MMC_ERR_BADCRC;
360 #if 0 // mask by Victor Yu. 03-19-2007
366 if ( status & MSD_RSP_CRC_OK ) {
367 writel(MSD_CLR_RSP_CRC_OK, &host->reg->clear);
369 cmd->resp[0] = readl(&host->reg->response0);
370 cmd->resp[1] = readl(&host->reg->response1);
371 cmd->resp[2] = readl(&host->reg->response2);
372 cmd->resp[3] = readl(&host->reg->response3);
373 #if 0 // mask by Victor Yu. 03-19-2007
374 cmd->error = MMC_ERR_NONE;
381 if ( status & MSD_CMD_SENT ) {
382 writel(MSD_CLR_CMD_SENT, &host->reg->clear);
383 #if 0 // mask by Victor Yu. 03-19-2007
384 cmd->error = MMC_ERR_NONE;
392 #if 0 // mask by Victor Yu. 03-19-2007
393 cmd->error = MMC_ERR_TIMEOUT;
397 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,12) // add by Victor Yu. 02-16-2007
398 static irqreturn_t moxasd_irq(int irq, void *devid)
400 static irqreturn_t moxasd_irq(int irq, void *devid, struct pt_regs *regs)
401 #endif // LINUX_VERSION_CODE
403 struct moxasd_host *host=devid;
406 // get the interrupt status
408 status = readl(&host->reg->status);
410 // acknowledge the interurpt
411 if ( status & MSD_CARD_CHANGE ) { // has card inserted or removed
412 //writel(MSD_CLR_CARD_CHANGE, &host->reg->clear);
413 tasklet_schedule(&host->card_change_tasklet);
416 if ( status & (MSD_FIFO_ORUN|MSD_FIFO_URUN) ) {
417 #if 0 // mask by Victor Yu. 03-19-2007
418 writel(status&(MSD_FIFO_ORUN|MSD_FIFO_URUN), &host->reg->clear);
420 tasklet_schedule(&host->fifo_run_tasklet);
426 static void moxasd_fifo_run(unsigned long param)
428 struct moxasd_host *host=(struct moxasd_host *)param;
429 struct mmc_data *data;
432 if ( !moxasd_check_fifo_ready(host) && host->data ) {
433 host->size = host->data->bytes_xfered;
434 host->data->error = MMC_ERR_TIMEOUT;
435 if ( host->dma && host->size > MSD_FIFO_LENB ) {
436 apb_dma_disable(host->dma);
439 writel(readl(&host->reg->status)&(MSD_FIFO_URUN|MSD_FIFO_ORUN), &host->reg->clear);
442 spin_lock(&host->lock);
443 #if 1 // add by Victor Yu. 03-19-2007
444 writel(readl(&host->reg->status)&(MSD_FIFO_URUN|MSD_FIFO_ORUN), &host->reg->clear);
446 if ( host->mrq == NULL ) {
447 spin_unlock(&host->lock);
450 #if 1 // never happened
451 if ( host->data == NULL ) {
452 goto moxasd_fifo_run_done;
456 #if 0 // add by Victor Yu. 03-19-2007
457 if ( data == NULL ) {
458 spin_unlock(&host->lock);
462 moxasd_do_fifo(host, data);
463 if ( host->size == data->bytes_xfered ) {
465 // maybe need to check the data is OK or fail
466 if ( data->error == MMC_ERR_NONE ) {
467 moxasd_check_data_crc(host, data);
471 moxasd_send_command(host, data->stop);
474 spin_unlock(&host->lock);
475 //tasklet_schedule(&host->fifo_run_tasklet);
479 moxasd_fifo_run_done:
480 moxasd_request_done(host);
481 spin_unlock(&host->lock);
484 static void moxasd_card_change(unsigned long param)
486 struct moxasd_host *host=(struct moxasd_host *)param;
490 spin_lock(&host->lock);
491 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
497 #endif // LINUX_VERSION_CODE
498 status = readl(&host->reg->status);
499 if ( status & MSD_CARD_DETECT ) { // card removed
500 printk("Moxa CPU SD/MMC card is removed.\n");
503 if ( host->dma && host->size > MSD_FIFO_LENB )
504 apb_dma_disable(host->dma);
505 host->size = host->data->bytes_xfered;
506 spin_unlock(&host->lock);
507 moxasd_fifo_run(*(unsigned long *)host);
508 spin_lock(&host->lock);
510 } else { // card inserted
511 printk("Moxa CPU SD/MMC card is inserted.\n");
512 #if 0 // mask by Victor Yu. 03-16-2007
513 if ( readl(&host->reg->clock_control) & MSD_CLK_SD ) { // SD
514 host->mmc->f_max = 25000000;
515 host->mmc->mode = MMC_MODE_SD;
517 host->mmc->f_max = 20000000;
518 host->mmc->mode = MMC_MODE_MMC;
523 writel(MSD_CLR_CARD_CHANGE, &host->reg->clear);
524 spin_unlock(&host->lock);
525 mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
528 static void moxasd_dma_irq(void *param)
530 struct moxasd_host *host=(struct moxasd_host *)param;
532 #if 0 // mask by Victor Yu. 03-19-2007
534 struct mmc_data *data=host->data;
535 if ( host->dma->error_flag ) {
536 host->size = data->bytes_xfered;
537 data->error = MMC_ERR_TIMEOUT;
540 tasklet_schedule(&host->fifo_run_tasklet);
544 tasklet_schedule(&host->fifo_run_tasklet);
548 static void moxasd_request(struct mmc_host *mmc, struct mmc_request *mrq)
550 struct moxasd_host *host=mmc_priv(mmc);
551 struct mmc_command *cmd;
553 spin_lock(&host->lock);
557 // if no card inserted, return timeout error
558 if ( readl(&host->reg->status) & MSD_CARD_DETECT ) { // card is removed
559 cmd->error = MMC_ERR_TIMEOUT;
563 // request include data or not
565 moxasd_prepare_data(host, cmd->data);
568 // do request command
569 moxasd_send_command(host, cmd);
571 if ( cmd->data && cmd->error == MMC_ERR_NONE ) {
572 spin_unlock(&host->lock);
577 moxasd_request_done(host);
578 spin_unlock(&host->lock);
581 #define MIN_POWER (MMC_VDD_360 - MSD_SD_POWER_MASK)
582 static void moxasd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
584 struct moxasd_host *host=mmc_priv(mmc);
586 spin_lock(&host->lock);
589 #ifdef MSD_SUPPORT_GET_CLOCK
590 div = (host->sysclk / (host->mmc->f_max * 2)) - 1;
592 div = (APB_CLK / (host->mmc->f_max * 2)) - 1;
594 if ( div > MSD_CLK_DIV_MASK )
595 div = MSD_CLK_DIV_MASK;
598 if ( host->mmc->mode == MMC_MODE_SD )
600 writel(div, &host->reg->clock_control);
601 } else if ( !(readl(&host->reg->clock_control) & MSD_CLK_DIS) ) {
603 * Ensure that the clock is off.
605 writel(readl(&host->reg->clock_control)|MSD_CLK_DIS, &host->reg->clock_control);
608 if ( ios->power_mode == MMC_POWER_OFF ) {
609 writel(readl(&host->reg->power_control)&~MSD_SD_POWER_ON, &host->reg->power_control);
611 unsigned short power;
612 if ( ios->vdd < MIN_POWER )
615 power = ios->vdd - MIN_POWER;
616 writel(MSD_SD_POWER_ON|(unsigned int)power, &host->reg->power_control);
620 if ( ios->bus_width == MMC_BUS_WIDTH_1 ) {
621 writel(MSD_SINGLE_BUS, &host->reg->bus_width);
623 writel(MSD_WIDE_BUS, &host->reg->bus_width);
626 spin_unlock(&host->lock);
630 * To check write protect or not. Return 0 for none, 1 for write protect.
632 static int moxasd_get_ro(struct mmc_host *mmc)
634 struct moxasd_host *host=mmc_priv(mmc);
636 if ( readl(&host->reg->status) & MSD_WRITE_PROT )
642 static struct mmc_host_ops moxasd_ops = {
643 .request = moxasd_request,
644 .set_ios = moxasd_set_ios,
645 .get_ro = moxasd_get_ro,
648 static int moxasd_probe(struct device *dev)
650 struct mmc_host *mmc;
651 struct moxasd_host *host=NULL;
654 mmc = mmc_alloc_host(sizeof(struct moxasd_host), dev);
660 mmc->ops = &moxasd_ops;
662 mmc->f_max = 25000000;
663 mmc->mode = MMC_MODE_SD;
665 mmc->ocr_avail = 0xffff00; // support 2.0v - 3.6v power
667 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
668 mmc->caps = MMC_CAP_4_BIT_DATA;
669 mmc->max_hw_segs = 128;
670 mmc->max_phys_segs = 128;
671 mmc->max_sectors = 128;
672 mmc->max_seg_size = mmc->max_sectors * 512;
675 host = mmc_priv(mmc);
677 spin_lock_init(&host->lock);
678 tasklet_init(&host->card_change_tasklet, moxasd_card_change, (unsigned long)host);
679 tasklet_init(&host->fifo_run_tasklet, moxasd_fifo_run, (unsigned long)host);
680 host->reg = (moxasd_reg *)CPE_SD_BASE;
681 host->dma = apb_dma_alloc(APB_DMA_SD_REQ_NO);
683 apb_dma_set_irq(host->dma, moxasd_dma_irq, host);
686 #ifdef MSD_SUPPORT_GET_CLOCK
689 unsigned int mul, val, div;
690 mul = (*(volatile unsigned int *)(CPE_PMU_BASE+0x30) >> 3) & 0x1ff;
691 val = (*(volatile unsigned int *)(CPE_PMU_BASE+0x0c) >> 4) & 0x7;
693 case 0 : div = 2; break;
694 case 1 : div = 3; break;
695 case 2 : div = 4; break;
696 case 3 : div = 6; break;
697 case 4 : div = 8; break;
698 default : div = 2; break;
700 host->sysclk = (38684*mul + 10000) / (div * 10000);
701 host->sysclk = (host->sysclk * 1000000) / 2;
705 // change I/O multiplexing to SD, so the GPIO 17-10 will be fail
706 *(volatile unsigned int *)(CPE_PMU_BASE+0x100) &= (~(0xff<<10));
709 * Ensure that the host controller is shut down, and setup
712 writel(0, &host->reg->interrupt_mask); // disable all interrupt
713 writel(MSD_SDC_RST, &host->reg->command); // reset chip
714 while ( readl(&host->reg->command) & MSD_SDC_RST); // wait for reset finished
715 writel(0, &host->reg->interrupt_mask); // disable all interrupt
717 // to check any card inserted or not
719 if ( !(readl(&host->reg->status) & MSD_CARD_DETECT) ) { // is inserted
720 if ( readl(&host->reg->clock_control) & MSD_CLK_SD ) { // is SD card
721 mmc->f_max = 25000000;
722 mmc->mode = MMC_MODE_SD;
723 } else { // is MMC card
724 mmc->f_max = 20000000;
725 mmc->mode = MMC_MODE_MMC;
730 mmc->caps = MMC_CAP_4_BIT_DATA;
731 writel(MSD_WIDE_BUS, &host->reg->bus_width);
733 cpe_int_set_irq(IRQ_SD, EDGE, H_ACTIVE);
734 ret = request_irq(IRQ_SD, moxasd_irq, SA_INTERRUPT, "MOXASD", host);
738 //writel(MSD_INT_CARD_CHANGE|MSD_INT_FIFO_ORUN|MSD_INT_FIFO_URUN, &host->reg->interrupt_mask);
739 writel(MSD_INT_CARD_CHANGE, &host->reg->interrupt_mask);
740 dev_set_drvdata(dev, mmc);
752 static int moxasd_remove(struct device *dev)
754 struct mmc_host *mmc=dev_get_drvdata(dev);
756 dev_set_drvdata(dev, NULL);
759 struct moxasd_host *host=mmc_priv(mmc);
761 mmc_remove_host(mmc);
765 apb_dma_disable(host->dma);
766 apb_dma_release_irq(host->dma);
767 apb_dma_release(host->dma);
769 writel(0, &host->reg->interrupt_mask);
770 writel(0, &host->reg->power_control);
771 writel(readl(&host->reg->clock_control)|MSD_CLK_DIS, &host->reg->clock_control);
773 free_irq(IRQ_SD, host);
774 tasklet_kill(&host->card_change_tasklet);
775 tasklet_kill(&host->fifo_run_tasklet);
782 static struct platform_device moxasd_device = {
787 static struct device_driver moxasd_driver = {
789 .bus = &platform_bus_type,
790 .probe = moxasd_probe,
791 .remove = moxasd_remove,
794 #if 1 // add by Victor Yu. 03-08-2007
795 extern int moxa_gpio_sd_used_flag; // define on arch/arm/kernel/armksyms.c
797 static int __init moxasd_init(void)
801 printk("Moxa CPU SD/MMC Device Driver V1.0 initialize ");
802 #if 1 // add by Victor Yu. 03-08-2007
805 local_irq_save(flags);
806 if ( moxa_gpio_sd_used_flag ) {
807 printk("The IO has used by other device driver !\n");
808 local_irq_restore(flags);
811 moxa_gpio_sd_used_flag = 1;
812 local_irq_restore(flags);
815 platform_device_register(&moxasd_device);
816 ret = driver_register(&moxasd_driver);
818 printk("Modules load fail !\n");
819 platform_device_unregister(&moxasd_device);
821 printk("Modules load OK.\n");
826 static void __exit moxasd_exit(void)
828 platform_device_unregister(&moxasd_device);
829 driver_unregister(&moxasd_driver);
832 module_init(moxasd_init);
833 module_exit(moxasd_exit);
835 MODULE_DESCRIPTION("Moxa CPU SD/Multimedia Card Interface Driver");
836 MODULE_LICENSE("GPL");