📄 s3c2440mci.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. 2005.07.09 godori from www.aesop-embedded.org(ghcstop@empal.com) porting sdi interface(DMA mode) to S3C2440A ==> irq mode?: see s3c2410mci.c.irq_mode_single_block_fault & this file's s3c2410sdi_irq()(fsta & dsta control routine) DMA mode transfer rate(10MBytes file tranfer time is 7 sec with single block mode) ==> HIGH system load with media player 2005.07.10 godori from www.aesop-embedded.org(ghcstop@empal.com) porting sdi interface(IRQ mode) to S3C2440A ==> dma mode?: s3c2410mci.c.dma IRQ mode transfer rate: slow than DMA mode ==> Low system load with media player: */#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 <asm/dma.h>#include <asm/dma-mapping.h>#include <asm/arch/dma.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/hardware/amba.h>#include <asm/hardware/clock.h>#include <asm/mach/mmc.h>#include <asm/arch/regs-sdi.h>#include <asm/arch/regs-gpio.h>//#define S3C2410SDI_DMA_BACKBUF#ifdef CONFIG_MMC_DEBUG#define DBG(x...) printk(KERN_DEBUG 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)// #define KERN_DEBUG KERN_INFO/* * 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; #ifdef CONFIG_CPU_S3C2440 u32 sdi_fsta; int i, txfifocnt, datalen; u32 sdi_data; #endif 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); #ifdef CONFIG_CPU_S3C2440 sdi_fsta = readl(host->base + S3C2410_SDIFSTA); #endif 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_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; } #ifdef CONFIG_CPU_S3C2440 if( sdi_fsta & S3C2410_SDIFSTA_FF_FAIL ) { host->mrq->cmd->error = MMC_ERR_NONE; host->mrq->data->error = MMC_ERR_FIFO; goto transfer_closed; } else if(sdi_fsta & S3C2410_SDIFSTA_FF_LASTXFER) { host->mrq->cmd->error = MMC_ERR_NONE; host->mrq->data->error = MMC_ERR_FIFO; sdi_fsta |= S3C2410_SDIFSTA_FIFORESET; writel(sdi_fsta, host->base + S3C2410_SDIFSTA); goto transfer_closed; } #else if(sdi_dsta & S3C2410_SDIDSTA_FIFOFAIL) { host->mrq->cmd->error = MMC_ERR_NONE; host->mrq->data->error = MMC_ERR_FIFO; goto transfer_closed; } #endif 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( host->mrq->data ) { datalen = host->mrq->data->blocks << host->mrq->data->blksz_bits; if( host->mrq->data->flags & MMC_DATA_READ ) { if( (sdi_fsta&S3C2410_SDIFSTA_RFLAST) == S3C2410_SDIFSTA_RFLAST ) { for(i=(sdi_fsta & 0x7f)/4;i>0;i--) { sdi_data = readl(host->base + S3C2410_SDIDATA); *((u32 *)(host->mrq->data->req->buffer + host->mrq->data->bytes_process)) = sdi_data; host->mrq->data->bytes_process += 4; } if(host->complete_what == COMPLETION_XFERFINISH) { host->mrq->cmd->error = MMC_ERR_NONE; host->mrq->data->error = MMC_ERR_NONE; sdi_fsta = 0; sdi_fsta |= S3C2410_SDIFSTA_RFLAST; writel(sdi_fsta, host->base + S3C2410_SDIFSTA); sdi_dsta = 0; sdi_dsta |= S3C2410_SDIDSTA_XFERFINISH; writel(sdi_dsta, host->base + S3C2410_SDIDSTA); goto transfer_closed; } } else if( (sdi_fsta&S3C2410_SDIFSTA_RFFULL) == S3C2410_SDIFSTA_RFFULL ) { for(i=0;i<8;i++) { sdi_data = readl(host->base + S3C2410_SDIDATA); *((u32 *)(host->mrq->data->req->buffer + host->mrq->data->bytes_process)) = sdi_data; host->mrq->data->bytes_process += 4; } } } else { if (sdi_fsta & S3C2410_SDIFSTA_TXEMPTY) { txfifocnt = 16; while (txfifocnt && host->mrq->data->bytes_process < datalen) { sdi_data = *((unsigned int *)(host->mrq->data->req->buffer + host->mrq->data->bytes_process)); writel(sdi_data, host->base + S3C2410_SDIDATA); host->mrq->data->bytes_process += 4; txfifocnt--; } } } } 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; sdi_fsta = 0; sdi_fsta |= S3C2410_SDIFSTA_RFLAST; writel(sdi_fsta, host->base + S3C2410_SDIFSTA); sdi_dsta = 0; sdi_dsta |= S3C2410_SDIDSTA_XFERFINISH; writel(sdi_dsta, host->base + S3C2410_SDIDSTA); 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); return IRQ_HANDLED;transfer_closed: host->complete_what = COMPLETION_NONE; complete(&host->complete_request); writel(0, host->base + S3C2410_SDIIMSK); spin_unlock_irqrestore( &host->complete_lock, iflags); return IRQ_HANDLED; clear_imask: writel(0, host->base + S3C2410_SDIIMSK); spin_unlock_irqrestore( &host->complete_lock, iflags); 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; mmc_detect_change(host->mmc);//printk("ghc: interrupt occur ***********************\n"); return IRQ_HANDLED;}static void s3c2410sdi_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct s3c2410sdi_host *host = mmc_priv(mmc); u32 sdi_carg, sdi_ccon, sdi_timer; u32 sdi_bsize, sdi_dcon, sdi_imsk, sdi_fsta; sdi_ccon = mrq->cmd->opcode & S3C2410_SDICMDCON_INDEX; sdi_ccon|= S3C2410_SDICMDCON_SENDERHOST; sdi_ccon|= S3C2410_SDICMDCON_CMDSTART; sdi_carg = mrq->cmd->arg; #ifdef CONFIG_ARCH_SMDK2410 //FIXME: Timer value ?! sdi_timer= 0xF000; #elif defined(CONFIG_CPU_S3C2440) sdi_timer= 0x7fffff; #endif sdi_bsize= 0; sdi_dcon = 0; sdi_imsk = 0; 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -