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>
39 #include <asm/arch/gpio.h>
43 #if 0 // mask by Victor Yu. 03-19-2007
44 #define MSD_RETRY_COUNT 1000
46 #define MSD_RETRY_COUNT 100
48 //#define CONFIG_MMC_DEBUG
49 #ifdef CONFIG_MMC_DEBUG
50 #define DBG(x...) printk(x)
60 #ifdef MSD_SUPPORT_GET_CLOCK
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
;
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
;
99 * Skip to next SG entry.
107 if (host
->num_sg
> 0) {
108 host
->remain
= host
->cur_sg
->length
;
109 #if 1 // add by Victor Yu. 07-04-2007
112 remain
= host
->size
- data
->bytes_xfered
;
113 if ( remain
> 0 && remain
< host
->remain
) {
114 host
->remain
= remain
;
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
)
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
);
140 if ( host
->size
== data
->bytes_xfered
) {
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
;
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
, ¶m
);
166 apb_dma_enable(host
->dma
);
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
);
173 for ( i
=0; i
<wcnt
; i
++, buffer
+=4 )
174 *(unsigned int *)buffer
= readl(&host
->reg
->data_window
);
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
;
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
199 #endif // LINUX_VERSION_CODE
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
);
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);
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
;
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);
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
;
246 #if 1 // add by Victor Yu. 03-19-2007
247 cmd
->error
= MMC_ERR_TIMEOUT
;
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
);
254 writel(cmd
->arg
, &host
->reg
->argument
);
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
);
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
;
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
;
282 if ( status
& MSD_RSP_CRC_FAIL
) {
284 if ( (cmd
->flags
&MMC_RSP_CRC
) && (status
&MSD_RSP_CRC_FAIL
) ) {
286 writel(MSD_CLR_RSP_CRC_FAIL
, &host
->reg
->clear
);
287 cmd
->error
= MMC_ERR_BADCRC
;
290 if ( status
& MSD_RSP_CRC_OK
) {
291 writel(MSD_CLR_RSP_CRC_OK
, &host
->reg
->clear
);
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
;
301 if ( status
& MSD_CMD_SENT
) {
302 writel(MSD_CLR_CMD_SENT
, &host
->reg
->clear
);
303 cmd
->error
= MMC_ERR_NONE
;
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
)
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
;
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
);
336 static void moxasd_fifo_run(unsigned long param
)
338 struct moxasd_host
*host
=(struct moxasd_host
*)param
;
339 struct mmc_data
*data
;
342 spin_lock_irqsave(&host
->lock
, flags
);
343 host
= (struct moxasd_host
*)param
;
345 if ( host
->mrq
== NULL
|| data
== NULL
) {
346 spin_unlock_irqrestore(&host
->lock
, flags
);
349 moxasd_do_fifo(host
, data
);
350 if ( host
->size
== data
->bytes_xfered
) {
351 #if 1 // mask by Victor Yu. 07-04-2007
354 status
= readl(&host
->reg
->status
);
355 if ( status
& (MSD_DATA_CRC_OK
|MSD_DATA_CRC_FAIL
|MSD_DATA_END
) )
357 current
->state
= TASK_INTERRUPTIBLE
;
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
);
372 moxasd_send_command(host
, data
->stop
);
375 spin_unlock_irqrestore(&host
->lock
, flags
);
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
;
390 spin_lock_irqsave(&host
->lock
, flags
);
391 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) // add by Victor Yu. 03-07-2007
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");
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
;
416 host
->mmc
->f_max
= 20000000;
417 host
->mmc
->mode
= MMC_MODE_MMC
;
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
;
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
;
440 spin_lock_irqsave(&host
->lock
, flags
);
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
;
450 // request include data or not
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
);
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
);
474 spin_lock_irqsave(&host
->lock
, flags
);
477 #ifdef MSD_SUPPORT_GET_CLOCK
478 div
= (host
->sysclk
/ (host
->mmc
->f_max
* 2)) - 1;
480 div
= (APB_CLK
/ (host
->mmc
->f_max
* 2)) - 1;
482 if ( div
> MSD_CLK_DIV_MASK
)
483 div
= MSD_CLK_DIV_MASK
;
486 if ( host
->mmc
->mode
== MMC_MODE_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
);
499 unsigned short power
;
500 if ( ios
->vdd
< MIN_POWER
)
503 power
= ios
->vdd
- MIN_POWER
;
504 writel(MSD_SD_POWER_ON
|(unsigned int)power
, &host
->reg
->power_control
);
508 if ( ios
->bus_width
== MMC_BUS_WIDTH_1
) {
509 writel(MSD_SINGLE_BUS
, &host
->reg
->bus_width
);
511 writel(MSD_WIDE_BUS
, &host
->reg
->bus_width
);
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
)
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
;
542 mmc
= mmc_alloc_host(sizeof(struct moxasd_host
), dev
);
548 mmc
->ops
= &moxasd_ops
;
550 mmc
->f_max
= 25000000;
551 mmc
->mode
= MMC_MODE_SD
;
553 mmc
->ocr_avail
= 0xffff00; // support 2.0v - 3.6v power
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;
563 host
= mmc_priv(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
);
571 apb_dma_set_irq(host
->dma
, moxasd_dma_irq
, host
);
574 #ifdef MSD_SUPPORT_GET_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;
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;
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
);
603 * Ensure that the host controller is shut down, and setup
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
);
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
);
644 static int moxasd_remove(struct device
*dev
)
646 struct mmc_host
*mmc
=dev_get_drvdata(dev
);
648 dev_set_drvdata(dev
, NULL
);
651 struct moxasd_host
*host
=mmc_priv(mmc
);
653 mmc_remove_host(mmc
);
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
);
674 static struct platform_device moxasd_device
= {
679 static struct device_driver moxasd_driver
= {
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
689 static int __init
moxasd_init(void)
693 printk("Moxa CPU SD/MMC Device Driver V1.0 initialize ");
694 #if 1 // add by Victor Yu. 03-08-2007
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
);
703 moxa_gpio_sd_used_flag
= 1;
704 local_irq_restore(flags
);
707 platform_device_register(&moxasd_device
);
708 ret
= driver_register(&moxasd_driver
);
710 printk("Modules load fail !\n");
711 platform_device_unregister(&moxasd_device
);
713 printk("Modules load OK.\n");
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
725 local_irq_save(flags
);
726 moxa_gpio_sd_used_flag
= 0;
727 local_irq_restore(flags
);
732 module_init(moxasd_init
);
733 module_exit(moxasd_exit
);
735 MODULE_DESCRIPTION("Moxa CPU SD/Multimedia Card Interface Driver");
736 MODULE_LICENSE("GPL");