📄 mhn_controller.c
字号:
/* * mhn_controller.c - MMC/SD/SDIO Controller driver * * Copyright (C) 2006 Intel Corporation * * 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 * *(C) Copyright 2006 Marvell International Ltd. * All Rights Reserved */#include <linux/config.h>#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/interrupt.h>#include <linux/blkdev.h>#include <linux/dma-mapping.h>#include <linux/wait.h>#include <linux/suspend.h>#include <asm/uaccess.h>#include <asm/hardware.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/irq.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/cpu-freq-voltage-mhn.h>#include <asm/arch/mhn_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 "mhn_controller.h"#define PXA_SLOT_SCAN 1#define PXA_SLOT_EXIT 2struct pxa_slot { u32 cd_irq; u32 cd_gpio; u32 wp_gpio;#if 0 struct completion thread_complete; struct semaphore thread_sem; wait_queue_head_t thread_wq; int flags;#endif struct work_struct sdio_int; struct work_struct card_change;};#if defined(CONFIG_CPU_MONAHANS_P) || \ defined(CONFIG_CPU_MONAHANS_PL) || \ defined(CONFIG_CPU_MONAHANS_L)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_MONAHANS_LV)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_INFO "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) { mhn_mfp_set_afds(MFP_MMC_CMD_0, MFP_AF4, MFP_DS03X); /* set to GPIO output high for CMD_1 pin */ mhn_mfp_set_afds(MFP_MMC_CMD_1, MFP_AF0, MFP_DS03X); mhn_gpio_set_direction(MFP_MMC_CMD_1, GPIO_DIR_OUT); mhn_gpio_set_level(MFP_MMC_CMD_1, GPIO_LEVEL_HIGH); } else if (slot->id == 1) { mhn_mfp_set_afds(MFP_MMC_CMD_1, MFP_MMC_CMD_1_AF, MFP_DS03X); /* set to GPIO output high for CMD_0 pin */ mhn_mfp_set_afds(MFP_MMC_CMD_0, MFP_AF0, MFP_DS03X); mhn_gpio_set_direction(MFP_MMC_CMD_0, GPIO_DIR_OUT); mhn_gpio_set_level(MFP_MMC_CMD_0, GPIO_LEVEL_HIGH); } else BUG(); } else if(slot->host->id == PXA_MMC_2) { mhn_mfp_set_afds(MFP_MMC2_CMD, MFP_AF4, MFP_DS03X); }#if defined(CONFIG_CPU_MONAHANS_LV) else if(slot->host->id == PXA_MMC_3) { mhn_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) { mhn_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 */ mhn_mfp_set_afds(MFP_MMC_CMD_0, MFP_AF0, MFP_DS03X); mhn_gpio_set_direction(MFP_MMC_CMD_0, GPIO_DIR_OUT); mhn_gpio_set_level(MFP_MMC_CMD_0, GPIO_LEVEL_HIGH); } else BUG(); } else if(slot->host->id == PXA_MMC_2) { mhn_mfp_set_afds(MFP_MMC2_CMD, MFP_AF4, MFP_DS03X); }#if defined(CONFIG_CPU_MONAHANS_LV) else if(slot->host->id == PXA_MMC_3) { mhn_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 = mhn_gpio_get_level(wp_gpio); 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 = (mhn_gpio_get_level(cd_gpio) == GPIO_LEVEL_HIGH); return empty; } return 0;}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_MONAHANS_L) || defined(CONFIG_CPU_MONAHANS_LV) if (ios->clock >= 26000000) { host->ios.clock = 26000000; clockrate = CLKRT_26MHZ; } else if (ios->clock >= 19500000)#else if (ios->clock >= 19500000)#endif { host->ios.clock = 19500000; clockrate = CLKRT_19_5MHZ; } else if (ios->clock >= 9750000) { host->ios.clock = 9750000; clockrate = CLKRT_9_75MHZ; } else if (ios->clock >= 4875000) { host->ios.clock = 4875000; clockrate = CLKRT_4_88MHZ; } else if (ios->clock >= 2437500) { host->ios.clock = 2437500; clockrate = CLKRT_2_44MHZ; } else if (ios->clock >= 1218750) { host->ios.clock = 1218750; clockrate = CLKRT_1_22MHZ; } else if (ios->clock >= 609375){ host->ios.clock = 609375; clockrate = CLKRT_0_609MHZ; } else { host->ios.clock = 304000; clockrate = CLKRT_0_304MHZ; } pxa_host_stop_busclock(pxa_host); writel(clockrate, pxa_host->base + MMC_CLKRT); pxa_host_start_busclock(pxa_host); } host->ios.bus_width = ios->bus_width; if (host->ios.bus_width > host->bus_width) host->ios.bus_width = host->bus_width;}#ifdef CONFIG_DVFMstatic int mhn_mmc_dvfm_notifier(unsigned cmd, void *client_data, void *info){ struct pxa_mss_host *pxa_host = (struct pxa_mss_host *)client_data; switch (cmd) { case FV_NOTIFIER_QUERY_SET : if (pxa_host->dma_run) return -1; break; case FV_NOTIFIER_PRE_SET : break; case FV_NOTIFIER_POST_SET : break; } return 0;}#endif/* * send command by setting CMDAT reg of MMC/SD/SDIO controller. * Note: to hide info about SD/SDIO. */static void pxa_mss_handle_request(struct mss_host *host, struct mss_ll_request *llreq){ struct pxa_mss_host *pxa_host = host->private; struct mss_cmd *cmd; struct mss_card *card = host->active_card; u32 cmd_dat = 0; dbg("pxammc %d acitive slot %x\n", host->id + 1, (pxa_host->active_slot) ? pxa_host->active_slot->id : -1); if (pxa_host->active_slot != card->slot) { dbg("pxammc %d change acitive slot to %d\n", host->id + 1, card->slot->id); pxa_mss_slot_select(card->slot); pxa_host->active_slot = card->slot; } pxa_host->cmd = llreq->cmd;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -