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

📄 bvd_mmc.c

📁 spi driver for sd /mmc card
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * drivers/mmc/bvd_mmc.c * Low-level MMC/SD functions for the Intel Bulverde MMC/SD on-chip controller * Mostly based on: drivers/mmc/omap_mmc.c * * Copyright 2003 MontaVista Software Inc. * Author: MontaVista Software, Inc. *	   source@mvista.com * *  This program is free software; you can redistribute	 it and/or modify it *  under  the terms of	 the GNU General  Public License as published by the *  Free Software Foundation;  either version 2 of the	License, or (at your *  option) any later version. * *  THIS  SOFTWARE  IS PROVIDED	  ``AS	IS'' AND   ANY	EXPRESS OR IMPLIED *  WARRANTIES,	  INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN *  NO	EVENT  SHALL   THE AUTHOR  BE	 LIABLE FOR ANY	  DIRECT, INDIRECT, *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT *  NOT LIMITED	  TO, PROCUREMENT OF  SUBSTITUTE GOODS	OR SERVICES; LOSS OF *  USE, DATA,	OR PROFITS; OR	BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *  ANY THEORY OF LIABILITY, WHETHER IN	 CONTRACT, STRICT LIABILITY, OR TORT *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *  You should have received a copy of the  GNU General Public License along *  with this program; if not, write  to the Free Software Foundation, Inc., *  675 Mass Ave, Cambridge, MA 02139, USA. * * References to technical documents used in comments: * [1] - Intel(R) Bulverde Processor (B-Stepping) Developer's Manual *//* * Copyright 2004 Motorola, Inc. All Rights Reserved. * Revision History:                    Modification     Changed by            Date             Description of Changes----------------   ------------      -------------------------Zhu Zhifu           04/05/2004         change for write protectionzhou qiong          06/08/2004         change for pm callbackJiang Lili          08/24/2005         change for add card detect when call pm resumeJiang Lili          10/25/2005         change for sleep power consume isue*/#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/pm.h>#include <linux/mmc/mmc_ll.h>#include <linux/pci.h>#include <asm/irq.h>        #include <asm/unaligned.h>#include <asm/io.h>#include <asm/arch/hardware.h>#include <asm/arch/mainstone.h>#include <asm/arch/ezx.h>#include <asm/dma.h>#ifdef CONFIG_ARCH_EZX_HAINANextern u8 ezx_mmc_get_slot_state(void);#endif #define MMC_CLK_OFF_TIMEOUT 1000#define MMC_COMMAND_TIMEOUT HZ/* Device-specific data */typedef struct bvd_mmc_data {	struct mmc_request *	request;	int			sd;	int			dma_rx_ch;	int			dma_tx_ch;	u32			event_mask;	int                     use_4bit;	struct timer_list	timeout;} bvd_mmc_data_t;#define MMC_EVENT_RESPONSE	0x01	/* Command response */#define MMC_EVENT_DATA_DONE	0x02	/* Data Transfer done */#define MMC_EVENT_RX_DMA_DONE	0x04	/* Rx DMA done */#define MMC_EVENT_TX_DMA_DONE	0x08	/* Tx DMA done */#define MMC_EVENT_PROG_DONE	0x10	/* Programming is done */#define MMC_EVENT_CLK_OFF	0x80	/* Clock is off */static bvd_mmc_data_t bvd_mmc_data;extern void ezx_detect_handler(unsigned long data);static int mmc_crc_error = 0;#ifdef CONFIG_ARCH_EZX_E680/* dont call hotplug funciton if phone resumes from sleeping */ static void e680_reconfig_gpio(){    set_GPIO_mode(GPIO_MMC_CLK | GPIO_ALT_FN_2_OUT);    set_GPIO_mode(GPIO_MMC_CMD | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);    set_GPIO_mode(GPIO_MMC_DATA0  | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);    set_GPIO_mode(GPIO_MMC_DATA1 | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);    set_GPIO_mode(GPIO_MMC_DATA2 | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);    set_GPIO_mode(GPIO_MMC_DATA3 | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);}#endif/* Stop the MMC clock and wait while it is happens */static inline int bvd_mmc_stop_clock_and_wait(void){	int timeout;		DEBUG(2, "stop MMC clock\n");	MMC_STRPCL = MMC_STRPCL_STOP_CLK;	for (timeout = 0; !(MMC_I_REG & MMC_I_CLK_IS_OFF); timeout++) {		if (timeout > MMC_CLK_OFF_TIMEOUT) {			DEBUG(3, "Timeout on stop clock waiting\n");			return MMC_ERROR_TIMEOUT;		}		udelay(1);	}	DEBUG(2, "clock off time is %d microsec\n", timeout);        return MMC_NO_ERROR;}/* Stop the clock */static inline void bvd_mmc_stop_clock(void){	MMC_STRPCL = MMC_STRPCL_STOP_CLK;}/* Start the MMC clock */static inline int bvd_mmc_start_clock(void){	DEBUG(2, "start MMC clock\n");	MMC_STRPCL = MMC_STRPCL_STRT_CLK;	return MMC_NO_ERROR;}/* Select the MMC clock frequency */int bvd_mmc_set_clock(u32 rate){	DEBUG(2, "set clock to %u Hz\n", rate);	bvd_mmc_stop_clock_and_wait();	if (rate < 304000)		return MMC_ERROR_OUT_OF_RANGE;	/* It seems, MMC controller can not operate correctly at highest	   clock frequency */	MMC_CLKRT = (rate >= 19500000 ? MMC_CLKRT_FREQ_19_5MHZ :		     rate >= 9750000  ? MMC_CLKRT_FREQ_9_75MHZ :		     rate >= 4880000  ? MMC_CLKRT_FREQ_4_88MHZ :		     rate >= 2440000  ? MMC_CLKRT_FREQ_2_44MHZ : 		     rate >= 1220000  ? MMC_CLKRT_FREQ_1_22MHZ :		     rate >= 609000   ? MMC_CLKRT_FREQ_609KHZ :		     MMC_CLKRT_FREQ_304KHZ);	return MMC_NO_ERROR;}/* Initialize the MMC controller to up the slot, assuming the card is in slot */void bvd_mmc_slot_up(void) {	DEBUG(2, "Init MMC h/w\n");		/* Turn on core clock signal for the MMC controller ([2], 3.8.2.2) */	CKEN |= CKEN12_MMC;	DEBUG(3, " ...core MMC clock OK\n");		/* Configure MMCLK bus clock output signal	   ([2], 15.3, 24.4.2) */	set_GPIO_mode(GPIO_MMC_CLK | GPIO_ALT_FN_2_OUT);	DEBUG(3, " ...MMCLK signal OK\n");	/* Configure MMCMD command/response bidirectional signal	   ([2], 15.3, 24.4.2) */	set_GPIO_mode(GPIO_MMC_CMD | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);	DEBUG(3, " ...MMCMD signal OK\n");	/* Configure MMDAT[0123] data bidirectional signals	   ([2], 15.3, 24.4.2) */	set_GPIO_mode(GPIO_MMC_DATA0  | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);	// for dat3 used to detect card insert and remove on A780, 	// so we only use 1 bit transfer mode ---zq#ifndef CONFIG_ARCH_EZX_A780    //from Barbados P3, we will use 4 bit mode --jll 	set_GPIO_mode(GPIO_MMC_DATA1 | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);	set_GPIO_mode(GPIO_MMC_DATA2 | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);	set_GPIO_mode(GPIO_MMC_DATA3 | GPIO_ALT_FN_1_IN | GPIO_ALT_FN_1_OUT);#endif 		DEBUG(3, " ...MMDATx signals OK\n");	/* One of Intel's hardware hackers recommend me	   to wait 1ms here. Ok, it's not so complex :-) */	mdelay(1);		/*	 * Ok, looks like basic h/w initialized, let's talk with MMC itself	 */	        /* Stop the MMC clock before 1st command ([2], 15.6) */	bvd_mmc_stop_clock_and_wait();	enable_irq(IRQ_MMC);	DEBUG(2, "MMC h/w initialized\n");}/* Shut down the slot */void bvd_mmc_slot_down(void){	DEBUG(2, "down MMC h/w\n");	/* Turn off core clock signal for the MMC controller ([2], 3.8.2.2) */	CKEN &= ~CKEN12_MMC;}/* Halt Tx and Rx DMA channels */static inline void bvd_mmc_dma_halt(struct bvd_mmc_data *mmc){	DCSR(mmc->dma_tx_ch) = DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR;	DCSR(mmc->dma_rx_ch) = DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR;}/* Establish set of events we are waiting for command completion */static void bvd_mmc_wait_for(u32 event_set){	unsigned long flags;	local_irq_save(flags);	bvd_mmc_data.event_mask = event_set;	MMC_I_MASK = MMC_I_ALL &		~((event_set & MMC_EVENT_DATA_DONE ? MMC_I_DATA_TRAN_DONE : 0) |		  (event_set & MMC_EVENT_RESPONSE ? MMC_I_END_CMD_RES : 0) |		  (event_set & MMC_EVENT_PROG_DONE ? MMC_I_PRG_DONE : 0) |		  (event_set == MMC_EVENT_CLK_OFF ? MMC_I_CLK_IS_OFF : 0) |		  (event_set != 0 ? 		   MMC_I_RES_ERR | MMC_I_DAT_ERR | MMC_I_TINT :		   0));	local_irq_restore(flags);}/* Complete the request processing */static inline void bvd_mmc_request_complete(struct bvd_mmc_data *mmc, 					    enum mmc_result_t result){	struct mmc_request *req = mmc->request;	unsigned int flags;	local_irq_save(flags);	if (req != NULL) {		bvd_mmc_dma_halt(mmc);		del_timer(&mmc->timeout);		req->result = result;		mmc_cmd_complete(req);		mmc->request = NULL;		bvd_mmc_wait_for(0);	}	local_irq_restore(flags);	if (result != MMC_NO_ERROR)		bvd_mmc_stop_clock_and_wait();}/* Trigger the event(s). Complete commands if all expected events are   occured */static void bvd_mmc_event(u32 event){	unsigned long flags;	u32 events;	struct bvd_mmc_data *mmc = &bvd_mmc_data;	local_irq_save(flags);	events = mmc->event_mask & ~event;	bvd_mmc_wait_for(events);	local_irq_restore(flags);	if (events == MMC_EVENT_CLK_OFF)		bvd_mmc_stop_clock();	else if (events == 0)	{		if (mmc_crc_error)		    bvd_mmc_request_complete(mmc, MMC_ERROR_CRC);		else		    bvd_mmc_request_complete(mmc, MMC_NO_ERROR);	}}static void bvd_mmc_dma_rx_start(struct bvd_mmc_data *mmc);/* Handle DMA data receive completion */static void bvd_mmc_dma_rx_callback(int channel, void *data, 				      struct pt_regs *regs){	struct bvd_mmc_data *mmc = data;	struct mmc_request *request = mmc->request;	DEBUG(3, "DMA RX Callback: DCSR 0x%08x MMC_STAT 0x%08x "		 "I_REG 0x%08x I_MASK 0x%08x nob %d\n",		DCSR(channel), MMC_STAT, MMC_I_REG, MMC_I_MASK,		request->nob);	request->buffer += request->block_len;	if (DCSR(channel) & DCSR_BUSERR)  {		printk(KERN_DEBUG "bvd_mmc: MMC rx dma bus error.\n");	}	DCSR(channel) = DCSR_STARTINTR | DCSR_ENDINTR | DCSR_BUSERR;	if (--request->nob > 0) {		bvd_mmc_dma_rx_start(mmc);	} else {		bvd_mmc_event(MMC_EVENT_RX_DMA_DONE);	}}/* Prepare DMA to start data transfer from the MMC card */static void bvd_mmc_dma_rx_start(struct bvd_mmc_data *mmc){	struct mmc_request *request = mmc->request;	int channel = mmc->dma_rx_ch; 	dma_addr_t dma_addr = virt_to_bus(request->buffer);	DEBUG(3, "MMC DMA Rx start: chan %d buf 0x%08x phys 0x%08x "		 "blklen %d nob%d\n",		 channel, (u32)request->buffer, (u32)dma_addr,		 request->block_len, request->nob);	consistent_sync(request->buffer, request->block_len, 			PCI_DMA_FROMDEVICE);	DCSR(channel) = DCSR_NODESC;	DSADR(channel) = __PREG(MMC_RXFIFO);	DTADR(channel) = dma_addr;	DCMD(channel) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_WIDTH1 |			DCMD_BURST32 | DCMD_ENDIRQEN |			(request->block_len & DCMD_LENGTH);	DRCMRRXMMC = (channel & DRCMR_CHLNUM) | DRCMR_MAPVLD;	DCSR(channel) |= DCSR_RUN;}static void bvd_mmc_dma_tx_start(struct bvd_mmc_data *mmc);/* Handle transmit DMA competion */static void bvd_mmc_dma_tx_callback(int channel, void *data, 				    struct pt_regs *regs){	struct bvd_mmc_data *mmc = data;	struct mmc_request *request = mmc->request;		DEBUG(3, "DMA TX Callback\n");	request->buffer += request->block_len;	if (DCSR(channel) & DCSR_BUSERR)  {		printk(KERN_DEBUG "bvd_mmc: MMC tx dma bus error.\n");	}	DCSR(channel) = DCSR_STARTINTR | DCSR_ENDINTR | DCSR_BUSERR;	if (--request->nob > 0)		bvd_mmc_dma_tx_start(mmc);	else		bvd_mmc_event(MMC_EVENT_TX_DMA_DONE);}/* Prepare DMA to start data transfer to the MMC card */static void bvd_mmc_dma_tx_start(struct bvd_mmc_data *mmc){	struct mmc_request *request = mmc->request;	int channel = mmc->dma_tx_ch;	dma_addr_t dma_addr = virt_to_bus(request->buffer);	DEBUG(3, "MMC DMA Tx start: chan %d buf 0x%08x phys 0x%08x "		 "blklen %d nob%d\n",		 channel, (u32)request->buffer, (u32)dma_addr,		 request->block_len, request->nob);	consistent_sync(request->buffer, request->block_len, 			PCI_DMA_TODEVICE);	DCSR(channel) = DCSR_NODESC;	DTADR(channel) = __PREG(MMC_TXFIFO);	DSADR(channel) = dma_addr;	DCMD(channel) = DCMD_INCSRCADDR | DCMD_FLOWTRG |  DCMD_WIDTH1 | 			DCMD_BURST32 | DCMD_ENDIRQEN |			(request->block_len & DCMD_LENGTH);	DRCMRTXMMC = (channel & DRCMR_CHLNUM) | DRCMR_MAPVLD;	DCSR(channel) |= DCSR_RUN;}/* Prepare MMC controller for card command execution */static int bvd_mmc_exec_command(struct mmc_request *request){	u32 cmdat = 0;	u32 nob = 1;	/* use 4-bit bus width when possible */	if (bvd_mmc_data.use_4bit) 		cmdat |= MMC_CMDAT_SD_4DAT;	switch (request->cmd) {	/* MMC core extra command */	case MMC_CIM_RESET:		cmdat |= MMC_CMDAT_INIT;		break;	/* bc - broadcast - no response */	case MMC_GO_IDLE_STATE:	case MMC_SET_DSR:		break;	/* bcr - broadcast with response */	case MMC_SEND_OP_COND:	case MMC_ALL_SEND_CID:	case MMC_GO_IRQ_STATE:		break;	/* adtc - addressed with data transfer */	case MMC_READ_DAT_UNTIL_STOP:	case MMC_READ_SINGLE_BLOCK:	case MMC_READ_MULTIPLE_BLOCK:		cmdat |= MMC_CMDAT_DATA_EN |			 MMC_CMDAT_RD | MMC_CMDAT_DMA_EN;		break;	case SEND_SCR:		cmdat |= MMC_CMDAT_DATA_EN | MMC_CMDAT_RD;		break;	case MMC_WRITE_DAT_UNTIL_STOP:	case MMC_WRITE_BLOCK:	case MMC_WRITE_MULTIPLE_BLOCK:	case MMC_PROGRAM_CID:	case MMC_PROGRAM_CSD:	case MMC_SEND_WRITE_PROT:	case MMC_GEN_CMD:		cmdat |= MMC_CMDAT_DATA_EN | MMC_CMDAT_WR | MMC_CMDAT_DMA_EN;		break;	case MMC_LOCK_UNLOCK:		cmdat |= MMC_CMDAT_DATA_EN | MMC_CMDAT_WR;		break;	case MMC_STOP_TRANSMISSION:		cmdat |= MMC_CMDAT_STOP_TRAN;		break;	/* ac - no data transfer */	default: 		break;	}          	switch (request->cmd) {	case MMC_READ_MULTIPLE_BLOCK:	case MMC_WRITE_MULTIPLE_BLOCK:		nob = request->nob;		break;	default:		nob = 1;	}	switch (request->rtype) {	case RESPONSE_NONE:		cmdat |= MMC_CMDAT_RES_NORESP;		break;	case RESPONSE_R1B:		cmdat |= MMC_CMDAT_BUSY;		/*FALLTHRU*/	case RESPONSE_R1:	case RESPONSE_R4:	case RESPONSE_R5:	case RESPONSE_R6:		cmdat |= MMC_CMDAT_RES_RESP;		break;	case RESPONSE_R3:		cmdat |= MMC_CMDAT_RES_R3;		break;	case RESPONSE_R2_CID:	case RESPONSE_R2_CSD:		cmdat |= MMC_CMDAT_RES_R2;		break;	default:		break;	}	/* Set command index */	if (request->cmd == MMC_CIM_RESET) {		MMC_CMD = MMC_GO_IDLE_STATE & MMC_CMD_MASK;	} else {		MMC_CMD = request->cmd & MMC_CMD_MASK;	}                /* Set argument */    	MMC_ARGL = request->arg & MMC_ARGL_MASK;	MMC_ARGH = (request->arg >> 16) & MMC_ARGH_MASK;	if (request->cmd == SEND_SCR) {		MMC_BLKLEN = 8;		MMC_NOB = 1;	} else {		MMC_BLKLEN = request->block_len & MMC_BLKLEN_MASK;		MMC_NOB = nob & MMC_NOB_MASK;	}	MMC_RDTO = ~0 & MMC_RDTO_MASK;	MMC_RESTO = ~0 & MMC_RESTO_MASK;	MMC_CMDAT = cmdat;        /* Send command */	DEBUG(3, ": Send cmd %d cmdat: %x arg: %d resp %d\n", request->cmd,	      cmdat, request->arg, request->rtype);

⌨️ 快捷键说明

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