S3C2410中SD卡驱动的移植

 S3C2410中SD卡驱动的移植收藏

S3C2410中SD卡驱动的移植

 

内核版本:2.6.30.4

 

1.  添加s3cmci..c和s3cmci.h到drivers/mmc目录下(这两个文件在drivers/mmc/host下已存在,若编译内核时出错就把这两个文件拷贝到drivers/mmc下,而且s3cmci.c是基于s3c系列的,要将关于s3c2412和s3c2440的注释掉,否则会出现

 

Error:Driver 's3c2410-sdi' is already registered,aborting...

 

Error:Driver 's3c2412-sdi' is already registered,aborting...

 

Error:Driver 's3c2440-sdi' is already registered,aborting...

 

但是这样只可以改掉后两条错误,第一条仍然存在),添加regs-sdi.h、mmc_debug.h到include/asm-arm/中(

 

添加的regs-sdi.h的内容如下:

 

/* linux/include/asm/arch-s3c2410/regs-sdi.h

 

 *

 

 * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>

 

 *       http://www.simtec.co.uk/products/SWLINUX/

 

 *

 

 * This program is free software; you can redistribute it and/or modify

 

 * it under the terms of the GNU General Public License version 2 as

 

 * published by the Free Software Foundation.

 

 *

 

 * S3C2410 MMC/SDIO register definitions

 

 *

 

 *  Changelog:

 

 *    18-Aug-2004 Ben Dooks      Created initial file

 

 *    29-Nov-2004 Koen Martens   Added some missing defines, fixed duplicates

 

 *    29-Nov-2004 Ben Dooks  Updated Koen's patch

 

*/

 

 

#ifndef __ASM_ARM_REGS_SDI

 

#define __ASM_ARM_REGS_SDI "regs-sdi.h"

 

 

#define S3C2410_SDICON                (0x00)

 

#define S3C2410_SDIPRE                (0x04)

 

#define S3C2410_SDICMDARG             (0x08)

 

#define S3C2410_SDICMDCON             (0x0C)

 

#define S3C2410_SDICMDSTAT            (0x10)

 

#define S3C2410_SDIRSP0               (0x14)

 

#define S3C2410_SDIRSP1               (0x18)

 

#define S3C2410_SDIRSP2               (0x1C)

 

#define S3C2410_SDIRSP3               (0x20)

 

#define S3C2410_SDITIMER              (0x24)

 

#define S3C2410_SDIBSIZE              (0x28)

 

#define S3C2410_SDIDCON               (0x2C)

 

#define S3C2410_SDIDCNT               (0x30)

 

#define S3C2410_SDIDSTA               (0x34)

 

#define S3C2410_SDIFSTA               (0x38)

 

 

//#ifdef CONFIG_CPU_S3C2440

 

#define S3C2440_SDIDATA               (0x40)

 

#define S3C2440_SDIIMSK               (0x3c)

 

#define S3C2440_SDICON_SDRESET   (1<<8)

 

#define S3C2440_SDICON_MMCCLOCK   (1<<5)

 

#define S3C2440_SDIDCON_DS_BYTE (0<<22)

 

#define S3C2440_SDIDCON_DS_HALFWORD (1<<22)

 

#define S3C2440_SDIDCON_DS_WORD (2<<22)

 

#define S3C2440_SDIDCON_DATSTART (1<<14)

 

#define S3C2440_SDIFSTA_FIFORESET (1<<16)

 

#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */

 

//#else

 

 

#define S3C2410_SDIDATA               (0x3C)

 

#define S3C2410_SDIIMSK               (0x40)

 

//#endif

 

 

#define S3C2410_SDICON_BYTEORDER      (1<<4)

 

#define S3C2410_SDICON_SDIOIRQ        (1<<3)

 

#define S3C2410_SDICON_RWAITEN        (1<<2)

 

#define S3C2410_SDICON_FIFORESET      (1<<1)

 

#define S3C2410_SDICON_CLOCKTYPE      (1<<0)

 

 

#define S3C2410_SDICMDCON_ABORT       (1<<12)

 

#define S3C2410_SDICMDCON_WITHDATA    (1<<11)

 

#define S3C2410_SDICMDCON_LONGRSP     (1<<10)

 

#define S3C2410_SDICMDCON_WAITRSP     (1<<9)

 

#define S3C2410_SDICMDCON_CMDSTART    (1<<8)

 

#define S3C2410_SDICMDCON_SENDERHOST  (1<<6)

 

//#define S3C2410_SDICMDCON_INDEX       (0xff)

 

#define S3C2410_SDICMDCON_INDEX       (0x3f)

 

 

#define S3C2410_SDICMDSTAT_CRCFAIL    (1<<12)

 

#define S3C2410_SDICMDSTAT_CMDSENT    (1<<11)

 

#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1<<10)

 

#define S3C2410_SDICMDSTAT_RSPFIN     (1<<9)

 

#define S3C2410_SDICMDSTAT_XFERING    (1<<8)

 

#define S3C2410_SDICMDSTAT_INDEX      (0xff)

 

 

#define S3C2410_SDIDCON_IRQPERIOD     (1<<21)

 

#define S3C2410_SDIDCON_TXAFTERRESP   (1<<20)

 

#define S3C2410_SDIDCON_RXAFTERCMD    (1<<19)

 

#define S3C2410_SDIDCON_BUSYAFTERCMD  (1<<18)

 

#define S3C2410_SDIDCON_BLOCKMODE     (1<<17)

 

#define S3C2410_SDIDCON_WIDEBUS       (1<<16)

 

#define S3C2410_SDIDCON_DMAEN         (1<<15)

 

#define S3C2410_SDIDCON_STOP          (1<<14)

 

#define S3C2410_SDIDCON_DATMODE       (3<<12)

 

#define S3C2410_SDIDCON_BLKNUM        (0x7ff)

 

 

/* constants for S3C2410_SDIDCON_DATMODE */

 

#define S3C2410_SDIDCON_XFER_READY    (0<<12)

 

#define S3C2410_SDIDCON_XFER_CHKSTART (1<<12)

 

#define S3C2410_SDIDCON_XFER_RXSTART  (2<<12)

 

#define S3C2410_SDIDCON_XFER_TXSTART  (3<<12)

 

 

#define S3C2410_SDIDCON_BLKNUM_MASK   (0xFFF)

 

#define S3C2410_SDIDCNT_BLKNUM_SHIFT  (12)

 

#define S3C2410_SDIDCNT_BLKNUM  (0x7ff)

 

 

#define S3C2410_SDIDSTA_RDYWAITREQ    (1<<10)

 

#define S3C2410_SDIDSTA_SDIOIRQDETECT (1<<9)

 

#define S3C2410_SDIDSTA_FIFOFAIL      (1<<8) /* reserved on 2440 */

 

#define S3C2410_SDIDSTA_CRCFAIL       (1<<7)

 

#define S3C2410_SDIDSTA_RXCRCFAIL     (1<<6)

 

#define S3C2410_SDIDSTA_DATATIMEOUT   (1<<5)

 

#define S3C2410_SDIDSTA_XFERFINISH    (1<<4)

 

#define S3C2410_SDIDSTA_BUSYFINISH    (1<<3)

 

#define S3C2410_SDIDSTA_SBITERR       (1<<2) /* reserved on 2410a/2440 */

 

#define S3C2410_SDIDSTA_TXDATAON      (1<<1)

 

#define S3C2410_SDIDSTA_RXDATAON      (1<<0)

 

 

#define S3C2410_SDIFSTA_TFDET          (1<<13)

 

#define S3C2410_SDIFSTA_RFDET          (1<<12)

 

//#define S3C2410_SDIFSTA_TXHALF         (1<<11)

 

//#define S3C2410_SDIFSTA_TXEMPTY        (1<<10)

 

#define S3C2410_SDIFSTA_TFHALF         (1<<11)

 

#define S3C2410_SDIFSTA_TFEMPTY        (1<<10)

 

#define S3C2410_SDIFSTA_RFLAST         (1<<9)

 

#define S3C2410_SDIFSTA_RFFULL         (1<<8)

 

#define S3C2410_SDIFSTA_RFHALF         (1<<7)

 

#define S3C2410_SDIFSTA_COUNTMASK      (0x7f)

 

 

#define S3C2410_SDIIMSK_RESPONSECRC    (1<<17)

 

#define S3C2410_SDIIMSK_CMDSENT        (1<<16)

 

#define S3C2410_SDIIMSK_CMDTIMEOUT     (1<<15)

 

#define S3C2410_SDIIMSK_RESPONSEND     (1<<14)

 

#define S3C2410_SDIIMSK_READWAIT       (1<<13)

 

#define S3C2410_SDIIMSK_SDIOIRQ        (1<<12)

 

#define S3C2410_SDIIMSK_FIFOFAIL       (1<<11)

 

#define S3C2410_SDIIMSK_CRCSTATUS      (1<<10)

 

#define S3C2410_SDIIMSK_DATACRC        (1<<9)

 

#define S3C2410_SDIIMSK_DATATIMEOUT    (1<<8)

 

#define S3C2410_SDIIMSK_DATAFINISH     (1<<7)

 

#define S3C2410_SDIIMSK_BUSYFINISH     (1<<6)

 

#define S3C2410_SDIIMSK_SBITERR        (1<<5) /* reserved 2440/2410a */

 

#define S3C2410_SDIIMSK_TXFIFOHALF     (1<<4)

 

#define S3C2410_SDIIMSK_TXFIFOEMPTY    (1<<3)

 

#define S3C2410_SDIIMSK_RXFIFOLAST     (1<<2)

 

#define S3C2410_SDIIMSK_RXFIFOFULL     (1<<1)

 

#define S3C2410_SDIIMSK_RXFIFOHALF     (1<<0)

 

 

#endif /* __ASM_ARM_REGS_SDI */

 

添加的mmc_debug.h的内容如下:

 

#ifndef MMC_DEBUG_H

 

#define MMC_DEBUG_H

 

 

char *mmc_cmd2str(int err);

 

char *mmc_err2str(int err);

 

 

#endif /* MMC_DEBUG_H */ )。

 

2.   删除s3c2410mci.c中以下几句:(在2.6.30.4内核中已存在的s3cmci.c中没有下面的这段代码,故不用改动即可)

 

if(host->mrq->cmd->flags & MMC_RSP_CRC) {

host->mrq->cmd->error = MMC_ERR_BADCRC;

goto transfer_closed;

}

 

    3.  在arch/arm/mach-s3c2410/mach-smdk2410.c platform结构中增加&s3c_device_sdi语句,同时要添加头文件#include <asm-arm/regs-sdi.h>。

 

static struct platform_device *smdk2410_devices[] __initdata = {

 &s3c_device_usb,

 &s3c_device_lcd,

 &s3c_device_wdt,

 &s3c_device_i2c,

 &s3c_device_iis,

 &s3c_device_nand,

 &s3c_device_sdi,//add here

};

 

   4. 修改drivers/mmc下的Kconfig和Makefile。

 

在Kconfig中添加:

 

  menu "MMC/SD Card support"

 

config SD    //add here

 

      tristate "SD support"

 

      help

 

config MMC

  tristate "MMC support"

  help

    MMC is the "multi-media card" bus protocol.

 

    If you want MMC support, you should say Y here and also to the specific driver for your MMC interface.

 

endmenu

 

在Makefile中添加:

 

obj-$(CONFIG_MMC_ARMMMCI)   += mmci.o

obj-$(CONFIG_MMC_PXA)         += pxamci.o

obj-$(CONFIG_MMC_WBSD)       += wbsd.o

obj-$(CONFIG_SD)                +=s3cmci.o

 

5.         添加内核的支持

 

Make menuconfig,将内核中关于MMC/SD的选项都选上,

 

最后make zImage

 

通常的驱动还不支持热插拔,因此在内核启动前先将sd卡插上。移植成功后,在启动信息中会显示:

 

mmc0: new SD card at address 8fe4

 

mmcblk0: mmc0:8fe4 SD02G 1.84 GiB

 

mmcblk0:

 

eth0: link up, 100Mbps, full-duplex, lpa 0xC5E1

 

如果使用的SD卡是没有被格式化的,就会出现如下信息:

 

mmc0: new SD card at address 8fe4

 

mmcblk0: mmc0:8fe4 SD02G 1.84 GiB

 

 mmcblk0: unknown partition table

 

eth0: link up, 100Mbps, full-duplex, lpa 0xC5E1

 

可以通过用读卡器将SD卡格式化成FAT32格式的,然后对其进行分区,不分区也可以,默认为只有一个区

 

移植成功后,挂载一个文件系统,在文件系统下挂载SD卡

 

Mount -t  vfat /dev/mmcblk0 /mnt

 

若挂载成功,就可以在/mnt下看到SD卡的内容

 

 

Cat /proc/partitions

 

查看分区信息

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/MHW2006/archive/2009/12/09/4973572.aspx

 

 

 

 

 

 

 

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

6.14下移植2440的SD卡驱动    

 

 

 

目前sd卡驱动已经能够正常发送命令及接收回应。但是数据传输好象有问题,一到DMA数据传输那部分系统就挂在那里了。请问有什么好的办法调试?

附SD卡的驱动源码:

s3c2410mci.c

/*

* linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver

*

* Copyright (C) 2004 Thomas Kleffel, All Rights Reserved.

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License version 2 as

* published by the Free Software Foundation.

*/

#include <linux/config.h>

#include <linux/module.h>

#include <linux/moduleparam.h>

#include <linux/init.h>

#include <linux/ioport.h>

#include <linux/device.h>

#include <linux/interrupt.h>

#include <linux/blkdev.h>

#include <linux/delay.h>

#include <linux/err.h>

#include <linux/dma-mapping.h>

#include <linux/mmc/host.h>

#include <linux/mmc/protocol.h>

//#include <linux/clk.h>

 

#include <asm/dma.h>

#include <asm/dma-mapping.h>

#include <asm/arch/dma.h>

 

#include <asm/io.h>

#include <asm/irq.h>

#include <asm/hardware/clock.h>

#include <asm/mach/mmc.h>

 

#include <asm/arch/regs-sdi.h>

#include <asm/arch/regs-gpio.h>

#include <asm/arch/mmc.h>

#include <asm/arch/regs-clock.h>

 

//#define S3C2410SDI_DMA_BACKBUF

 

#ifdef CONFIG_MMC_DEBUG

#define DBG(x...) printk(KERN_INFO x)

#else

#define DBG(x...) do { } while (0)

#endif

 

#include "s3c2410mci.h"

 

#define DRIVER_NAME "mmci-s3c2410"

#define PFX DRIVER_NAME ": "

 

#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)

 

 

static struct s3c2410_dma_client s3c2410sdi_dma_client = {

.name = "s3c2410-sdi",

};

 

 

 

/*

* ISR for SDI Interface IRQ

* Communication between driver and ISR works as follows:

* host->mrq points to current request

* host->complete_what tells the ISR when the request is considered done

* COMPLETION_CMDSENT when the command was sent

* COMPLETION_RSPFIN when a response was received

* COMPLETION_XFERFINISH when the data transfer is finished

* COMPLETION_XFERFINISH_RSPFIN both of the above.

* host->complete_request is the completion-object the driver waits for

*

* 1) Driver sets up host->mrq and host->complete_what

* 2) Driver prepares the transfer

* 3) Driver enables interrupts

* 4) Driver starts transfer

* 5) Driver waits for host->complete_rquest

* 6) ISR checks for request status (errors and success)

* 6) ISR sets host->mrq->cmd->error and host->mrq->data->error

* 7) ISR completes host->complete_request

* 8) ISR disables interrupts

* 9) Driver wakes up and takes care of the request

*/

 

static irqreturn_t s3c2410sdi_irq(int irq, void *dev_id, struct pt_regs *regs)

{

struct s3c2410sdi_host *host;

u32 sdi_csta, sdi_dsta, sdi_dcnt;

u32 sdi_cclear, sdi_dclear;

unsigned long iflags;

 

host = (struct s3c2410sdi_host *)dev_id;

 

//Check for things not supposed to happen

if(!host) return IRQ_HANDLED;

 

sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);

sdi_dsta = readl(host->base + S3C2410_SDIDSTA);

sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);

 

DBG(PFX "IRQ csta=0x%08x dsta=0x%08x dcnt:0x%08x/n", sdi_csta, sdi_dsta, sdi_dcnt);

 

spin_lock_irqsave( &host->complete_lock, iflags);

 

if( host->complete_what==COMPLETION_NONE ) {

goto clear_imask;

}

 

if(!host->mrq) {

goto clear_imask;

}

 

 

sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);

sdi_dsta = readl(host->base + S3C2410_SDIDSTA);

sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);

sdi_cclear = 0;

sdi_dclear = 0;

 

 

if(sdi_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {

host->mrq->cmd->error = MMC_ERR_TIMEOUT;

goto transfer_closed;

}

 

if(sdi_csta & S3C2410_SDICMDSTAT_CMDSENT) {

if(host->complete_what == COMPLETION_CMDSENT) {

host->mrq->cmd->error = MMC_ERR_NONE;

goto transfer_closed;

}

 

sdi_cclear |= S3C2410_SDICMDSTAT_CMDSENT;

}

 

 

 

if(sdi_csta & S3C2410_SDICMDSTAT_CRCFAIL) {

if (host->mrq->cmd->flags & MMC_RSP_LONG) {

DBG(PFX "s3c2410 fixup : ignore CRC fail with long rsp/n");

}

else {

DBG(PFX "COMMAND CRC FAILED %x/n", sdi_csta);

if(host->mrq->cmd->flags & MMC_RSP_CRC) {

host->mrq->cmd->error = MMC_ERR_BADCRC;

goto transfer_closed;

}

}

 

if(host->mrq->cmd->flags & MMC_RSP_CRC) {

host->mrq->cmd->error = MMC_ERR_BADCRC;

goto transfer_closed;

}

 

sdi_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;

}

 

if(sdi_csta & S3C2410_SDICMDSTAT_RSPFIN) {

if(host->complete_what == COMPLETION_RSPFIN) {

host->mrq->cmd->error = MMC_ERR_NONE;

goto transfer_closed;

}

 

if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {

host->mrq->cmd->error = MMC_ERR_NONE;

host->complete_what = COMPLETION_XFERFINISH;

}

 

sdi_cclear |= S3C2410_SDICMDSTAT_RSPFIN;

}

 

if(sdi_dsta & S3C2410_SDIDSTA_FIFOFAIL) {

host->mrq->cmd->error = MMC_ERR_NONE;

host->mrq->data->error = MMC_ERR_FIFO;

goto transfer_closed;

}

 

if(sdi_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {

host->mrq->cmd->error = MMC_ERR_NONE;

host->mrq->data->error = MMC_ERR_BADCRC;

goto transfer_closed;

}

 

if(sdi_dsta & S3C2410_SDIDSTA_CRCFAIL) {

host->mrq->cmd->error = MMC_ERR_NONE;

host->mrq->data->error = MMC_ERR_BADCRC;

goto transfer_closed;

}

 

if(sdi_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {

host->mrq->cmd->error = MMC_ERR_NONE;

host->mrq->data->error = MMC_ERR_TIMEOUT;

goto transfer_closed;

}

 

if(sdi_dsta & S3C2410_SDIDSTA_XFERFINISH) {

if(host->complete_what == COMPLETION_XFERFINISH) {

host->mrq->cmd->error = MMC_ERR_NONE;

host->mrq->data->error = MMC_ERR_NONE;

goto transfer_closed;

}

 

if(host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {

host->mrq->data->error = MMC_ERR_NONE;

host->complete_what = COMPLETION_RSPFIN;

}

 

sdi_dclear |= S3C2410_SDIDSTA_XFERFINISH;

}

 

writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT);

writel(sdi_dclear, host->base + S3C2410_SDIDSTA);

 

spin_unlock_irqrestore( &host->complete_lock, iflags);

DBG(PFX "IRQ still waiting./n");

return IRQ_HANDLED;

 

 

transfer_closed:

writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT);

writel(sdi_dclear, host->base + S3C2410_SDIDSTA);

host->complete_what = COMPLETION_NONE;

complete(&host->complete_request);

writel(0, host->base + S3C2410_SDIIMSK);

spin_unlock_irqrestore( &host->complete_lock, iflags);

DBG(PFX "IRQ transfer closed./n");

return IRQ_HANDLED;

 

clear_imask:

writel(0, host->base + S3C2410_SDIIMSK);

spin_unlock_irqrestore( &host->complete_lock, iflags);

DBG(PFX "IRQ clear imask./n");

return IRQ_HANDLED;

 

}

 

 

/*

* ISR for the CardDetect Pin

*/

 

static irqreturn_t s3c2410sdi_irq_cd(int irq, void *dev_id, struct pt_regs *regs)

{

struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)dev_id;

printk("s3c2410sdi_irq_cd/n");

mmc_detect_change(host->mmc, S3C2410SDI_CDLATENCY);

 

 

return IRQ_HANDLED;

}

 

 

 

void s3c2410sdi_dma_done_callback(s3c2410_dma_chan_t *dma_ch, void *buf_id,

int size, s3c2410_dma_buffresult_t result)

{ unsigned long iflags;

u32 sdi_csta, sdi_dsta,sdi_dcnt;

struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)buf_id;

 

sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);

sdi_dsta = readl(host->base + S3C2410_SDIDSTA);

sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);

 

DBG(PFX "DMAD csta=0x%08x dsta=0x%08x dcnt:0x%08x result:0x%08x/n", sdi_csta, sdi_dsta, sdi_dcnt, result);

 

spin_lock_irqsave( &host->complete_lock, iflags);

 

if(!host->mrq) goto out;

if(!host->mrq->data) goto out;

 

 

sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);

sdi_dsta = readl(host->base + S3C2410_SDIDSTA);

sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);

 

if( result!=S3C2410_RES_OK ) {

goto fail_request;

}

 

 

if(host->mrq->data->flags & MMC_DATA_READ) {

if( sdi_dcnt>0 ) {

goto fail_request;

}

}

 

out:

complete(&host->complete_dma);

spin_unlock_irqrestore( &host->complete_lock, iflags);

return;

 

 

fail_request:

host->mrq->data->error = MMC_ERR_FAILED;

host->complete_what = COMPLETION_NONE;

complete(&host->complete_request);

writel(0, host->base + S3C2410_SDIIMSK);

goto out;

 

}

 

 

void s3c2410sdi_dma_setup(struct s3c2410sdi_host *host, s3c2410_dmasrc_t source) {

 

s3c2410_dma_devconfig(host->dma, source, 3, host->mem->start + S3C2410_SDIDATA);

s3c2410_dma_config(host->dma, 4, (1<<23) | (2<<24));

s3c2410_dma_set_buffdone_fn(host->dma, s3c2410sdi_dma_done_callback);

s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);

}

 

static void s3c2410sdi_request(struct mmc_host *mmc, struct mmc_request *mrq) {

struct s3c2410sdi_host *host = mmc_priv(mmc);

struct device *dev = mmc_dev(host->mmc);

struct platform_device *pdev = to_platform_device(dev);

u32 sdi_carg, sdi_ccon, sdi_timer;

u32 sdi_bsize, sdi_dcon, sdi_imsk;

int dma_len = 0;

 

 

DBG(KERN_DEBUG PFX "request: [CMD] opcode:0x%02d arg:0x%08x flags:%x retries:%u/n",

mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries);

 

DBG(PFX "request : %s mode/n",mmc->mode == MMC_MODE_MMC ? "mmc" : "sd");

 

sdi_ccon = mrq->cmd->opcode & S3C2410_SDICMDCON_INDEX;

sdi_ccon|= S3C2410_SDICMDCON_SENDERHOST;

sdi_ccon|= S3C2410_SDICMDCON_CMDSTART;

 

sdi_carg = mrq->cmd->arg;

 

//FIXME: Timer value ?!

sdi_timer= 0xF000;

 

sdi_bsize= 0;

sdi_dcon = 0;

sdi_imsk = 0;

 

//enable interrupts for transmission errors

sdi_imsk |= S3C2410_SDIIMSK_RESPONSEND;

sdi_imsk |= S3C2410_SDIIMSK_CRCSTATUS;

 

 

host->complete_what = COMPLETION_CMDSENT;

 

if (mrq->cmd->flags & MMC_RSP_MASK) {

host->complete_what = COMPLETION_RSPFIN;

 

sdi_ccon |= S3C2410_SDICMDCON_WAITRSP;

sdi_imsk |= S3C2410_SDIIMSK_CMDTIMEOUT;

 

} else {

//We need the CMDSENT-Interrupt only if we want are not waiting

//for a response

sdi_imsk |= S3C2410_SDIIMSK_CMDSENT;

}

 

if(mrq->cmd->flags & MMC_RSP_LONG) {

sdi_ccon|= S3C2410_SDICMDCON_LONGRSP;

}

 

if(mrq->cmd->flags & MMC_RSP_CRC) {

sdi_imsk |= S3C2410_SDIIMSK_RESPONSECRC;

}

 

 

if (mrq->data) {

host->complete_what = COMPLETION_XFERFINISH_RSPFIN;

 

 

 

sdi_bsize = (1 << mrq->data->blksz_bits);

 

sdi_dcon = (mrq->data->blocks & S3C2410_SDIDCON_BLKNUM_MASK);

sdi_dcon |= S3C2410_SDIDCON_DMAEN;

 

sdi_imsk |= S3C2410_SDIIMSK_FIFOFAIL;

sdi_imsk |= S3C2410_SDIIMSK_DATACRC;

sdi_imsk |= S3C2410_SDIIMSK_DATATIMEOUT;

sdi_imsk |= S3C2410_SDIIMSK_DATAFINISH;

sdi_imsk |= 0xFFFFFFE0;

 

DBG(PFX "request: [DAT] bsize:%u blocks:%u bytes:%u/n",

sdi_bsize, mrq->data->blocks, mrq->data->blocks * sdi_bsize);

 

if (host->bus_width == MMC_BUS_WIDTH_4) {

sdi_dcon |= S3C2410_SDIDCON_WIDEBUS;

}

 

if(!(mrq->data->flags & MMC_DATA_STREAM)) {

sdi_dcon |= S3C2410_SDIDCON_BLOCKMODE;

}

 

if(mrq->data->flags & MMC_DATA_WRITE) {

sdi_dcon |= S3C2410_SDIDCON_TXAFTERRESP;

sdi_dcon |= S3C2410_SDIDCON_XFER_TXSTART;

}

 

if(mrq->data->flags & MMC_DATA_READ) {

sdi_dcon |= S3C2410_SDIDCON_RXAFTERCMD;

sdi_dcon |= S3C2410_SDIDCON_XFER_RXSTART;

sdi_dcon |=S3C2410_SDIDCON_DAT_WD;

//sdi_dcon |=S3C2410_SDIDCON_BURST4;

}

 

s3c2410sdi_dma_setup(host, mrq->data->flags & MMC_DATA_WRITE ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);

 

/* see DMA-API.txt */

dma_len = dma_map_sg(&pdev->dev, mrq->data->sg, /

mrq->data->sg_len, /

mrq->data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);

DBG(PFX "dma_len: 0x%08x/n",dma_len);

 

/* start DMA */

s3c2410_dma_enqueue(host->dma, (void *) host,

sg_dma_address(&mrq->data->sg[0]),

(mrq->data->blocks << mrq->data->blksz_bits) );

 

}

 

host->mrq = mrq;

 

init_completion(&host->complete_request);

init_completion(&host->complete_dma);

 

//Clear command and data status registers

writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);

writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);

 

// Setup SDI controller

writel(sdi_bsize,host->base + S3C2410_SDIBSIZE);

writel(sdi_timer,host->base + S3C2410_SDITIMER);

writel(sdi_imsk,host->base + S3C2410_SDIIMSK);

printk("sdi_carg = %x/n",sdi_carg);

printk("sdi_dcon = %x/n",sdi_dcon);

printk("sdi_ccon = %x/n",sdi_ccon);

 

// Setup SDI command argument and data control

writel(sdi_carg, host->base + S3C2410_SDICMDARG);

writel(sdi_dcon, host->base + S3C2410_SDIDCON);

// This initiates transfer

writel(sdi_ccon, host->base + S3C2410_SDICMDCON);

 

// Wait for transfer to complete

 

wait_for_completion(&host->complete_request);

DBG("[CMD] request complete./n");

if(mrq->data) {

wait_for_completion(&host->complete_dma);

DBG("[DAT] DMA complete./n");

}

 

 

//Cleanup controller

writel(0, host->base + S3C2410_SDICMDARG);

writel(0, host->base + S3C2410_SDIDCON);

writel(0, host->base + S3C2410_SDICMDCON);

writel(0, host->base + S3C2410_SDIIMSK);

 

// Read response

mrq->cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);

mrq->cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);

mrq->cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);

mrq->cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);

 

host->mrq = NULL;

DBG(PFX "mrq->cmd->resp[0]: 0x%08x/n",mrq->cmd->resp[0]);

DBG(PFX "mrq->cmd->resp[1]: 0x%08x/n",mrq->cmd->resp[1]);

DBG(PFX "mrq->cmd->resp[2]: 0x%08x/n",mrq->cmd->resp[2]);

DBG(PFX "mrq->cmd->resp[3]: 0x%08x/n",mrq->cmd->resp[3]);

// If we have no data transfer we are finished here

if (!mrq->data) goto request_done;

 

// Calulate the amout of bytes transfer, but only if there was

// no error

 

dma_unmap_sg(&pdev->dev,mrq->data->sg,dma_len,/

mrq->data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);

 

if(mrq->data->error == MMC_ERR_NONE) {

mrq->data->bytes_xfered = (mrq->data->blocks << mrq->data->blksz_bits);

if(mrq->data->flags & MMC_DATA_READ);

} else {

mrq->data->bytes_xfered = 0;

}

 

// If we had an error while transfering data we flush the

// DMA channel to clear out any garbage

if(mrq->data->error != MMC_ERR_NONE) {

s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);

DBG(PFX "flushing DMA./n");

}

// Issue stop command

if(mrq->data->stop) mmc_wait_for_cmd(mmc, mrq->data->stop, 3);

 

 

request_done:

 

mrq->done(mrq);

}

 

static void s3c2410sdi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) {

struct s3c2410sdi_host *host = mmc_priv(mmc);

u32 sdi_psc, sdi_con;

u32 sdifsta;

 

//Set power

sdi_con = readl(host->base + S3C2410_SDICON);

DBG(PFX "s3c2410sdi_set_ios/n");

DBG(PFX "S3C2410_SDICON :%08x/n",sdi_con);

 

switch(ios->power_mode) {

case MMC_POWER_ON:

case MMC_POWER_UP:

//s3c2410_gpio_setpin(S3C2410_GPA17, 1); // card power on

 

s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);

s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);

s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);

s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);

s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);

s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);

if (host->pdata->set_power)

(host->pdata->set_power)(1);

sdifsta = readl(host->base + S3C2410_SDIFSTA);

 

sdifsta |= S3C2410_SDICON_FIFORESET;

writel(sdifsta,host->base + S3C2410_SDIFSTA);

//sdi_con|= S3C2410_SDICON_FIFORESET;

break;

 

case MMC_POWER_OFF:

default:

if (host->pdata->set_power)

(host->pdata->set_power)(0);

break;

}

 

//Set clock

for(sdi_psc=0;sdi_psc<255;sdi_psc++) {

if( (clk_get_rate(host->clk) / (2*(sdi_psc+1))) <= ios->clock) break;

}

 

if(sdi_psc > 255) sdi_psc = 255;

printk("sdi_psc = %x/n",sdi_psc);

writel(sdi_psc, host->base + S3C2410_SDIPRE);

 

//Set CLOCK_ENABLE

if(ios->clock) sdi_con |= S3C2410_SDICON_CLOCKTYPE;

else sdi_con &=~S3C2410_SDICON_CLOCKTYPE;

 

writel(sdi_con, host->base + S3C2410_SDICON);

DBG(PFX "S3C2410_SDICON :%08x/n",readl(host->base + S3C2410_SDICON));

DBG(PFX "S3C2410_SDICON :%08x/n",readl(host->base + S3C2410_SDIPRE));

 

host->bus_width = ios->bus_width;

 

}

 

static struct mmc_host_ops s3c2410sdi_ops = {

.request = s3c2410sdi_request,

.set_ios = s3c2410sdi_set_ios,

};

 

static void s3c2410_mmc_def_setpower(unsigned int to)

{

//s3c2410_gpio_cfgpin(S3C2410_GPA17, S3C2410_GPA17_OUT);

//s3c2410_gpio_setpin(S3C2410_GPA17, to);

}

 

static struct s3c24xx_mmc_platdata s3c2410_mmc_defplat = {

.gpio_detect = S3C2410_GPG10,

.set_power = s3c2410_mmc_def_setpower,

.f_max = 3000000,

.ocr_avail = MMC_VDD_32_33,

};

 

 

void s3c2410_sdi_test(struct s3c2410sdi_host *host)

{

int finish;

s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);

s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);

s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);

s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);

s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);

s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);

 

writel((50000000/300000-1), host->base + S3C2410_SDIPRE);

 

writel((1<<4|1), host->base + S3C2410_SDICON);

// Setup SDI command argument and data control

writel(0, host->base + S3C2410_SDICMDARG);

writel((1<<8)|0x40, host->base + S3C2410_SDIDCON);

 

finish = readl(host->base + S3C2410_SDICMDSTAT);

printk("finish1 =%x/n",finish);

 

while((finish&0x800)!=0x800)

{

finish = readl(host->base + S3C2410_SDICMDSTAT);

printk("finish1 =%x/n",finish);

 

}

writel(finish , host->base + S3C2410_SDICMDSTAT);

writel(0xff8000, host->base + S3C2410_SDICMDARG);

writel((0x1<<9)|(0x1<<8)|0x41, host->base + S3C2410_SDIDCON);

finish = readl(host->base + S3C2410_SDICMDSTAT);

printk("finish2 =%x/n",finish);

 

while( !( ((finish&0x200)==0x200) | ((finish&0x400)==0x400) ))

{

finish = readl(host->base + S3C2410_SDICMDSTAT);

 

}

writel(finish , host->base + S3C2410_SDICMDSTAT);

printk("finish2 =%x/n",finish);

 

}

 

static int s3c2410sdi_probe(struct device *dev)

{

struct platform_device *pdev = to_platform_device(dev);

struct mmc_host *mmc;

s3c24xx_mmc_pdata_t *pdata;

 

struct s3c2410sdi_host *host;

 

int ret;

 

mmc = mmc_alloc_host(sizeof(struct s3c2410sdi_host), dev);

if (!mmc) {

ret = -ENOMEM;

goto probe_out;

}

 

host = mmc_priv(mmc);

 

spin_lock_init( &host->complete_lock );

host->complete_what = COMPLETION_NONE;

host->mmc = mmc;

host->dma = S3C2410SDI_DMA;

 

pdata = dev->platform_data;

if (!pdata) {

dev->platform_data = &s3c2410_mmc_defplat;

pdata = &s3c2410_mmc_defplat;

}

 

host->pdata = pdata;

host->irq_cd = s3c2410_gpio_getirq(pdata->gpio_detect);

printk("host->irq_cd=%d/n",host->irq_cd);

s3c2410_gpio_cfgpin(pdata->gpio_detect, S3C2410_GPG10_EINT18);

 

host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (!host->mem) {

printk(KERN_INFO PFX "failed to get io memory region resouce./n");

ret = -ENOENT;

goto probe_free_host;

}

 

host->mem = request_mem_region(host->mem->start,

RESSIZE(host->mem), pdev->name);

 

if (!host->mem) {

printk(KERN_INFO PFX "failed to request io memory region./n");

ret = -ENOENT;

goto probe_free_host;

}

 

host->base = ioremap(host->mem->start, RESSIZE(host->mem));

if (host->base == 0) {

printk(KERN_INFO PFX "failed to ioremap() io memory region./n");

ret = -EINVAL;

goto probe_free_mem_region;

}

 

 

host->irq = platform_get_irq(pdev, 0);

if (host->irq == 0) {

printk(KERN_INFO PFX "failed to get interrupt resouce./n");

ret = -EINVAL;

goto probe_iounmap;

}

 

if(request_irq(host->irq, s3c2410sdi_irq, 0, DRIVER_NAME, host)) {

printk(KERN_INFO PFX "failed to request sdi interrupt./n");

ret = -ENOENT;

goto probe_iounmap;

}

 

 

//s3c2410_gpio_cfgpin(S3C2410_GPG10, S3C2410_GPG10_EINT18); //GPG10 for sd detect;

set_irq_type(host->irq_cd, IRQT_BOTHEDGE);

 

if(request_irq(host->irq_cd, s3c2410sdi_irq_cd, 0, DRIVER_NAME, host)) {

printk(KERN_WARNING PFX "failed to request card detect interrupt./n" );

ret = -ENOENT;

goto probe_free_irq;

}

if(s3c2410_dma_request(S3C2410SDI_DMA, &s3c2410sdi_dma_client, NULL)) {

printk(KERN_WARNING PFX "unable to get DMA channel./n" );

ret = -EBUSY;

goto probe_free_irq_cd;

}

 

host->clk = clk_get(dev, "sdi");

if (IS_ERR(host->clk)) {

printk(KERN_INFO PFX "failed to find clock source./n");

ret = PTR_ERR(host->clk);

host->clk = NULL;

goto probe_free_host;

}

 

if((ret = clk_use(host->clk))) {

printk(KERN_INFO PFX "failed to use clock source./n");

goto clk_free;

}

 

if((ret = clk_enable(host->clk))) {

printk(KERN_INFO PFX "failed to enable clock source./n");

goto clk_free;

}

//s3c2410_sdi_test(host);

 

 

mmc->ops = &s3c2410sdi_ops;

mmc->ocr_avail = MMC_VDD_32_33;

//mmc->flags = MMC_HOST_WIDEMODE;

mmc->f_min = clk_get_rate(host->clk) / 512;

mmc->f_max = clk_get_rate(host->clk) / 2;

mmc->caps = MMC_CAP_4_BIT_DATA;

 

//HACK: There seems to be a hardware bug in TomTom GO.

if(mmc->f_max>3000000) mmc->f_max=3000000;

 

 

/*

* Since we only have a 16-bit data length register, we must

* ensure that we don't exceed 2^16-1 bytes in a single request.

* Choose 64 (512-byte) sectors as the limit.

*/

mmc->max_sectors = 64;

 

/*

* Set the maximum segment size. Since we aren't doing DMA

* (yet) we are only limited by the data length register.

*/

 

mmc->max_seg_size = mmc->max_sectors << 9;

 

 

printk(KERN_INFO PFX "probe: mapped sdi_base=%p irq=%u irq_cd=%u dma=%u./n",

host->base, host->irq, host->irq_cd, host->dma);

 

// if (host->pdata && host->pdata->init)

// host->pdata->init(dev, s3c2410sdi_irq_cd, mmc);

 

if((ret = mmc_add_host(mmc))) {

printk(KERN_INFO PFX "failed to add mmc host./n");

goto clk_disable;

}

 

dev_set_drvdata(dev, mmc);

 

printk(KERN_INFO PFX "initialisation done./n");

return 0;

 

 

 

clk_disable:

clk_disable(host->clk);

 

//clk_unuse:

// clk_unuse(host->clk);

 

clk_free:

clk_put(host->clk);

 

probe_free_irq_cd:

free_irq(host->irq_cd, host);

 

probe_free_irq:

free_irq(host->irq, host);

 

probe_iounmap:

iounmap(host->base);

 

probe_free_mem_region:

release_mem_region(host->mem->start, RESSIZE(host->mem));

 

probe_free_host:

mmc_free_host(mmc);

probe_out:

return ret;

}

 

static int s3c2410sdi_remove(struct device *dev)

{

struct mmc_host *mmc = dev_get_drvdata(dev);

struct s3c2410sdi_host *host = mmc_priv(mmc);

 

mmc_remove_host(mmc);

clk_disable(host->clk);

clk_unuse(host->clk);

clk_put(host->clk);

free_irq(host->irq_cd, host);

free_irq(host->irq, host);

iounmap(host->base);

release_mem_region(host->mem->start, RESSIZE(host->mem));

mmc_free_host(mmc);

 

return 0;

}

 

static struct device_driver s3c2410sdi_driver =

{

.name = "s3c2410-sdi",

.bus = &platform_bus_type,

.probe = s3c2410sdi_probe,

.remove = s3c2410sdi_remove,

};

 

static int __init s3c2410sdi_init(void)

{

return driver_register(&s3c2410sdi_driver);

}

 

static void __exit s3c2410sdi_exit(void)

{

driver_unregister(&s3c2410sdi_driver);

}

 

module_init(s3c2410sdi_init);

module_exit(s3c2410sdi_exit);

 

MODULE_DESCRIPTION("Samsung S3C2410 Multimedia Card Interface driver");

MODULE_LICENSE("GPL");

s3c2410mci.h:

/*

* linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver

*

* Copyright (C) 2004 Thomas Kleffel, All Rights Reserved.

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License version 2 as

* published by the Free Software Foundation.

*/

 

struct clk;

 

//FIXME: DMA Resource management ?!

#define S3C2410SDI_DMA 0

 

#define S3C2410SDI_CDLATENCY 50

 

enum s3c2410sdi_waitfor {

COMPLETION_NONE,

COMPLETION_CMDSENT,

COMPLETION_RSPFIN,

COMPLETION_XFERFINISH,

COMPLETION_XFERFINISH_RSPFIN,

};

 

typedef struct s3c24xx_mmc_platdata s3c24xx_mmc_pdata_t;

 

struct s3c2410sdi_host {

struct mmc_host *mmc;

s3c24xx_mmc_pdata_t *pdata;

 

struct resource *mem;

struct clk *clk;

void __iomem *base;

int irq;

int irq_cd;

int dma;

 

 

struct scatterlist* cur_sg; /* Current SG entry */

unsigned int num_sg; /* Number of entries left */

void* mapped_sg; /* vaddr of mapped sg */

unsigned int offset; /* Offset into current entry */

unsigned int remain; /* Data left in curren entry */

 

int size; /* Total size of transfer */

 

struct mmc_request *mrq;

 

unsigned char bus_width; /* Current bus width */

 

spinlock_t complete_lock;

struct completion complete_request;

struct completion complete_dma;

enum s3c2410sdi_waitfor complete_what;

};

 

 

文章选

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值