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

📄 pxa3xx_controller.c

📁 基于PXA3XX的SD驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * pxa3xx_controller.c - MMC/SD/SDIO Controller driver * * Copyright (C) 2006 Intel Corporation * Copyright (C) 2006 Marvell International Ltd   * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/interrupt.h>#include <linux/blkdev.h>#include <linux/dma-mapping.h>#include <linux/wait.h>#include <linux/suspend.h>#include <linux/workqueue.h>#include <linux/platform_device.h>#include <asm/uaccess.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/sizes.h>#include <asm/types.h>#include <asm/dma-mapping.h>#include <asm/arch/pxa-regs.h>#include <asm/arch/gpio.h>#include <asm/arch/pxa3xx_gpio.h>#include <asm/arch/cpu-freq-voltage-pxa3xx.h>#include <asm/arch/pxa3xx_pmic.h>#include <asm/arch/arava.h>#ifdef	CONFIG_MACH_ZYLONITE#include <asm/arch/zylonite.h>#elif	defined (CONFIG_MACH_LITTLETON)#include <asm/arch/littleton.h>#else#error	"Please select correct Platform for build"#endif #include "pxa3xx_controller.h"#ifdef CONFIG_PXA_MWB_12extern void pxa3xx_enable_pxa_mwb_wifi(void);#endif #define PXA_SLOT_SCAN	1#define PXA_SLOT_EXIT	2struct pxa_slot {	struct mss_slot *slot;	u32 cd_irq;	u32 cd_gpio;	u32 wp_gpio;	struct work_struct	sdio_int;	struct delayed_work	card_change;};#if defined(CONFIG_CPU_PXA320) || defined(CONFIG_CPU_PXA300)static u32 PXA_HOST_IRQ[PXA_MMC_MAX] = {IRQ_MMC, IRQ_MMC2};static u32 PXA_HOST_DRCMRTX[PXA_MMC_MAX] 			= {(u32)&(DRCMRTXMMC), (u32)&(DRCMRTXMMC2)};static u32 PXA_HOST_DRCMRRX[PXA_MMC_MAX]			= {(u32)&(DRCMRRXMMC), (u32)&(DRCMRRXMMC2)};#ifdef CONFIG_MMC1_SLOT1static u32 PXA_HOST_SLOTS[PXA_MMC_MAX] = {2, 1};#elsestatic u32 PXA_HOST_SLOTS[PXA_MMC_MAX] = {1, 1};#endifstatic u32 PXA_HOST_BASE[PXA_MMC_MAX] = {(u32)&(__REG(0x41100000)), 	(u32)&(__REG_2(0x42000000))};static u32 PXA_HOST_PHYBASE[PXA_MMC_MAX] = {0x41100000, 0x42000000};static u32 PXA_HOST_CKEN[PXA_MMC_MAX] = {CKEN_MMC1, CKEN_MMC2};#elif defined(CONFIG_CPU_PXA310)static u32 PXA_HOST_IRQ[PXA_MMC_MAX] = {IRQ_MMC, IRQ_MMC2, IRQ_MMC3};static u32 PXA_HOST_DRCMRTX[PXA_MMC_MAX] 			= {(u32)&(DRCMRTXMMC), (u32)&(DRCMRTXMMC2), 				(u32)&(DRCMRTXMMC3)};static u32 PXA_HOST_DRCMRRX[PXA_MMC_MAX]			= {(u32)&(DRCMRRXMMC), (u32)&(DRCMRRXMMC2), 				(u32)&(DRCMRRXMMC3)};#ifdef CONFIG_MMC1_SLOT1static u32 PXA_HOST_SLOTS[PXA_MMC_MAX] = {2, 1, 1};#elsestatic u32 PXA_HOST_SLOTS[PXA_MMC_MAX] = {1, 1, 1};#endifstatic u32 PXA_HOST_BASE[PXA_MMC_MAX] = {(u32)&(__REG(0x41100000)),	(u32)&(__REG_2(0x42000000)), (u32)&(__REG_4(0x42500000))};static u32 PXA_HOST_PHYBASE[PXA_MMC_MAX] = {0x41100000, 0x42000000, 0x42500000};static u32 PXA_HOST_CKEN[PXA_MMC_MAX] = {CKEN_MMC1, CKEN_MMC2, CKEN_MMC3};#endif/*  enable controller INT according to mask */static void pxa_host_enable_int(struct pxa_mss_host *pxa_host, unsigned int mask){	u32 i_reg = 0;	unsigned long flags;	local_irq_save(flags);	i_reg = readl(pxa_host->base + MMC_I_MASK);	i_reg &= ~mask;	writel(i_reg, pxa_host->base + MMC_I_MASK);	i_reg = readl(pxa_host->base + MMC_I_MASK);	local_irq_restore(flags);}/* *  disable controller INT according to mask */static void pxa_host_disable_int(struct pxa_mss_host *pxa_host, unsigned int mask){	u32 i_reg;	unsigned long flags;	local_irq_save(flags);	i_reg = readl(pxa_host->base + MMC_I_MASK);	i_reg |= mask;	writel(i_reg, pxa_host->base + MMC_I_MASK);	i_reg = readl(pxa_host->base + MMC_I_MASK);	local_irq_restore(flags);}static void pxa_host_start_busclock(struct pxa_mss_host *pxa_host){	u32 retries = 0xff;		writel(MMC_STRPCL_START_CLOCK, pxa_host->base + MMC_STRPCL);	while (retries--) {		if (readl(pxa_host->base + MMC_STAT) & MMC_STAT_CLOCK_ON);			break;		udelay(1);	}}static void pxa_host_stop_busclock(struct pxa_mss_host *pxa_host){	u32 retries = 0xff;		writel(MMC_STRPCL_STOP_CLOCK, pxa_host->base + MMC_STRPCL);	while (retries--) {		if (readl(pxa_host->base + MMC_I_REG) & MMC_I_REG_CLK_OFF)			break;		udelay(1);	}}/* *  setup DMA controller for data transfer  */static void pxa_host_setup_data(struct pxa_mss_host *pxa_host,		struct mss_data *data){	u32 dcmd = 0;	int i;	struct mss_host *host = pxa_host->host;	writel(data->blocks, pxa_host->base + MMC_NUMBLK);	writel(data->blksz, pxa_host->base + MMC_BLKLEN);	if (data->flags & MSS_DATA_READ) { /*read*/		dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC;		writel(0x0, pxa_host->drcmrtx);		writel(pxa_host->dma | DRCMR_MAPVLD, pxa_host->drcmrrx);	} else if (data->flags & MSS_DATA_WRITE ) { /*write*/		dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;		writel(0x0, pxa_host->drcmrrx);		writel(pxa_host->dma | DRCMR_MAPVLD, pxa_host->drcmrtx);	} else {		BUG();	}	dcmd |= DCMD_BURST32 | DCMD_WIDTH1;	dbg("read to set up dma., sg_len:%d", data->sg_len);	pxa_host->sg_idx = 0;	pxa_host->dma_len = dma_map_sg(host->dev, data->sg, data->sg_len, 			(data->flags & MSS_DATA_READ) ? 			DMA_FROM_DEVICE : DMA_TO_DEVICE);	for (i = 0; i < pxa_host->dma_len; i++) {		data->bytes_xfered += sg_dma_len(&data->sg[i]);		if (data->flags & MSS_DATA_READ) {			pxa_host->sg_cpu[i].dsadr = 				pxa_host->phybase + MMC_RXFIFO;			pxa_host->sg_cpu[i].dtadr = 				sg_dma_address(&data->sg[i]);		}		else {			pxa_host->sg_cpu[i].dsadr = 				sg_dma_address(&data->sg[i]);			pxa_host->sg_cpu[i].dtadr = 				pxa_host->phybase + MMC_TXFIFO;		}		pxa_host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]);		pxa_host->sg_cpu[i].ddadr = pxa_host->sg_dma + (i + 1) *					sizeof(struct pxa_dma_desc);		dbg("sg:%d, dsadr:0x%x, dtadr:0x%x, dcmd:0x%x, ddadr:0x%x\n", i,			pxa_host->sg_cpu[i].dsadr, pxa_host->sg_cpu[i].dtadr, 			pxa_host->sg_cpu[i].dcmd, pxa_host->sg_cpu[i].ddadr);	}	pxa_host->sg_cpu[pxa_host->dma_len - 1].ddadr = DDADR_STOP;	pxa_host->sg_cpu[pxa_host->dma_len - 1].dcmd |= DCMD_ENDIRQEN;	wmb();}/* *  read response of MMC/SD/SDIO controller. */static void pxa_host_get_response(struct pxa_mss_host *pxa_host,		struct mss_cmd *cmd){	int i;	u32 mmc_res;	for (i = 0; i < PXA_MSS_MAX_RESPONSE_SIZE; i = i + 2) {		mmc_res = readl(pxa_host->base + MMC_RES);		if (i < MSS_MAX_RESPONSE_SIZE) {			cmd->response[i] = mmc_res >> 8;			cmd->response[i + 1] = mmc_res & 0xff;		}	}#ifdef CONFIG_MMC_DEBUG	printk(KERN_DEBUG "Response for CMD 0x%x, RES:", cmd->opcode);	for (i = 0; i < PXA_MSS_MAX_RESPONSE_SIZE; i++) 		printk("0x%x,", cmd->response[i]);	printk("\n");#endif}/* *  set io_request result according to error status in MMC/SD/SDIO controller  *  status reg. */static void pxa_host_set_error(struct mss_cmd *cmd, u32 stat){	if (stat & MMC_STAT_RESP_TIMEOUT ) 		cmd->error = MSS_ERROR_TIMEOUT;	else if (stat & MMC_STAT_RESP_CRC) 		cmd->error = MSS_ERROR_CRC;	else if (stat & MMC_STAT_FLASH_ERR)		cmd->error = MSS_ERROR_FLASH;	else if (stat & MMC_STAT_READ_TIMEOUT)		cmd->error = MSS_ERROR_TIMEOUT;	else if (stat & (MMC_STAT_READ_CRC | MMC_STAT_WRITE_CRC))		cmd->error = MSS_ERROR_CRC;	dbg("Error %d for command: 0x%x\n", cmd->error, cmd->opcode);}static inline int get_slot_cd_irq(struct mss_slot *slot) {	return ((struct pxa_slot *)slot->private)->cd_irq;}static inline void set_slot_cd_irq(struct mss_slot *slot, int cd_irq){	((struct pxa_slot *)slot->private)->cd_irq = cd_irq;}static inline int get_slot_cd_gpio(struct mss_slot *slot) {	return ((struct pxa_slot *)slot->private)->cd_gpio;}static inline void set_slot_cd_gpio(struct mss_slot *slot, int cd_gpio){	((struct pxa_slot *)slot->private)->cd_gpio = cd_gpio;}static inline int get_slot_wp_gpio(struct mss_slot *slot) {	return ((struct pxa_slot *)slot->private)->wp_gpio;}static inline void set_slot_wp_gpio(struct mss_slot *slot, int wp_gpio){	((struct pxa_slot *)slot->private)->wp_gpio = wp_gpio;}#ifdef	CONFIG_MACH_ZYLONITEstatic void pxa_mss_slot_select(struct mss_slot *slot){	if (slot->host->id == PXA_MMC_1) {		if (slot->id == 0) {			pxa3xx_mfp_set_afds(MFP_MMC_CMD_0, MFP_AF4, MFP_DS03X);			/* set to GPIO output high for CMD_1 pin *///			pxa3xx_mfp_set_afds(MFP_MMC_CMD_1, MFP_AF0, MFP_DS03X);//			pxa3xx_gpio_set_direction(MFP_MMC_CMD_1, GPIO_DIR_OUT);//			pxa3xx_gpio_set_level(MFP_MMC_CMD_1, GPIO_LEVEL_HIGH);		} else if (slot->id == 1) {//			pxa3xx_mfp_set_afds(MFP_MMC_CMD_1, MFP_MMC_CMD_1_AF, //					MFP_DS03X);//			/* set to GPIO output high for CMD_0 pin *///			pxa3xx_mfp_set_afds(MFP_MMC_CMD_0, MFP_AF0, MFP_DS03X);////			pxa3xx_gpio_set_direction(MFP_MMC_CMD_0, GPIO_DIR_OUT);//			pxa3xx_gpio_set_level(MFP_MMC_CMD_0, GPIO_LEVEL_HIGH);		} else			BUG();	} else if(slot->host->id == PXA_MMC_2) {//		pxa3xx_mfp_set_afds(MFP_MMC2_CMD, MFP_AF4, MFP_DS08X);	}#if defined(CONFIG_CPU_PXA310)	else if(slot->host->id == PXA_MMC_3) {//		pxa3xx_mfp_set_afds(MFP_MMC3_CMD, MFP_MMC3_CMD_AF, MFP_DS03X);	}#endif	else		BUG();}#elif	defined (CONFIG_MACH_LITTLETON)static void pxa_mss_slot_select(struct mss_slot *slot){	if (slot->host->id == PXA_MMC_1) {		if (slot->id == 0) {			pxa3xx_mfp_set_afds(MFP_MMC_CMD_0, MFP_AF4, MFP_DS03X);		} else if (slot->id == 1) {			/* set to GPIO output high for CMD_0 pin */ 			pxa3xx_mfp_set_afds(MFP_MMC_CMD_0, MFP_AF0, MFP_DS03X);			pxa3xx_gpio_set_direction(MFP_MMC_CMD_0, GPIO_DIR_OUT);			pxa3xx_gpio_set_level(MFP_MMC_CMD_0, GPIO_LEVEL_HIGH);		} else			BUG();	} else if(slot->host->id == PXA_MMC_2) {		pxa3xx_mfp_set_afds(MFP_MMC2_CMD, MFP_AF4, MFP_DS08X);	}#if defined(CONFIG_CPU_PXA310)	else if(slot->host->id == PXA_MMC_3) {		pxa3xx_mfp_set_afds(MFP_MMC3_CMD, MFP_MMC3_CMD_AF, MFP_DS03X);	}#endif	else		BUG();}#else#error	"Please select correct platform for build"#endif/* *  return value: 0 -- not write-protected, 1 -- write-protected */static int pxa_mss_slot_is_wp(struct mss_slot *slot){	u32 wp_gpio = get_slot_wp_gpio(slot);	u32 level = GPIO_LEVEL_LOW;	if (wp_gpio)		level = pxa3xx_gpio_get_level(wp_gpio);	//	printk("%s:========================host%d, slot%d wp is %s\n", __FUNCTION__, slot->host->id, slot->id, //			(level == GPIO_LEVEL_HIGH) ? "TRUE":"FALSE");	dbg("host%d, slot%d wp is %s", slot->host->id, slot->id, 			(level == GPIO_LEVEL_HIGH) ? "TRUE":"FALSE");	return (level == GPIO_LEVEL_HIGH);}/* *  return value: 0 -- inserted, 1 -- empty. */static int pxa_mss_slot_is_empty(struct mss_slot *slot){	u32 cd_gpio = get_slot_cd_gpio(slot);	if (cd_gpio) {		int empty = (pxa3xx_gpio_get_level(cd_gpio) == GPIO_LEVEL_HIGH);		return empty;		}		return 0;} static int free_sdio_dat1_irq (struct pxa_mss_host *pxa_host){	int irq = IRQ_GPIO(MFP2GPIO(MFP_MMC_DAT1));	free_irq(irq, pxa_host);	pxa_host->dat1_gpio_irq = 0;	pxa3xx_mfp_set_afds(MFP_MMC_DAT1, MFP_MMC_DAT1_AF, MFP_DS03X);	return 0;}static irqreturn_t pxa_sdio_dat1_irq(int irq, void *devid){	struct pxa_mss_host *pxa_host;	pxa_host = (struct pxa_mss_host *)devid;	dbg("%s\n", __func__);	free_sdio_dat1_irq(pxa_host);	pxa_host_start_busclock(pxa_host);	return IRQ_HANDLED;}static int set_sdio_dat1_irq (struct pxa_mss_host *pxa_host){ 	int irq = IRQ_GPIO(MFP2GPIO(MFP_MMC_DAT1));	int ret;	dbg("%s\n", __func__);	if (pxa_host->dat1_gpio_irq) {		printk(KERN_ERR "re-enterance of %s\n", __func__);		return -EFAULT;	}			pxa3xx_mfp_set_afds(MFP_MMC_DAT1, 0, 0);	pxa3xx_gpio_set_direction(MFP_MMC_DAT1, GPIO_DIR_IN);		set_irq_type(irq, IRQT_BOTHEDGE);	ret = request_irq(irq, pxa_sdio_dat1_irq, 0, 		"SDIO DAT1/INT", (void *)pxa_host);	if (ret)		printk(KERN_ERR "SDIO: request irq %d fails, ret: %d\n", 			irq, ret);	else		pxa_host->dat1_gpio_irq = irq;	return ret;}static int pxa3xx_mmc_get_clockrate(struct mss_host *host, int clock){	int ret;	switch (clock) {#if	defined(CONFIG_CPU_PXA300) || defined(CONFIG_CPU_PXA310) 	case 26000000:		ret = CLKRT_26MHZ;		break;#endif	case 19500000:		ret = CLKRT_19_5MHZ;		break;	case  9750000:		ret = CLKRT_9_75MHZ;		break;	case  4875000:		ret = CLKRT_4_88MHZ;		break;	case  2437500:		ret = CLKRT_2_44MHZ;		break;	case  1218750:		ret = CLKRT_1_22MHZ;		break;	case   609375:		ret = CLKRT_0_609MHZ;		break;	case   304000:		ret = CLKRT_0_304MHZ;		break;	default:		ret = -EINVAL;		break;	}	return ret;}static void pxa_mss_set_ios(struct mss_host *host, struct mss_ios *ios){	unsigned int clockrate;	struct pxa_mss_host *pxa_host;       	pxa_host = host->private;		dbg("clock now %d, to set %d, CLKRT:%x", host->ios.clock, ios->clock, 			readl(pxa_host->base + MMC_CLKRT));	if (ios->clock != host->ios.clock) {#if	defined(CONFIG_CPU_PXA300) || defined(CONFIG_CPU_PXA310) 		if (ios->clock >= 26000000) {			host->ios.clock = 26000000;		}		else if (ios->clock >= 19500000)#else		if (ios->clock >= 19500000)#endif		{			host->ios.clock = 19500000;		}		else if (ios->clock >= 9750000) {			host->ios.clock = 9750000;		}		else if (ios->clock >= 4875000) {			host->ios.clock = 4875000;		}		else if (ios->clock >= 2437500) {			host->ios.clock = 2437500;		}			else if (ios->clock >= 1218750) {			host->ios.clock = 1218750;		}		else if (ios->clock >= 609375){			host->ios.clock = 609375;		}			else if ((ios->clock != MSS_CLOCK_START) &&			 (ios->clock != MSS_CLOCK_STOP)) {			host->ios.clock = 304000;		}		clockrate = pxa3xx_mmc_get_clockrate(host, host->ios.clock);		pxa_host_stop_busclock(pxa_host);		if (ios->clock != MSS_CLOCK_STOP) {			if (pxa_host->dat1_gpio_irq)				free_sdio_dat1_irq(pxa_host);			writel(clockrate, pxa_host->base + MMC_CLKRT);			pxa_host_start_busclock(pxa_host);		} else if (host->active_card->card_type == MSS_SDIO_CARD) {			/* enable the dat1 interrupt by setting to GPIO edge			 * detion interrupt to detect interrupts, only 			 * functional when MMC_CLK is stopped */			if (ios->dat1_gpio_irq_en)				set_sdio_dat1_irq(pxa_host);		}	}	host->ios.bus_width = ios->bus_width;	if (host->ios.bus_width > host->bus_width)

⌨️ 快捷键说明

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