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

📄 jz_mmc.c

📁 包含MMC协议,驱动源码. 在LINUX操作系统下通过.
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * drivers/mmc/jz_mmc.c * * Low-level MMC/SD functions for the Ingenic JZ4730 MMC/SD on-chip controller * * Copyright 2006 Ingenic Semiconductor Inc. * * 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. */#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/jzsoc.h>/* Device-specific data */typedef struct jz_mmc_data {	struct mmc_request *	request;	int			sd;	int			dma_rx_ch;	int			dma_tx_ch;	int                     use_4bit;	u32			events;	u32			clock;} jz_mmc_data_t;static jz_mmc_data_t jz_mmc_data;/* MMC Events */#define MMC_EVENT_NONE	        0x00	/* No events */#define MMC_EVENT_RX_DMA_DONE	0x01	/* Rx DMA done */#define MMC_EVENT_TX_DMA_DONE	0x02	/* Tx DMA done */#define MMC_EVENT_DATA_DONE	0x04	/* Data Transfer done */#define MMC_EVENT_PROG_DONE	0x08	/* Programming is done *//* Stop the MMC clock and wait while it happens */static inline int jz_mmc_stop_clock(void){	int timeout = 1000;		DEBUG(2, "stop MMC clock\n");	REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP;	while (timeout && (REG_MSC_STAT & MSC_STAT_CLK_EN)) {		timeout--;		if (timeout == 0) {			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;}/* Start the MMC clock and operation */static inline int jz_mmc_start_clock(void){	REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP;	return MMC_NO_ERROR;}static inline u32 jz_mmc_calc_clkrt(int is_sd, u32 rate){	u32 clkrt;	u32 clk_src = is_sd ? 24000000: 16000000;	clkrt = 0;  	while (rate < clk_src)    	{      		clkrt ++;      		clk_src >>= 1;    	}	return clkrt;}/* Select the MMC clock frequency */static int jz_mmc_set_clock(u32 rate){	int clkrt;	jz_mmc_stop_clock();	__cpm_select_msc_clk(jz_mmc_data.sd); /* select clock source from CPM */	clkrt = jz_mmc_calc_clkrt(jz_mmc_data.sd, rate);	DEBUG(2, "set clock to %u Hz is_sd=%d clkrt=%d\n", 	      rate, jz_mmc_data.sd, clkrt);	REG_MSC_CLKRT = clkrt;	jz_mmc_data.clock = rate;	return MMC_NO_ERROR;}/* Initialize the MMC controller to up the slot, assuming the card is in slot */void jz_mmc_slot_up(void){	DEBUG(2, "Init MMC h/w\n");	/* Init GPIO */	__gpio_as_msc();	/* Turn on power for the slot */	__msc_init_io();	__msc_enable_power();		/* Turn on core clock signal for the MMC/SC controller  */	__cpm_select_msc_clk(0); /* select 19MHz clock to MSC module */	__msc_set_clkrt(7);      /* lowest clock to MMC/SD card */	        /* Stop the MMC clock before 1st command */	jz_mmc_stop_clock();}/* Shut down the slot */void jz_mmc_slot_down(void){	DEBUG(2, "down MMC h/w\n");	/* Turn off MMC clock */	jz_mmc_stop_clock();	/* Turn off power for the slot */	__msc_disable_power();}/* Complete the request processing */static inline void jz_mmc_request_complete(struct jz_mmc_data *mmc, 					   enum mmc_result_t result){	struct mmc_request *req = mmc->request;	if (!req) return;	jz_mmc_stop_clock();	req->result = result;	mmc_cmd_complete(req);	mmc->request = NULL;}static inline voidjz_mmc_start_dma(int chan, unsigned long phyaddr,int count, int mode){	unsigned long flags;	flags = claim_dma_lock();	disable_dma(chan);	clear_dma_ff(chan);	jz_set_dma_block_size(chan, 32);	set_dma_mode(chan, mode);	set_dma_addr(chan, phyaddr);	set_dma_count(chan, count + 31);	enable_dma(chan);	release_dma_lock(flags);}static void jz_mmc_dma_rx_start(struct jz_mmc_data *mmc);/* Handle DMA data receive completion */static void jz_mmc_dma_rx_callback(int irq, void *data, 				   struct pt_regs *regs){	struct jz_mmc_data *mmc = data;	int channel = jz_mmc_data.dma_rx_ch;	DEBUG(3, "DMA RX Callback: MMC_STAT 0x%08x "		 "I_REG 0x%08x I_MASK 0x%08x\n",	      REG_MSC_STAT, REG_MSC_IREG, REG_MSC_IMASK);	disable_dma(channel);	if (__dmac_channel_address_error_detected(channel)) {		printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__);		__dmac_channel_clear_address_error(channel);	}	if (__dmac_channel_transmit_end_detected(channel)) {		__dmac_channel_clear_transmit_end(channel);		mmc->events &= ~MMC_EVENT_RX_DMA_DONE;		if (mmc->events == 0) {			jz_mmc_request_complete(mmc, MMC_NO_ERROR);		}	}}/* Prepare DMA to start data transfer from the MMC card */static void jz_mmc_dma_rx_start(struct jz_mmc_data *mmc){	struct mmc_request *request = mmc->request;	int channel = mmc->dma_rx_ch; 	dma_addr_t dma_addr = PHYSADDR(request->buffer);	u32 size = request->block_len * request->nob;	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);	dma_cache_wback_inv((unsigned int)request->buffer, size);	jz_mmc_start_dma(channel, dma_addr, size, DMA_MODE_READ);}static void jz_mmc_dma_tx_start(struct jz_mmc_data *mmc);/* Handle transmit DMA competion */static void jz_mmc_dma_tx_callback(int irq, void *data, 				    struct pt_regs *regs){	struct jz_mmc_data *mmc = data;	int channel = jz_mmc_data.dma_tx_ch;		DEBUG(3, "DMA TX Callback\n");	disable_dma(channel);	if (__dmac_channel_address_error_detected(channel)) {		__dmac_channel_clear_address_error(channel);	}	if (__dmac_channel_transmit_end_detected(channel)) {		__dmac_channel_clear_transmit_end(channel);		mmc->events &= ~MMC_EVENT_TX_DMA_DONE;		if (mmc->events == 0) {			jz_mmc_request_complete(mmc, MMC_NO_ERROR);		}	}}/* Prepare DMA to start data transfer to the MMC card */static void jz_mmc_dma_tx_start(struct jz_mmc_data *mmc){	struct mmc_request *request = mmc->request;	int channel = mmc->dma_tx_ch;	dma_addr_t dma_addr = PHYSADDR(request->buffer);	u32 size = request->block_len * request->nob;	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);	dma_cache_wback_inv((unsigned int)request->buffer, size);	jz_mmc_start_dma(channel, dma_addr, size, DMA_MODE_WRITE);}static int jz_mmc_check_status(struct mmc_request *request){	u32 status = REG_MSC_STAT;	/* Checking for response or data timeout */	if (status & (MSC_STAT_TIME_OUT_RES | MSC_STAT_TIME_OUT_READ)) {		printk("MMC/SD timeout, MMC_STAT 0x%x\n", status);		return MMC_ERROR_TIMEOUT;	}	/* Checking for CRC error */	if (status & (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR | MSC_STAT_CRC_RES_ERR)) {		printk("MMC/CD CRC error, MMC_STAT 0x%x\n", status);		return MMC_ERROR_CRC;	}	return MMC_NO_ERROR;}/* Obtain response to the command and store it to response buffer */static void jz_mmc_get_response(struct mmc_request *request){	int i;	u8 *buf;	u32 data;	DEBUG(2, "fetch response for request %d, cmd %d\n", request->rtype, request->cmd);	buf = request->response;	request->result = MMC_NO_ERROR;	switch (request->rtype) {	case RESPONSE_R1: case RESPONSE_R1B: case RESPONSE_R6:	case RESPONSE_R3: case RESPONSE_R4: case RESPONSE_R5:	{		data = REG_MSC_RES;		buf[0] = (data >> 8) & 0xff;		buf[1] = data & 0xff;		data = REG_MSC_RES;		buf[2] = (data >> 8) & 0xff;		buf[3] = data & 0xff;		data = REG_MSC_RES;		buf[4] = data & 0xff;		DEBUG(3, "request %d, response [%02x %02x %02x %02x %02x]\n",		      request->rtype, buf[0], buf[1], buf[2], buf[3], buf[4]);		break;	}	case RESPONSE_R2_CID: case RESPONSE_R2_CSD:	{		for (i = 0; i < 16; i += 2) {			data = REG_MSC_RES;			buf[i] = (data >> 8) & 0xff;			buf[i+1] = data & 0xff;		}		DEBUG(3, "request %d, response [", request->rtype);#if CONFIG_MMC_DEBUG_VERBOSE > 2		if (g_mmc_debug >= 3) {			int n;			for (n = 0; n < 17; n++)				printk("%02x ", buf[n]);			printk("]\n");		}#endif		break;	}	case RESPONSE_NONE:		DEBUG(3, "No response\n");		break;			default:		DEBUG(3, "unhandled response type for request %d\n", request->rtype);		break;	}}/* Prepare MMC controller for card command execution */static int jz_mmc_exec_command(struct mmc_request *request){	struct jz_mmc_data *mmc = &jz_mmc_data;	u32 cmdat = 0, timeout = 0x3fffff;	int retval;	/* stop MMC clock */	jz_mmc_stop_clock();	/* mask interrupts */	REG_MSC_IMASK = 0xffff;	/* clear status */	REG_MSC_IREG = 0xffff;	/* use 4-bit bus width when possible */	if (jz_mmc_data.use_4bit) 		cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT;        /* Set command type and events */ 	mmc->events = MMC_EVENT_NONE;	switch (request->cmd) {	/* MMC core extra command */	case MMC_CIM_RESET:		cmdat |= MSC_CMDAT_INIT; /* Initialization sequence sent prior to command */		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 |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ | MSC_CMDAT_DMA_EN;		mmc->events = MMC_EVENT_RX_DMA_DONE | MMC_EVENT_DATA_DONE;		break;	case SEND_SCR:		cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ;		mmc->events = MMC_EVENT_DATA_DONE;		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:	case MMC_LOCK_UNLOCK:		cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE | MSC_CMDAT_DMA_EN;		mmc->events = MMC_EVENT_TX_DMA_DONE | MMC_EVENT_DATA_DONE | MMC_EVENT_PROG_DONE;		break;	case MMC_STOP_TRANSMISSION:		mmc->events = MMC_EVENT_PROG_DONE;		break;	/* ac - no data transfer */	default: 		break;	}	/* Set response type */	switch (request->rtype) {	case RESPONSE_NONE:		break;	case RESPONSE_R1B:		cmdat |= MSC_CMDAT_BUSY;		/*FALLTHRU*/	case RESPONSE_R1:		cmdat |= MSC_CMDAT_RESPONSE_R1;		break;	case RESPONSE_R2_CID:	case RESPONSE_R2_CSD:		cmdat |= MSC_CMDAT_RESPONSE_R2;		break;	case RESPONSE_R3:		cmdat |= MSC_CMDAT_RESPONSE_R3;		break;	case RESPONSE_R4:		cmdat |= MSC_CMDAT_RESPONSE_R4;		break;	case RESPONSE_R5:		cmdat |= MSC_CMDAT_RESPONSE_R5;		break;	case RESPONSE_R6:		cmdat |= MSC_CMDAT_RESPONSE_R6;

⌨️ 快捷键说明

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