📄 pxa3xx_controller.c
字号:
/* * 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 + -