omap24xx_mmc.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,027 行 · 第 1/2 页

C
1,027
字号
/* * drivers/media/mmc/omap.c * * Driver for OMAP24xx MMC controller. * * Author: Texas Instruments *  * Copyright (C) 2004 Texas Instruments. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. *  * History: * * 2004/10/07 Madhusudhan Chikksture - Modified to integrate MMC hotplug requirements on *                                     2420 platforms */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/dma-mapping.h>#include <asm/irq.h>#include <asm/dma.h>#include <asm/hardware.h>#include <asm/mach-types.h>#include <asm/arch/bus.h>#include <linux/mmc/host.h>#include <linux/mmc/protocol.h>#include <linux/mmc/card.h>#include <asm/arch/mux.h>#include <asm/arch/gpio.h>#include <linux/ioport.h>#include <linux/blkdev.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <linux/mmc/mmc.h>#include <asm/arch/dma.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/fb.h>#include "omap24xx_mmc.h"/* global variables *//* Time out Value for the mmc wait for command */#define MMC_WAIT_TIMEOUT	10000#ifdef CONFIG_MMC_DEBUG#define DBG(x...)       printk(x)#else#define DBG(x...)       do { } while (0)#endif#ifdef CONFIG_MMC_HOTPLUG/************************ Hotplug specific function prototypes and macros ****/#define CONTROL_PADCONF_sdrc_a14  0x0030static int card_present;static unsigned short card_status;static void start_state_machine(void);static void set_hp_handler(void *);DECLARE_WORK(set_hp_work, &set_hp_handler, NULL);static void stop_state_machine(void);static void stop_hp_handler(void *);DECLARE_WORK(stop_hp_work, &stop_hp_handler, NULL);static struct mmc_omap_host *saved_host;static void run_sbin_hotplug(int insert);void mmc_cd_register(struct hotplug_structure *ptr);int mmc_hotplug_insert(struct mmc_host *host);int mmc_hotplug_remove(struct mmc_host *host);#define GPIO_0           0#define DEBOUNCE_ENABLE  1#define DEBOUNCE_TIME    10#define INSERT           1#define REMOVE           0/************************************************************************/static __inline__ u8 omap_mmc_pin_out(u32 offset, u8 val){	return writeb(val, OMAP24XX_VA_SYSTEM_CONTROL_BASE + offset);}#endifstatic voidmmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd){	u32 cmdreg;	u32 resptype;	u32 cmdtype;	udelay(500);	DBG("MMC%d: CMD%d, argument 0x%08x", host->id, cmd->opcode, cmd->arg);	if (cmd->flags & MMC_RSP_SHORT)		DBG(", 32-bit response");	if (cmd->flags & MMC_RSP_LONG)		DBG(", 128-bit response");	if (cmd->flags & MMC_RSP_CRC)		DBG(", CRC");	if (cmd->flags & MMC_RSP_BUSY)		DBG(", busy notification");	DBG("\n");	host->cmd = cmd;	resptype = 0;	cmdtype = 0;	/* Protocol layer does not provide response type,	 * but our hardware needs to know exact type, not just size!	 */	switch (cmd->flags & MMC_RSP_MASK) {	case MMC_RSP_NONE:		/* resp 0 */		break;	case MMC_RSP_SHORT:		/* resp 1, resp 1b */		/* OR resp 3!! (assume this if bus is set opendrain) */		if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)			resptype = 3;		else			resptype = 1;		break;	case MMC_RSP_LONG:		/* resp 2 */		resptype = 2;		break;	}	/* Protocol layer does not provide command type, but our hardware	 * needs it!	 * any data transfer means adtc type (but that information is not	 * in command structure, so we flagged it into host struct.)	 * However, telling bc, bcr and ac apart based on response is	 * not foolproof:	 * CMD0  = bc  = resp0  CMD15 = ac  = resp0	 * CMD2  = bcr = resp2  CMD10 = ac  = resp2	 *	 * Resolve to best guess with some exception testing:	 * resp0 -> bc, except CMD15 = ac	 * rest are ac, except if opendrain	 */	if (host->datadir) {		cmdtype = OMAP_MMC_CMDTYPE_ADTC;	} else if (resptype == 0 && cmd->opcode != 15) {		cmdtype = OMAP_MMC_CMDTYPE_BC;	} else if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) {		cmdtype = OMAP_MMC_CMDTYPE_BCR;	} else {		cmdtype = OMAP_MMC_CMDTYPE_AC;	}	cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);	if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)		cmdreg |= 1 << 6;	if (cmd->flags & MMC_RSP_BUSY)		cmdreg |= 1 << 11;	if (host->datadir == OMAP_MMC_DATADIR_READ)		cmdreg |= 1 << 15;	OMAP_MMC_WRITE(host->base, CTO, 200);	OMAP_MMC_WRITE(host->base, ARGL, cmd->arg & 0xffff);	OMAP_MMC_WRITE(host->base, ARGH, cmd->arg >> 16);	OMAP_MMC_WRITE(host->base, IE,		       OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |		       OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |		       OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |		       OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |		       OMAP_MMC_STAT_END_OF_DATA);	OMAP_MMC_WRITE(host->base, CMD, cmdreg);}static voidmmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data){	host->data = NULL;	host->datadir = OMAP_MMC_DATADIR_NONE;	if (data->error == MMC_ERR_NONE)		data->bytes_xfered += data->blocks * (1 << data->blksz_bits);	if (!data->stop) {		host->mrq = NULL;		mmc_request_done(host->mmc, data->mrq);		return;	}	mmc_omap_start_command(host, data->stop);}static voidmmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd){	host->cmd = NULL;	switch (cmd->flags & MMC_RSP_MASK) {	case MMC_RSP_NONE:		/* resp 0 */		break;	case MMC_RSP_SHORT:		/* response types 1, 1b, 3, 4, 5, 6 */		cmd->resp[0] =		    OMAP_MMC_READ(host->base, RSP6) |		    (OMAP_MMC_READ(host->base, RSP7) << 16);		DBG("MMC%d: Response %08x\n", host->id, cmd->resp[0]);		break;	case MMC_RSP_LONG:		/* response type 2 */		cmd->resp[3] =		    OMAP_MMC_READ(host->base, RSP0) |		    (OMAP_MMC_READ(host->base, RSP1) << 16);		cmd->resp[2] =		    OMAP_MMC_READ(host->base, RSP2) |		    (OMAP_MMC_READ(host->base, RSP3) << 16);		cmd->resp[1] =		    OMAP_MMC_READ(host->base, RSP4) |		    (OMAP_MMC_READ(host->base, RSP5) << 16);		cmd->resp[0] =		    OMAP_MMC_READ(host->base, RSP6) |		    (OMAP_MMC_READ(host->base, RSP7) << 16);		DBG("MMC%d: Response %08x %08x %08x %08x\n", host->id,		    cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);		break;	}	if (host->data == NULL || cmd->error != MMC_ERR_NONE) {		DBG("MMC%d: End request, err %x\n", host->id, cmd->error);		host->mrq = NULL;		mmc_request_done(host->mmc, cmd->mrq);	}}static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs){	struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;	u16 status;	int end_command;	int end_transfer;	int ii;	if (host->cmd == NULL && host->data == NULL) {		status = OMAP_MMC_READ(host->base, STAT);		printk(KERN_INFO "MMC%d: Spurious interrupt 0x%04x\n", host->id,		       status);		if (status != 0) {			OMAP_MMC_WRITE(host->base, STAT, status);			OMAP_MMC_WRITE(host->base, IE, 0);		}		return IRQ_HANDLED;	}	end_command = 0;	end_transfer = 0;	while ((status = OMAP_MMC_READ(host->base, STAT)) != 0) {		OMAP_MMC_WRITE(host->base, STAT, status);	// Reset status bits		DBG("\tMMC IRQ %04x\n", status);		if ((status & OMAP_MMC_STAT_A_FULL) ||		    ((status & OMAP_MMC_STAT_END_OF_DATA) &&		     (host->bytesleft > 0))) {			// Buffer almost full			ii = host->bytesleft / 2;			if (ii > 32)				ii = 32;			host->bytesleft -= ii * 2;			while (ii-- > 0)				*host->buffer++ =				    OMAP_MMC_READ(host->base, DATA);		}		if (status & OMAP_MMC_STAT_A_EMPTY) {			// Buffer almost empty			ii = host->bytesleft / 2;			if (ii > 32)				ii = 32;			host->bytesleft -= ii * 2;			while (ii-- > 0)				OMAP_MMC_WRITE(host->base, DATA,					       *host->buffer++);		}		if (status & OMAP_MMC_STAT_END_OF_DATA) {			// Block sent/received			end_transfer = 1;		}		if (status & OMAP_MMC_STAT_DATA_TOUT) {			// Data timeout			printk(KERN_DEBUG "MMC%d: Data timeout\n", host->id);			if (host->data) {				host->data->error |= MMC_ERR_TIMEOUT;				end_transfer = 1;			}		}		if (status & OMAP_MMC_STAT_DATA_CRC) {			// Data CRC error			if (host->data) {				host->data->error |= MMC_ERR_BADCRC;				printk(KERN_DEBUG				       "MMC%d: Data CRC error, bytes left %d\n",				       host->id, host->bytesleft);				end_transfer = 1;			} else {				printk(KERN_DEBUG "MMC%d: Data CRC error\n",				       host->id);			}		}		if (status & OMAP_MMC_STAT_CMD_TOUT) {			// Command timeout			if (host->cmd) {				/* Timeouts are normal in case of MMC_SEND_STATUS */				if (host->cmd->opcode != MMC_ALL_SEND_CID	/* &&										   !mmc_omap_cover_is_open(host) */ )					printk(KERN_ERR					       "MMC%d: Command timeout, CMD%d\n",					       host->id, host->cmd->opcode);				host->cmd->error |= MMC_ERR_TIMEOUT;				end_command = 1;			}		}		if (status & OMAP_MMC_STAT_CMD_CRC) {			/* Command CRC error */			printk(KERN_ERR "MMC%d: Command CRC error\n", host->id);			if (host->cmd) {				host->cmd->error |= MMC_ERR_BADCRC;				end_command = 1;			}		}		if (status & OMAP_MMC_STAT_OCR_BUSY) {			// OCR Busy			if (host->cmd && host->cmd->opcode != MMC_SEND_OP_COND			    && host->cmd->opcode != MMC_SET_RELATIVE_ADDR) {				printk(KERN_DEBUG				       "MMC%d: OCR busy error, CMD%d\n",				       host->id, host->cmd->opcode);			}		}		if (status & OMAP_MMC_STAT_CARD_ERR) {			// Card status error			printk(KERN_DEBUG "MMC%d: Card status error\n",			       host->id);			if (host->cmd) {				host->cmd->error |= MMC_ERR_FAILED;				end_command = 1;			}			if (host->data) {				host->data->error |= MMC_ERR_FAILED;				end_transfer = 1;			}		}		/*		 * NOTE: On 1610 the END_OF_CMD may come too early when		 *       starting a write 		 */		if ((status & OMAP_MMC_STAT_END_OF_CMD) &&		    (!(status & OMAP_MMC_STAT_A_EMPTY))) {			// End of command phase			end_command = 1;		}	}	if (end_command) {		mmc_omap_cmd_done(host, host->cmd);	}	if (end_transfer) {		mmc_omap_xfer_done(host, host->data);	}	return IRQ_HANDLED;}#ifdef CONFIG_MMC_HOTPLUG/* Interrupt service routine for handling card insertion and removal */static irqreturn_t mmc_omap_irq_cd(int irq, void *dev_id, struct pt_regs *regs){	saved_host = (struct mmc_omap_host *)dev_id;	card_status = omap_get_gpio_datain(GPIO_0);	if (card_status == 1) {		card_present = REMOVE;		stop_state_machine();	} else {		card_present = INSERT;		start_state_machine();	}	return IRQ_HANDLED;}#endif/* * DMA call back function */static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data){	struct mmc_omap_host *host = (struct mmc_omap_host *)data;	int dma_ch;	/* FIXME: We ignore the possible errors for now. */	if (host->dma_ch < 0) {		printk(KERN_ERR "MMC%d: DMA callback while DMA not enabled?\n",		       host->id);		return;	}	dma_ch = host->dma_ch;	host->dma_ch = -1;	omap_free_dma(dma_ch);	complete(&host->dma_completion);}static int mmc_omap_start_dma_transfer(struct mmc_omap_host *host,				       struct mmc_request *req){	int sync_dev, dma_ch, r;	const char *dev_name;	/* If for some reason the DMA transfer is still active,	 * we wait for it to complete. This shouldn't normally happen. */	if (host->dma_ch != -1) {		printk(" MMC : waiting for DMA channel \n");		wait_for_completion(&host->dma_completion);	}	init_completion(&host->dma_completion);	if (!(req->data->flags & MMC_DATA_WRITE)) {		sync_dev = MMC_DMA_RX_REQUEST + 1;		dev_name = "MMC1 read";	} else {		sync_dev = MMC_DMA_TX_REQUEST + 1;		dev_name = "MMC1 write";	}	r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb, host,			     &dma_ch);	if (r != 0) {		printk("MMC%d: omap_request_dma() failed with %d\n", host->id,		       r);		return r;	}	if (!(req->data->flags & MMC_DATA_WRITE)) {		/* 16 frames/block, 32 bytes/frame */		omap_set_dma_src_params(dma_ch, 0x00,					(OMAP_MMC_BASE + OMAP_MMC_REG_DATA), 0,					0);		omap_set_dma_dest_params(dma_ch, 0x01,					 virt_to_phys(req->data->req->buffer),					 0, 0);		OMAP_MMC_WRITE(host->base, BUF, 0x8f0f);		r = 0;	} else {		omap_set_dma_dest_params(dma_ch, 0x00,					 (OMAP_MMC_BASE + OMAP_MMC_REG_DATA), 0,					 0);		omap_set_dma_src_params(dma_ch, 0x01,					virt_to_phys(req->data->req->buffer), 0,					0);		OMAP_MMC_WRITE(host->base, BUF, 0x0f8f);		r = 0;	}	omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, 16,				     16 * req->data->blocks, 0x02, sync_dev, r);	host->dma_ch = dma_ch;	omap_start_dma(dma_ch);	return 0;}static void mmc_omap_prepare_data(struct mmc_omap_host *host,				  struct mmc_request *req){	int timeout;	host->data = req->data;

⌨️ 快捷键说明

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