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

📄 mmc_slot_s3c2410.c

📁 s3c2410 linux的sd驱动
💻 C
字号:
/* * drivers/mmc/mmc_slot_s3c2410.c * * MMC slot interfaces specific to S3C2410 SD Controller * * Copyright (C) 2002-2003 MIZI Research, Inc. * * Author: Chan Gyun Jeong <cgjeong@mizi.com> * $Id: mmc_slot_s3c2410.c,v 1.2 2004/01/26 08:29:56 laputa Exp $ * * Revision History: * * 2002-12-07 Chan Gyun Jeong <cgjeong@mizi.com> * - initial release *  * 2003-01-13 Chan Gyun Jeong <cgjeong@mizi.com> * - clean up, and sort of improvements * * 2003-01-17 Chan Gyun Jeong <cgjeong@mizi.com> * - support for interrupt-driven data I/O * * 2004-01-06 kwang hyun LA <laputa: nala.la@samsung.com> * - pre-scaler caculate method changed for s3c2410 *  * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/irq.h>#include <linux/delay.h>#include <asm/hardware.h>#include <asm/arch/cpu_s3c2410.h>#include "mmc.h"/* Board-specific definitions */#ifdef CONFIG_S3C2410_SMDK#define SD_GPIO_CD		GPIO_nCD_SD#define SD_IRQ_CD		IRQ_nCD_SD#define SD_CD_LOWACTIVE#undef USE_POLLING#define USE_INTERRUPT#endifstatic struct tq_struct card_detect_task;static volatile int card_in;#ifdef USE_INTERRUPTstatic DECLARE_WAIT_QUEUE_HEAD(wq);static volatile int reading;static u_char *buffer;static int bufcnt;static int datalen;static volatile int error;#endif/* * MMC Data R/W functions  */static int read_data(struct mmc_slot *slot, u_char *buf, int len) {#ifdef USE_POLLING	int i = 0;	int cnt;	u32 stat;	DEBUG2(4, "[%s] ", __FUNCTION__);	while (i < len) {		stat = SDIDSTA;		if (!card_in) {			SDIDSTA = stat;			return -ENODEV;		}		if (stat & SDIDSTA_TOUT) {			DEBUG2(2, "[%s] Timeout Error\n", __FUNCTION__);			SDIDSTA = stat; /* clear */			return -EIO;		}		stat = SDIFSTA;		if (stat & SDIFSTA_RX) {			cnt = (stat & SDIFSTA_CNT);			while (cnt) {				*((u32 *)(buf + i)) = SDIDAT;#ifdef CONFIG_MMC_DEBUG				DEBUG2(4, "%02x", buf[i + 0]);				DEBUG2(4, "%02x", buf[i + 1]);				DEBUG2(4, "%02x", buf[i + 2]);				DEBUG2(4, "%02x", buf[i + 3]);#endif				i += sizeof(u32);				cnt -= sizeof(u32);			}		}	}	DEBUG2(4, "\n");	do {		stat = SDIDSTA;		if (!card_in) {			SDIDSTA = stat;			return -ENODEV;		}	} while (!(stat & SDIDSTA_TOUT) && !(stat & SDIDSTA_DFIN));	SDIDSTA = stat; /* clear */	return 0;#elif defined(USE_INTERRUPT)	interruptible_sleep_on(&wq);	if (!card_in) {		return -ENODEV;	}	if (error) {		return -EIO;	}	return 0;#endif /* USE_POLLING */}static int write_data(struct mmc_slot *slot, const u_char *buf, int len){#ifdef USE_POLLING	int i;	int cnt;	u32 stat;#ifdef CONFIG_MMC_DEBUG	DEBUG2(4, "[%s] ", __FUNCTION__);	for(i = 0; i < len; i++)		DEBUG2(4, "%02x", buf[i]);	DEBUG2(4, "\n");#endif	i = 0;	while (i < len) {		stat = SDIFSTA;		if (!card_in) {			SDIDSTA = stat;			return -ENODEV;		}		if (stat & SDIFSTA_TX) {			cnt = SDI_MAX_TX_FIFO - (stat & SDIFSTA_CNT);			while (cnt) {				SDIDAT = *((u32 *)(buf + i));				i += sizeof(u32);				cnt -= sizeof(u32);			}		}	}	do {		stat = SDIDSTA;		if (!card_in) {			SDIDSTA = stat;			return -ENODEV;		}	} while (!(stat & SDIDSTA_TOUT) && !(stat & SDIDSTA_DFIN));	SDIDSTA = stat; /* clear */	return 0;#elif defined(USE_INTERRUPT)	interruptible_sleep_on(&wq);	if (!card_in) {		return -ENODEV;	}	if (error) {		return -EIO;	}	return 0;	#endif /* USE_POLLING */}#ifdef USE_INTERRUPTstatic void sdi_interrupt(int irq, void *dev_id, struct pt_regs *regs){	u32 stat;	u32 cnt;	if (reading) {#ifdef CONFIG_MMC_DEBUG		if (bufcnt == 0) {			DEBUG2(4, "[%s] RX: ", __FUNCTION__);		}#endif		stat = SDIFSTA;		if (stat & SDIFSTA_RX_LAST) {			DEBUG2(5, "\nRX_LAST\n");			cnt = (stat & SDIFSTA_CNT) / sizeof(u32);			while (cnt) {				*((u32 *)(buffer + bufcnt)) = SDIDAT;#ifdef CONFIG_MMC_DEBUG				DEBUG2(4, "%02x", buffer[bufcnt + 0]);				DEBUG2(4, "%02x", buffer[bufcnt + 1]);				DEBUG2(4, "%02x", buffer[bufcnt + 2]);				DEBUG2(4, "%02x", buffer[bufcnt + 3]);#endif				bufcnt += sizeof(u32);				cnt--;			}		} else if (stat & SDIFSTA_RX_FULL) {			DEBUG2(5, "\nRX_FULL\n");			cnt = SDI_MAX_RX_FIFO / sizeof(u32);			while (cnt && bufcnt < datalen) {				*((u32 *)(buffer + bufcnt)) = SDIDAT;#ifdef CONFIG_MMC_DEBUG				DEBUG2(4, "%02x", buffer[bufcnt + 0]);				DEBUG2(4, "%02x", buffer[bufcnt + 1]);				DEBUG2(4, "%02x", buffer[bufcnt + 2]);				DEBUG2(4, "%02x", buffer[bufcnt + 3]);#endif				bufcnt += sizeof(u32);				cnt--;			}		}		stat = SDIDSTA;		if (stat & SDIDSTA_DFIN) {			DEBUG2(4, "\n");			DEBUG2(5, "DFIN\n");			SDIDSTA = stat;			wake_up_interruptible(&wq);		} else if (stat & SDIDSTA_TOUT) {			DEBUG2(4, "\n");			DEBUG2(5, "DTOUT\n");			SDIDSTA = stat;			error = 1;			wake_up_interruptible(&wq);		}	} else {		stat = SDIFSTA;		if (stat & SDIFSTA_TX_EMP) {			DEBUG2(5, "TX_EMP\n");			cnt = SDI_MAX_TX_FIFO / sizeof(u32);			while (cnt && bufcnt < datalen) {				SDIDAT = *((u32 *)(buffer + bufcnt));				bufcnt += sizeof(u32);				cnt--;			}		}		stat = SDIDSTA;		if (stat & SDIDSTA_DFIN) {			DEBUG2(5, "DFIN\n");			SDIDSTA = stat;			wake_up_interruptible(&wq);		} else if (stat & SDIDSTA_TOUT) {			DEBUG2(5, "DTOUT\n");			SDIDSTA = stat;			error = 1;			wake_up_interruptible(&wq);		}	}}#endif /* USE_INTERRRUPT *//* * SD Slot Interface funtions */static void power_up(struct mmc_slot *slot){	/* Set block size to 512 bytes */	SDIBSIZE = 0x200;	/* Set timeout count */	SDIDTIMER = 0xffff; //  timeout value for 2410 [22..0]	/* Disable SDI interrupt */	SDIIMSK = 0x0;	/* Type B, FIFO reset, SD clock enable */	SDICON = SDICON_LE | SDICON_FRESET | SDICON_ENCLK;	/* clear all */	SDIDSTA = SDIDSTA_ALL;	/* Wait for power-up */	mdelay(125);}static void power_down(struct mmc_slot *slot){	/* Disable SD Controller */	SDICON = 0x0;	SDIIMSK = 0x0;	SDIDTIMER = 0x0;	SDIBSIZE = 0x0;}static void set_clock(struct mmc_slot *slot, int rate){	unsigned long val, pclk;	if (rate == MMC_MMC_CLOCK_HIGH) {		rate = 20000000;	}	pclk = s3c2410_get_bus_clk(GET_PCLK); // laputa to receive s3c2410 PCLK from 2.4.19	val = (pclk / (rate)) ; // protect mmc over clocking to set prescler	SDIPRE = (SDIPRE_MSK & val); // prescaler set value anding}static int send_cmd(struct mmc_slot *slot,  struct mmc_cmd *cmd){	int ret;	u32 stat;	if (cmd->res_flag & MMC_RES_FLAG_RDATA) {#ifdef USE_INTERRUPT		SDIIMSK = SDIIMSK_TOUT | SDIIMSK_DFIN | SDIIMSK_RX_LAST | 			SDIIMSK_RX_FULL;		reading = 1;		buffer = cmd->data;		bufcnt = 0;		datalen = cmd->data_len;		error = 0;#endif		stat = SDIDCON_RACMD_1 | SDIDCON_BLK | SDIDCON_RX | 1 << 0;		SDIBSIZE = cmd->data_len;		SDICON |= SDICON_FRESET;		SDIDCON = stat;	} else if (cmd->res_flag & MMC_RES_FLAG_WDATA) {#ifdef USE_INTERRUPT		SDIIMSK = SDIIMSK_TOUT | SDIIMSK_DFIN | SDIIMSK_TX_EMP; 		reading = 0;		buffer = cmd->data;		bufcnt = 0;		datalen = cmd->data_len;		error = 0;#endif		stat = SDIDCON_TARSP_1 | SDIDCON_BLK | SDIDCON_TX | 1 << 0;		SDIBSIZE = cmd->data_len;		SDICON |= SDICON_FRESET;		SDIDCON = stat;	} else if (cmd->res_flag & MMC_RES_FLAG_DATALINE) {#ifdef USE_INTERRUPT		SDIIMSK = SDIIMSK_TOUT | SDIIMSK_DFIN | SDIIMSK_RX_LAST | 			SDIIMSK_RX_FULL;		reading = 1;		buffer = cmd->res;		bufcnt = 0;		datalen = cmd->data_len;		error = 0;#endif		stat = SDIDCON_RACMD_1 | SDIDCON_BLK | SDIDCON_RX | 1 << 0;		SDIBSIZE = cmd->data_len;		SDICON |= SDICON_FRESET;		SDIDCON = stat;	}	stat = SDICCON_START | cmd->cmd;	if (cmd->res_type != MMC_RES_TYPE_NONE) {		stat |=  SDICCON_WRSP;		if (cmd->res_type == MMC_RES_TYPE_R2) {			stat |= SDICCON_LRSP;		}	}	SDICARG = cmd->arg;	SDICCON = stat;	if (cmd->res_type != MMC_RES_TYPE_NONE) {		if (cmd->res_flag & MMC_RES_FLAG_DATALINE) {			ret = read_data(slot, cmd->res, cmd->data_len);			goto cmdexit;		} else {			while(1) {				stat = SDICSTA;				if (!card_in) {					SDICSTA = stat;					return -ENODEV;				}				if (stat & SDICSTA_TOUT) {					ret = -ETIMEDOUT;					SDICSTA = stat; /* clear bits */					DEBUG2(2, "[%s] Timeout Error\n",					       __FUNCTION__);					goto cmdexit;				}				if (stat & SDICSTA_RSP) break;			}			if (!(cmd->res_flag & MMC_RES_FLAG_NOCRC)) {				if ((stat & SDICSTA_ALLFLAG) != 				    (SDICSTA_SENT | SDICSTA_RSP)) {#if 0					ret = -EIO;					SDICSTA = stat; /* clear bits */					DEBUG2(2, "[%s] CRC Error\n",					       __FUNCTION__);					goto cmdexit;#endif				}			}			SDICSTA = stat; /* clear bits */			if (cmd->res_flag & MMC_RES_FLAG_RDATA) {				ret = read_data(slot, cmd->data, 						cmd->data_len);				goto cmdexit;			}			if (cmd->res_flag & MMC_RES_FLAG_WDATA) {				ret = write_data(slot, cmd->data, 						 cmd->data_len);				goto cmdexit;			}			if (mmc_get_res_len(cmd->res_type) == MMC_RES_LEN_LONG) {				stat = SDIRSP0;				stat = cpu_to_be32(stat);				memcpy(cmd->res, &stat, sizeof(stat));				stat = SDIRSP1;				stat = cpu_to_be32(stat);				memcpy(cmd->res + (1 * sizeof(stat)), 				       &stat, sizeof(stat));				stat = SDIRSP2;				stat = cpu_to_be32(stat);				memcpy(cmd->res + (2 * sizeof(stat)), 				       &stat, sizeof(stat));				stat = SDIRSP3;				stat = cpu_to_be32(stat);				memcpy(cmd->res + (3 * sizeof(stat)), 				       &stat, sizeof(stat));			} else {				stat = SDIRSP0;				stat = cpu_to_be32(stat);				memcpy(cmd->res, &stat, sizeof(stat));				stat = SDIRSP1;				stat = cpu_to_be32(stat);				memcpy(cmd->res + (1 * sizeof(stat)), 				       &stat, sizeof(stat));			}		}	} else {		do {			stat = SDICSTA;			if (!card_in) {				SDICSTA = stat;				return -ENODEV;			}		} while (!(stat & SDICSTA_SENT));		SDICSTA = stat; /* clear bits */	}	ret = 0; cmdexit:	return ret;}#define WIDE_BUS 0#define STANDARD_BUS 1static struct mmc_slot slot = {	id:			0,	narrow_bus:     WIDE_BUS,	power_up:		power_up,	power_down:		power_down,	set_clock:		set_clock,	send_cmd:		send_cmd,};static void add_task_handler(void *data){	struct mmc_slot *slot = (struct mmc_slot *)data;	int wp;#ifdef CONFIG_S3C2410_SMDK	/* Not available on SMDK2410 */	wp = 0;#endif	if (wp) {		slot->readonly = 1;	}	else {		slot->readonly = 0;	}	add_mmc_device(slot);}static void del_task_handler(void *data){	struct mmc_slot *slot = (struct mmc_slot *)data;	del_mmc_device(slot);}static void card_detect_interrupt(int irq, void *dev_id, struct pt_regs *regs){	int empty;#ifdef SD_CD_LOWACTIVE	empty = read_gpio_bit(SD_GPIO_CD);#else	empty = !read_gpio_bit(SD_GPIO_CD);#endif	DEBUG2(1, "[%s] SD CD = %d\n", __FUNCTION__, !empty);	if (!card_in && !empty) {		/* card inserted */		DEBUG2(1, "[%s] card inserted\n", __FUNCTION__);		card_in = 1;		card_detect_task.data = (void *)dev_id;		card_detect_task.routine = add_task_handler;		schedule_task(&card_detect_task);	} else if (card_in && empty) {		/* card ejected */		DEBUG2(1, "[%s] card ejected\n", __FUNCTION__);		card_in = 0;#ifdef USE_INTERRUPT		wake_up_interruptible(&wq);#endif		card_detect_task.data = (void *)dev_id;		card_detect_task.routine = del_task_handler;		schedule_task(&card_detect_task);	}}static int __init init_mmc_slot_s3c2410(void){	int ret;	/* Initialize status variables */	card_in = 0;	/* Initialize h/w settings */	set_gpio_ctrl(GPIO_SDDAT3 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN);	set_gpio_ctrl(GPIO_SDDAT2 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN);	set_gpio_ctrl(GPIO_SDDAT1 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN);	set_gpio_ctrl(GPIO_SDDAT0 | GPIO_MODE_SDDAT | GPIO_PULLUP_EN);	set_gpio_ctrl(GPIO_SDCMD | GPIO_MODE_SDCMD | GPIO_PULLUP_EN);	set_gpio_ctrl(GPIO_SDCLK | GPIO_MODE_SDCLK | GPIO_PULLUP_EN);#ifdef CONFIG_S3C2410_SMDK	set_gpio_ctrl(SD_GPIO_CD);#endif		/* Register IRQ handlers */	set_external_irq(SD_IRQ_CD, EXT_BOTH_EDGES, GPIO_PULLUP_DIS);	ret = request_irq(SD_IRQ_CD, card_detect_interrupt, 			  SA_INTERRUPT, "SD CD", (void *)&slot);	if (ret) {		printk("MMC Slot: request_irq(SD CD) failed\n");		goto err1;	}#ifdef USE_INTERRUPT	ret = request_irq(IRQ_SDI, sdi_interrupt, 			  SA_INTERRUPT, "SDI", (void *)&slot);	if (ret) {		printk("MMC Slot: request_irq(SDI) failed\n");		goto err2;	}#endif	printk("MMC Slot initialized\n");	card_detect_interrupt(SD_IRQ_CD, (void *)&slot, NULL);	return 0;#ifdef USE_INTERRUPT err2:	free_irq(SD_IRQ_CD, (void *)&slot);#endif err1:	return ret;}static void __exit exit_mmc_slot_s3c2410(void){#ifdef USE_INTERRUPT	free_irq(IRQ_SDI, (void *)&slot);#endif	free_irq(SD_IRQ_CD, (void *)&slot);	if (card_in) {		card_in = 0;		del_task_handler((void *)&slot);	}}module_init(init_mmc_slot_s3c2410);module_exit(exit_mmc_slot_s3c2410);MODULE_AUTHOR("Chan Gyun Jeong <cgjeong@mizi.com>");MODULE_LICENSE("Not GPL, Proprietary License");MODULE_DESCRIPTION("MMC slot interfaces specific to S3C2410 SD Controller");

⌨️ 快捷键说明

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