⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 s3cmci.c

📁 《ARM嵌入式Linux设备驱动实例开发》
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver * *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de> * * 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/module.h>#include <linux/dma-mapping.h>#include <linux/clk.h>#include <linux/mmc/host.h>#include <linux/mmc/protocol.h>#include <linux/platform_device.h>#include <asm/dma.h>#include <asm/dma-mapping.h>#include <asm/io.h>#include <asm/arch/regs-sdi.h>#include <asm/arch/regs-gpio.h>#include "mmc_debug.h"#include "s3cmci.h"#define DRIVER_NAME "s3c-mci"enum dbg_channels {	dbg_err   = (1 << 0),	dbg_debug = (1 << 1),	dbg_info  = (1 << 2),	dbg_irq   = (1 << 3),	dbg_sg    = (1 << 4),	dbg_dma   = (1 << 5),	dbg_pio   = (1 << 6),	dbg_fail  = (1 << 7),	dbg_conf  = (1 << 8),};static const int dbgmap_err   = dbg_err | dbg_fail;static const int dbgmap_info  = dbg_info | dbg_conf;static const int dbgmap_debug = dbg_debug;#define dbg(host, channels, args...)		 \	if (dbgmap_err & channels) 		 \		dev_err(&host->pdev->dev, args); \	else if (dbgmap_info & channels)	 \		dev_info(&host->pdev->dev, args);\	else if (dbgmap_debug & channels)	 \		dev_dbg(&host->pdev->dev, args);#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)static struct s3c2410_dma_client s3cmci_dma_client = {	.name		= "s3c-mci",};static void finalize_request(struct s3cmci_host *host);static void s3cmci_send_request(struct mmc_host *mmc);static void s3cmci_reset(struct s3cmci_host *host);#ifdef CONFIG_MMC_DEBUGstatic inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix){	u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;	u32 datcon, datcnt, datsta, fsta, imask;	con 	= readl(host->base + S3C2410_SDICON);	pre 	= readl(host->base + S3C2410_SDIPRE);	cmdarg 	= readl(host->base + S3C2410_SDICMDARG);	cmdcon 	= readl(host->base + S3C2410_SDICMDCON);	cmdsta 	= readl(host->base + S3C2410_SDICMDSTAT);	r0 	= readl(host->base + S3C2410_SDIRSP0);	r1 	= readl(host->base + S3C2410_SDIRSP1);	r2 	= readl(host->base + S3C2410_SDIRSP2);	r3 	= readl(host->base + S3C2410_SDIRSP3);	timer 	= readl(host->base + S3C2410_SDITIMER);	bsize 	= readl(host->base + S3C2410_SDIBSIZE);	datcon 	= readl(host->base + S3C2410_SDIDCON);	datcnt 	= readl(host->base + S3C2410_SDIDCNT);	datsta 	= readl(host->base + S3C2410_SDIDSTA);	fsta 	= readl(host->base + S3C2410_SDIFSTA);	imask   = readl(host->base + host->sdiimsk);	dbg(host, dbg_debug, "%s  CON:[%08x]  PRE:[%08x]  TMR:[%08x]\n",				prefix, con, pre, timer);	dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",				prefix, cmdcon, cmdarg, cmdsta);	dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"			       " DSTA:[%08x] DCNT:[%08x]\n",				prefix, datcon, fsta, datsta, datcnt);	dbg(host, dbg_debug, "%s   R0:[%08x]   R1:[%08x]"			       "   R2:[%08x]   R3:[%08x]\n",				prefix, r0, r1, r2, r3);}static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,								int stop){ 	snprintf(host->dbgmsg_cmd, 300,		"#%u%s op:%s(%i) arg:0x%08x flags:0x08%x retries:%u",		host->ccnt, (stop?" (STOP)":""), mmc_cmd2str(cmd->opcode),		cmd->opcode, cmd->arg, cmd->flags, cmd->retries);	if (cmd->data) {		snprintf(host->dbgmsg_dat, 300,			"#%u bsize:%u blocks:%u bytes:%u",			host->dcnt, cmd->data->blksz,			cmd->data->blocks,			cmd->data->blocks * cmd->data->blksz);	} else {		host->dbgmsg_dat[0] = '\0';	}}static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,								int fail){	unsigned int dbglvl = fail?dbg_fail:dbg_debug;	if (!cmd)		return;	if (cmd->error == MMC_ERR_NONE) {		dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",			host->dbgmsg_cmd, cmd->resp[0]);	} else {		dbg(host, dbglvl, "CMD[%s] %s Status:%s\n",			mmc_err2str(cmd->error), host->dbgmsg_cmd,			host->status);	}	if (!cmd->data)		return;	if (cmd->data->error == MMC_ERR_NONE) {		dbg(host, dbglvl, "DAT[%s] %s\n",			mmc_err2str(cmd->data->error), host->dbgmsg_dat);	} else {		dbg(host, dbglvl, "DAT[%s] %s DCNT:0x%08x\n",			mmc_err2str(cmd->data->error), host->dbgmsg_dat,			readl(host->base + S3C2410_SDIDCNT));	}}#endifstatic inline u32 enable_imask(struct s3cmci_host *host, u32 imask){	u32 newmask;	newmask = readl(host->base + host->sdiimsk);	newmask|= imask;	writel(newmask, host->base + host->sdiimsk);	return newmask;}static inline u32 disable_imask(struct s3cmci_host *host, u32 imask){	u32 newmask;	newmask = readl(host->base + host->sdiimsk);	newmask&= ~imask;	writel(newmask, host->base + host->sdiimsk);	return newmask;}static inline void clear_imask(struct s3cmci_host *host){	writel(0, host->base + host->sdiimsk);}static inline int get_data_buffer(struct s3cmci_host *host,			volatile u32 *words, volatile u32 **pointer){	struct scatterlist *sg;	if (host->pio_active == XFER_NONE)		return -EINVAL;	if ((!host->mrq) || (!host->mrq->data))		return -EINVAL;	if (host->pio_sgptr >= host->mrq->data->sg_len) {		dbg(host, dbg_debug, "no more buffers (%i/%i)\n",		      host->pio_sgptr, host->mrq->data->sg_len);		return -EBUSY;	}	sg = &host->mrq->data->sg[host->pio_sgptr];	*words	= sg->length >> 2;	*pointer= page_address(sg->page) + sg->offset;	host->pio_sgptr++;	dbg(host, dbg_sg, "new buffer (%i/%i)\n",	      host->pio_sgptr, host->mrq->data->sg_len);	return 0;}#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)static inline void do_pio_read(struct s3cmci_host *host){	int res;	u32 fifo;	void __iomem *from_ptr;	//write real prescaler to host, it might be set slow to fix	writel(host->prescaler, host->base + S3C2410_SDIPRE);	from_ptr = host->base + host->sdidata;	while ((fifo = FIFO_FILL(host))) {		if (!host->pio_words) {			res = get_data_buffer(host, &host->pio_words,							&host->pio_ptr);			if (res) {				host->pio_active = XFER_NONE;				host->complete_what = COMPLETION_FINALIZE;				dbg(host, dbg_pio, "pio_read(): "					"complete (no more data).\n");				return;			}			dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",			       host->pio_words, host->pio_ptr);		}		dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "				   "buffer:[%03i] dcnt:[%08X]\n",				   fifo, host->pio_words,				   readl(host->base + S3C2410_SDIDCNT));		if (fifo > host->pio_words)			fifo = host->pio_words;		host->pio_words-= fifo;		host->pio_count+= fifo;		while(fifo--) {			*(host->pio_ptr++) = readl(from_ptr);		}	}	if (!host->pio_words) {		res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);		if (res) {			dbg(host, dbg_pio, "pio_read(): "				"complete (no more buffers).\n");			host->pio_active = XFER_NONE;			host->complete_what = COMPLETION_FINALIZE;			return;		}	}	enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);}static inline void do_pio_write(struct s3cmci_host *host){	int res;	u32 fifo;	void __iomem *to_ptr;	to_ptr = host->base + host->sdidata;	while ((fifo = FIFO_FREE(host))) {		if (!host->pio_words) {			res = get_data_buffer(host, &host->pio_words,							&host->pio_ptr);			if (res) {				dbg(host, dbg_pio, "pio_write(): "					"complete (no more data).\n");				host->pio_active = XFER_NONE;				return;			}			dbg(host, dbg_pio, "pio_write(): "				"new source: [%i]@[%p]\n",				host->pio_words, host->pio_ptr);		}		if (fifo > host->pio_words)			fifo = host->pio_words;		host->pio_words-= fifo;		host->pio_count+= fifo;		while(fifo--) {			writel(*(host->pio_ptr++), to_ptr);		}	}	enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);}static void pio_tasklet(unsigned long data){	struct s3cmci_host *host = (struct s3cmci_host *) data;	if (host->pio_active == XFER_WRITE)		do_pio_write(host);	if (host->pio_active == XFER_READ)		do_pio_read(host);	if (host->complete_what == COMPLETION_FINALIZE) {		clear_imask(host);		if (host->pio_active != XFER_NONE) {			dbg(host, dbg_err, "unfinished %s "				"- pio_count:[%u] pio_words:[%u]\n",				(host->pio_active == XFER_READ)?"read":"write",				host->pio_count, host->pio_words);			host->mrq->data->error = MMC_ERR_DMA;		}		disable_irq(host->irq);		finalize_request(host);	}}/* * 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 * * Note: "->error"-fields are expected to be set to 0 before the request *       was issued by mmc.c - therefore they are only set, when an error *       contition comes up */static irqreturn_t s3cmci_irq(int irq, void *dev_id){	struct s3cmci_host *host;	struct mmc_command *cmd;	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;	u32 mci_cclear, mci_dclear;	unsigned long iflags;	host = (struct s3cmci_host *)dev_id;	spin_lock_irqsave(&host->complete_lock, iflags);	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);	mci_imsk	= readl(host->base + host->sdiimsk);	mci_cclear	= 0;	mci_dclear	= 0;	if ((host->complete_what == COMPLETION_NONE) ||			(host->complete_what == COMPLETION_FINALIZE)) {		host->status = "nothing to complete";		clear_imask(host);		goto irq_out;	}	if (!host->mrq) {		host->status = "no active mrq";		clear_imask(host);		goto irq_out;	}	cmd = host->cmd_is_stop?host->mrq->stop:host->mrq->cmd;	if (!cmd) {		host->status = "no active cmd";		clear_imask(host);		goto irq_out;	}	if (!host->dodma) {		if ((host->pio_active == XFER_WRITE) &&				(mci_fsta & S3C2410_SDIFSTA_TFDET)) {			disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);			tasklet_schedule(&host->pio_tasklet);			host->status = "pio tx";		}		if ((host->pio_active == XFER_READ) &&				(mci_fsta & S3C2410_SDIFSTA_RFDET)) {			disable_imask(host,				S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);			tasklet_schedule(&host->pio_tasklet);			host->status = "pio rx";		}	}	if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {		cmd->error = MMC_ERR_TIMEOUT;		host->status = "error: command timeout";		goto fail_transfer;	}	if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {		if (host->complete_what == COMPLETION_CMDSENT) {			host->status = "ok: command sent";			goto close_transfer;		}		mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;	}	if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {		if (host->complete_what == COMPLETION_RSPFIN) {			host->status = "ok: command response received";			goto close_transfer;		}		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)			host->complete_what = COMPLETION_XFERFINISH;		mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;	}	/* errors handled after this point are only relevant	   when a data transfer is in progress */	if (!cmd->data)		goto clear_status_bits;	/* Check for FIFO failure */		if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {			cmd->data->error = MMC_ERR_FIFO;			host->status = "error:  fifo failure";			goto fail_transfer;		}		if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {		cmd->data->error = MMC_ERR_BADCRC;		host->status = "error: bad data crc (outgoing)";		goto fail_transfer;	}	if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {		cmd->data->error = MMC_ERR_BADCRC;		host->status = "error: bad data crc (incoming)";		goto fail_transfer;	}	if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {		cmd->data->error = MMC_ERR_TIMEOUT;		host->status = "error: data timeout";		goto fail_transfer;	}	if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {		if (host->complete_what == COMPLETION_XFERFINISH) {			host->status = "ok: data transfer completed";			goto close_transfer;		}		if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {			host->complete_what = COMPLETION_RSPFIN;		}		mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;	}clear_status_bits:	writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);	writel(mci_dclear, host->base + S3C2410_SDIDSTA);	goto irq_out;fail_transfer:	host->pio_active = XFER_NONE;close_transfer:	host->complete_what = COMPLETION_FINALIZE;	clear_imask(host);	tasklet_schedule(&host->pio_tasklet);	goto irq_out;irq_out:	dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x "			   "fsta:0x%08x dcnt:0x%08x status:%s.\n",				mci_csta, mci_dsta, mci_fsta,				mci_dcnt, host->status);	spin_unlock_irqrestore(&host->complete_lock, iflags);	return IRQ_HANDLED;}/* * ISR for the CardDetect Pin*/static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id){	struct s3cmci_host *host = (struct s3cmci_host *)dev_id;	dbg(host, dbg_irq, "card detect\n");	mmc_detect_change(host->mmc, 500);	return IRQ_HANDLED;}void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,	int size, enum s3c2410_dma_buffresult result){	unsigned long iflags;	u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;	struct s3cmci_host *host = (struct s3cmci_host *)buf_id;	mci_csta 	= readl(host->base + S3C2410_SDICMDSTAT);	mci_dsta 	= readl(host->base + S3C2410_SDIDSTA);	mci_fsta 	= readl(host->base + S3C2410_SDIFSTA);	mci_dcnt 	= readl(host->base + S3C2410_SDIDCNT);	if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))		return;	if (!host->dmatogo)		return;	spin_lock_irqsave(&host->complete_lock, iflags);	if (result != S3C2410_RES_OK) {		dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "			"fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",			mci_csta, mci_dsta, mci_fsta,			mci_dcnt, result, host->dmatogo);		goto fail_request;	}	host->dmatogo--;	if (host->dmatogo) {		dbg(host, dbg_dma, "DMA DONE  Size:%i DSTA:[%08x] "			"DCNT:[%08x] toGo:%u\n",			size, mci_dsta, mci_dcnt, host->dmatogo);		goto out;	}	dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",		size, mci_dsta, mci_dcnt);	host->complete_what = COMPLETION_FINALIZE;out:	tasklet_schedule(&host->pio_tasklet);	spin_unlock_irqrestore(&host->complete_lock, iflags);	return;fail_request:	host->mrq->data->error = MMC_ERR_DMA;	host->complete_what = COMPLETION_FINALIZE;	writel(0, host->base + host->sdiimsk);	goto out;}static void finalize_request(struct s3cmci_host *host){	struct mmc_request *mrq = host->mrq;	struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;	int debug_as_failure = 0;	u32 mci_con;	if (host->complete_what != COMPLETION_FINALIZE)		return;	if (!mrq)		return;	if (cmd->data && (cmd->error == MMC_ERR_NONE) &&		  (cmd->data->error == MMC_ERR_NONE)) {		if (host->dodma && (!host->dma_complete)) {			dbg(host, dbg_dma, "DMA Missing!\n");			return;		}	}	// Read response	cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);	cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);	cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);	cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);	// reset clock speed, as it could still be set low for	writel(host->prescaler, host->base + S3C2410_SDIPRE);	if (cmd->error)		debug_as_failure = 1;	if (cmd->data && cmd->data->error)		debug_as_failure = 1;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -