/*++
linux/drivers/mmc/host/mmc_atsmb.c
Copyright (c) 2008 WonderMedia Technologies, Inc.
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, see .
WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mmc_atsmb.h"
//#include
#include
#include
#include
#include
int enable_wifi_irq = 1;
#define MMC2_SDIO_EXT_IRQ (1) /* use sdio ext irq or not */
#if MMC2_SDIO_EXT_IRQ
static int is_mtk6620 = 0;
static unsigned int mmc2_sdio_ext_irq = 0;
static unsigned long mmc2_sdio_ext_irq_flags = 0;
enum ext_irq_flag {
IN_SUSPEND = 0,
IRQ_IN_SUSPEND = 1,
};
int wmt_mtk6620_intr=0xf; //gpio 15
#endif
static int is_nmc1000 = 0x0;
static int is_ap6330 = 0;
int set_bus_dead = 1;
#define mprintk
#if 0
#define DBG(host, fmt, args...) \
pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args)
#endif
#define ATSMB_TIMEOUT_TIME (HZ*2)
//add by jay,for modules support
static u64 wmt_sdmmc2_dma_mask = 0xffffffffUL;
static struct resource wmt_sdmmc2_resources[] = {
[0] = {
.start = SD2_SDIO_MMC_BASE_ADDR,
.end = (SD2_SDIO_MMC_BASE_ADDR + 0x3FF),
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDC2,
.end = IRQ_SDC2,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_SDC2_DMA,
.end = IRQ_SDC2_DMA,
.flags = IORESOURCE_IRQ,
},
/*2008/10/6 RichardHsu-s*/
[3] = {
.start = IRQ_PMC_WAKEUP,
.end = IRQ_PMC_WAKEUP,
.flags = IORESOURCE_IRQ,
},
/*2008/10/6 RichardHsu-e*/
};
struct kobject *atsmb2_kobj;
struct mmc_host *mmc2_host_attr = NULL;
//
static void atsmb2_release(struct device * dev) {}
/*#ifdef CONFIG_MMC_DEBUG*/
#define MORE_INFO
#if 0
#define DBG(x...) printk(KERN_ALERT x)
#define DBGR(x...) printk(KERN_ALERT x)
#else
#define DBG(x...) do { } while (0)
#define DBGR(x...) do { } while (0)
#endif
/* when Read CRC error occur, and clear read CRC error by software reset.*/
void atsmb2_copy_reg(int direct)
{
static u8 CTL, CMD_IDX, RSP_TYPE, BUS_MODE, INT_MASK_0, INT_MASK_1, SD_STS_0, SD_STS_1, SD_STS_2, SD_STS_3;
static u8 EXT_BUS_MODE, EXT_CTL_1, EXT_CTL_2, MAN_TUNE_VAL, SD_WRI_TUNE;
static u8 RSP_0, RSP_1, RSP_2, RSP_3, RSP_4, RSP_5, RSP_6, RSP_7;
static u8 RSP_8, RSP_9, RSP_10, RSP_11, RSP_12, RSP_13, RSP_14, RSP_15;
static u8 RSP_TOUT, CLK_SEL, EXT_CTL;
static u16 BLK_LEN, BLK_CNT, SHDW_BLKLEN, TIMER_VAL, CTL2;
static u32 CMD_ARG, CURBLK_CNT;
/*direct 0: copy register to memory; 1: copy memory to register.*/
if (direct == 0) {
CTL = *ATSMB2_CTL;
CMD_IDX = *ATSMB2_CMD_IDX;
RSP_TYPE = *ATSMB2_RSP_TYPE;
CMD_ARG = *ATSMB2_CMD_ARG;
BUS_MODE = *ATSMB2_BUS_MODE;
EXT_BUS_MODE = *ATSMB2_EXT_BUS_MODE;
CTL2 = *ATSMB2_CTL2;
BLK_LEN = *ATSMB2_BLK_LEN;
BLK_CNT = *ATSMB2_BLK_CNT;
RSP_0 = *ATSMB2_RSP_0;
RSP_1 = *ATSMB2_RSP_1;
RSP_2 = *ATSMB2_RSP_2;
RSP_3 = *ATSMB2_RSP_3;
RSP_4 = *ATSMB2_RSP_4;
RSP_5 = *ATSMB2_RSP_5;
RSP_6 = *ATSMB2_RSP_6;
RSP_7 = *ATSMB2_RSP_7;
RSP_8 = *ATSMB2_RSP_8;
RSP_9 = *ATSMB2_RSP_9;
RSP_10 = *ATSMB2_RSP_10;
RSP_11 = *ATSMB2_RSP_11;
RSP_12 = *ATSMB2_RSP_12;
RSP_13 = *ATSMB2_RSP_13;
RSP_14 = *ATSMB2_RSP_14;
RSP_15 = *ATSMB2_RSP_15;
CURBLK_CNT = *ATSMB2_CURBLK_CNT;
INT_MASK_0 = *ATSMB2_INT_MASK_0;
INT_MASK_1 = *ATSMB2_INT_MASK_1;
SD_STS_0 = *ATSMB2_SD_STS_0;
SD_STS_1 = *ATSMB2_SD_STS_1;
SD_STS_2 = *ATSMB2_SD_STS_2;
SD_STS_3 = *ATSMB2_SD_STS_3;
RSP_TOUT = *ATSMB2_RSP_TOUT;
CLK_SEL = *ATSMB2_CLK_SEL;
EXT_CTL = *ATSMB2_EXT_CTL;
EXT_CTL_1 = *ATSMB2_EXT_CTL_1;
EXT_CTL_2 = *ATSMB2_EXT_CTL_2;
SHDW_BLKLEN = *ATSMB2_SHDW_BLKLEN;
MAN_TUNE_VAL = *ATSMB2_MAN_TUNE_VAL;
SD_WRI_TUNE = *ATSMB2_SD_WRI_TUNE;
TIMER_VAL = *ATSMB2_TIMER_VAL;
} else {
*ATSMB2_CTL = CTL;
*ATSMB2_CMD_IDX = CMD_IDX;
*ATSMB2_RSP_TYPE = RSP_TYPE;
*ATSMB2_CMD_ARG = CMD_ARG;
*ATSMB2_BUS_MODE = BUS_MODE;
*ATSMB2_EXT_BUS_MODE = EXT_BUS_MODE;
*ATSMB2_CTL2 = CTL2;
*ATSMB2_BLK_LEN = BLK_LEN;
*ATSMB2_BLK_CNT = BLK_CNT;
*ATSMB2_RSP_0 = RSP_0;
*ATSMB2_RSP_1 = RSP_1;
*ATSMB2_RSP_2 = RSP_2;
*ATSMB2_RSP_3 = RSP_3;
*ATSMB2_RSP_4 = RSP_4;
*ATSMB2_RSP_5 = RSP_5;
*ATSMB2_RSP_6 = RSP_6;
*ATSMB2_RSP_7 = RSP_7;
*ATSMB2_RSP_8 = RSP_8;
*ATSMB2_RSP_9 = RSP_9;
*ATSMB2_RSP_10 = RSP_10;
*ATSMB2_RSP_11 = RSP_11;
*ATSMB2_RSP_12 = RSP_12;
*ATSMB2_RSP_13 = RSP_13;
*ATSMB2_RSP_14 = RSP_14;
*ATSMB2_RSP_15 = RSP_15;
*ATSMB2_CURBLK_CNT = CURBLK_CNT;
*ATSMB2_INT_MASK_0 = INT_MASK_0;
*ATSMB2_INT_MASK_1 = INT_MASK_1;
*ATSMB2_SD_STS_0 = SD_STS_0;
*ATSMB2_SD_STS_1 = SD_STS_1;
*ATSMB2_SD_STS_2 = SD_STS_2;
*ATSMB2_SD_STS_3 = SD_STS_3;
*ATSMB2_RSP_TOUT = RSP_TOUT;
*ATSMB2_CLK_SEL = CLK_SEL;
*ATSMB2_EXT_CTL = EXT_CTL;
*ATSMB2_EXT_CTL_1 = EXT_CTL_1;
*ATSMB2_EXT_CTL_2 = EXT_CTL_2;
*ATSMB2_SHDW_BLKLEN = SHDW_BLKLEN;
*ATSMB2_MAN_TUNE_VAL = MAN_TUNE_VAL;
*ATSMB2_SD_WRI_TUNE = SD_WRI_TUNE;
*ATSMB2_TIMER_VAL = TIMER_VAL;
}
}
#if 0
void atsmb2_dump_reg(struct atsmb_host *host)
{
u8 CTL, CMD_IDX, RSP_TYPE, BUS_MODE, INT_MASK_0, INT_MASK_1, SD_STS_0, SD_STS_1, SD_STS_2, SD_STS_3;
u8 EXT_BUS_MODE, EXT_CTL_1, EXT_CTL_2, MAN_TUNE_VAL, SD_WRI_TUNE;
u8 RSP_0, RSP_1, RSP_2, RSP_3, RSP_4, RSP_5, RSP_6, RSP_7;
u8 RSP_8, RSP_9, RSP_10, RSP_11, RSP_12, RSP_13, RSP_14, RSP_15;
u8 RSP_TOUT, CLK_SEL, EXT_CTL;
u16 BLK_LEN, BLK_CNT, SHDW_BLKLEN, TIMER_VAL, CTL2;
u32 CMD_ARG, PDMA_GCR, PDMA_IER, PDMA_ISR, PDMA_DESPR, PDMA_RBR, PDMA_DAR, PDMA_BAR, PDMA_CPR, PDMA_CCR;
u32 CURBLK_CNT;
CTL = *ATSMB2_CTL;
CMD_IDX = *ATSMB2_CMD_IDX;
RSP_TYPE = *ATSMB2_RSP_TYPE;
CMD_ARG = *ATSMB2_CMD_ARG;
BUS_MODE = *ATSMB2_BUS_MODE;
EXT_BUS_MODE = *ATSMB2_EXT_BUS_MODE;
CTL2 = *ATSMB2_CTL2;
BLK_LEN = *ATSMB2_BLK_LEN;
BLK_CNT = *ATSMB2_BLK_CNT;
RSP_0 = *ATSMB2_RSP_0;
RSP_1 = *ATSMB2_RSP_1;
RSP_2 = *ATSMB2_RSP_2;
RSP_3 = *ATSMB2_RSP_3;
RSP_4 = *ATSMB2_RSP_4;
RSP_5 = *ATSMB2_RSP_5;
RSP_6 = *ATSMB2_RSP_6;
RSP_7 = *ATSMB2_RSP_7;
RSP_8 = *ATSMB2_RSP_8;
RSP_9 = *ATSMB2_RSP_9;
RSP_10 = *ATSMB2_RSP_10;
RSP_11 = *ATSMB2_RSP_11;
RSP_12 = *ATSMB2_RSP_12;
RSP_13 = *ATSMB2_RSP_13;
RSP_14 = *ATSMB2_RSP_14;
RSP_15 = *ATSMB2_RSP_15;
CURBLK_CNT = *ATSMB2_CURBLK_CNT;
INT_MASK_0 = *ATSMB2_INT_MASK_0;
INT_MASK_1 = *ATSMB2_INT_MASK_1;
SD_STS_0 = *ATSMB2_SD_STS_0;
SD_STS_1 = *ATSMB2_SD_STS_1;
SD_STS_2 = *ATSMB2_SD_STS_2;
SD_STS_3 = *ATSMB2_SD_STS_3;
RSP_TOUT = *ATSMB2_RSP_TOUT;
CLK_SEL = *ATSMB2_CLK_SEL;
EXT_CTL = *ATSMB2_EXT_CTL;
EXT_CTL_1 = *ATSMB2_EXT_CTL_1;
EXT_CTL_2 = *ATSMB2_EXT_CTL_2;
SHDW_BLKLEN = *ATSMB2_SHDW_BLKLEN;
MAN_TUNE_VAL = *ATSMB2_MAN_TUNE_VAL;
SD_WRI_TUNE = *ATSMB2_SD_WRI_TUNE;
TIMER_VAL = *ATSMB2_TIMER_VAL;
PDMA_GCR = *ATSMB2_PDMA_GCR;
PDMA_IER = *ATSMB2_PDMA_IER;
PDMA_ISR = *ATSMB2_PDMA_ISR;
PDMA_DESPR = *ATSMB2_PDMA_DESPR;
PDMA_RBR = *ATSMB2_PDMA_RBR;
PDMA_DAR = *ATSMB2_PDMA_DAR;
PDMA_BAR = *ATSMB2_PDMA_BAR;
PDMA_CPR = *ATSMB2_PDMA_CPR;
PDMA_CCR = *ATSMB2_PDMA_CCR;
DBGR("\n+---------------------------Registers----------------------------+\n");
DBGR("%16s = 0x%8x |", "CTL", CTL);
DBGR("%16s = 0x%8x\n", "CMD_IDX", CMD_IDX);
DBGR("%16s = 0x%8x |", "RSP_TYPE", RSP_TYPE);
DBGR("%16s = 0x%8x\n", "CMD_ARG", CMD_ARG);
DBGR("%16s = 0x%8x |", "BUS_MODE", BUS_MODE);
DBGR("%16s = 0x%8x\n", "EXT_BUS_MODE", EXT_BUS_MODE);
DBGR("%16s = 0x%8x |", "CTL2", CTL2);
DBGR("%16s = 0x%8x\n", "BLK_LEN", BLK_LEN);
DBGR("%16s = 0x%8x |", "BLK_CNT", BLK_CNT);
DBGR("%16s = 0x%8x\n", "RSP_0", RSP_0);
DBGR("%16s = 0x%8x |", "RSP_1", RSP_1);
DBGR("%16s = 0x%8x\n", "RSP_2", RSP_2);
DBGR("%16s = 0x%8x |", "RSP_3", RSP_3);
DBGR("%16s = 0x%8x\n", "RSP_4", RSP_4);
DBGR("%16s = 0x%8x |", "RSP_5", RSP_5);
DBGR("%16s = 0x%8x\n", "RSP_6", RSP_6);
DBGR("%16s = 0x%8x |", "RSP_7", RSP_7);
DBGR("%16s = 0x%8x\n", "RSP_8", RSP_8);
DBGR("%16s = 0x%8x |", "RSP_9", RSP_9);
DBGR("%16s = 0x%8x\n", "RSP_10", RSP_10);
DBGR("%16s = 0x%8x |", "RSP_11", RSP_11);
DBGR("%16s = 0x%8x\n", "RSP_12", RSP_12);
DBGR("%16s = 0x%8x |", "RSP_13", RSP_13);
DBGR("%16s = 0x%8x\n", "RSP_14", RSP_14);
DBGR("%16s = 0x%8x\n", "RSP_15", RSP_15);
DBGR("%16s = 0x%8x |", "CURBLK_CNT", CURBLK_CNT);
DBGR("%16s = 0x%8x\n", "INT_MASK_0", INT_MASK_0);
DBGR("%16s = 0x%8x |", "INT_MASK_1", INT_MASK_1);
DBGR("%16s = 0x%8x\n", "SD_STS_0", SD_STS_0);
DBGR("%16s = 0x%8x |", "SD_STS_1", SD_STS_1);
DBGR("%16s = 0x%8x\n", "SD_STS_2", SD_STS_2);
DBGR("%16s = 0x%8x |", "SD_STS_3", SD_STS_3);
DBGR("%16s = 0x%8x\n", "RSP_TOUT", RSP_TOUT);
DBGR("%16s = 0x%8x |", "CLK_SEL", CLK_SEL);
DBGR("%16s = 0x%8x\n", "EXT_CTL", EXT_CTL);
DBGR("%16s = 0x%8x |", "EXT_CTL_1", EXT_CTL_1);
DBGR("%16s = 0x%8x\n", "EXT_CTL_2", EXT_CTL_2);
DBGR("%16s = 0x%8x |", "SHDW_BLKLEN", SHDW_BLKLEN);
DBGR("%16s = 0x%8x\n", "MAN_TUNE_VAL", MAN_TUNE_VAL);
DBGR("%16s = 0x%8x |", "SD_WRI_TUNE", SD_WRI_TUNE);
DBGR("%16s = 0x%8x\n", "TIMER_VAL", TIMER_VAL);
DBGR("%16s = 0x%8x |", "PDMA_GCR", PDMA_GCR);
DBGR("%16s = 0x%8x\n", "PDMA_IER", PDMA_IER);
DBGR("%16s = 0x%8x |", "PDMA_ISR", PDMA_ISR);
DBGR("%16s = 0x%8x\n", "PDMA_DESPR", PDMA_DESPR);
DBGR("%16s = 0x%8x |", "PDMA_RBR", PDMA_RBR);
DBGR("%16s = 0x%8x\n", "PDMA_DAR", PDMA_DAR);
DBGR("%16s = 0x%8x |", "PDMA_BAR", PDMA_BAR);
DBGR("%16s = 0x%8x\n", "PDMA_CPR", PDMA_CPR);
DBGR("%16s = 0x%8x |", "PDMA_CCR", PDMA_CCR);
DBGR("\n+----------------------------------------------------------------+\n");
}
#else
void atsmb2_dump_reg(struct atsmb_host *host) {}
#endif
unsigned int fmax2 = 515633;
unsigned int MMC2_DRIVER_VERSION;
int SD2_function = 0; /*0: normal SD/MMC card reader , 1: internal SDIO wifi*/
int SDXC2_function;
int SCC2_ID(void){
unsigned short val;
val = REG16_VAL(SYSTEM_CFG_CTRL_BASE_ADDR + 0x2);
return val;
}
int get_chip_version2(void) /*2008/05/01 janshiue modify for A1 chip*/
{
u32 tmp;
tmp = REG32_VAL(SYSTEM_CFG_CTRL_BASE_ADDR);
tmp = ((tmp & 0xF00) >> 4) + 0x90 + ((tmp & 0xFF) - 1);
return tmp;
}
void get_driver_version2(void)
{
if (SCC2_ID() == 0x3426) {
if (get_chip_version2() < 0xA1)
MMC2_DRIVER_VERSION = MMC_DRV_3426_A0;
else if (get_chip_version2() == 0xA1)
MMC2_DRIVER_VERSION = MMC_DRV_3426_A1;
else
MMC2_DRIVER_VERSION = MMC_DRV_3426_A2;
} else if (SCC2_ID() == 0x3437) {
if (get_chip_version2() < 0xA1)
MMC2_DRIVER_VERSION = MMC_DRV_3437_A0;
else
MMC2_DRIVER_VERSION = MMC_DRV_3437_A1;
} else if (SCC2_ID() == 0x3429) {
MMC2_DRIVER_VERSION = MMC_DRV_3429;
} else if (SCC2_ID() == 0x3451) {
if (get_chip_version2() < 0xA1)
MMC2_DRIVER_VERSION = MMC_DRV_3451_A0;
} else if (SCC2_ID() == 0x3465) {
MMC2_DRIVER_VERSION = MMC_DRV_3465;
} else if (SCC2_ID() == 0x3445) {
MMC2_DRIVER_VERSION = MMC_DRV_3445;
} else if (SCC2_ID() == 0x3481) {
MMC2_DRIVER_VERSION = MMC_DRV_3481;
} else if (SCC2_ID() == 0x3498) {
MMC2_DRIVER_VERSION = MMC_DRV_3498;
}
}
/*2008/10/6 RichardHsu-e*/
/**********************************************************************
Name : atsmb2_alloc_desc
Function : To config PDMA descriptor setting.
Calls :
Called by :
Parameter :
Author : Janshiue Wu
History :
***********************************************************************/
static inline int atsmb2_alloc_desc(struct atsmb_host *host,
unsigned int bytes)
{
void *DescPool = NULL;
DBG("[%s] s\n",__func__);
DescPool = dma_alloc_coherent(host->mmc->parent, bytes, &(host->DescPhyAddr), GFP_KERNEL);
if (!DescPool) {
DBG("can't allocal desc pool=%8X %8X\n", DescPool, host->DescPhyAddr);
DBG("[%s] e1\n",__func__);
return -1;
}
DBG("allocal desc pool=%8X %8X\n", DescPool, host->DescPhyAddr);
host->DescVirAddr = (unsigned long *)DescPool;
host->DescSize = bytes;
DBG("[%s] e2\n",__func__);
return 0;
}
/**********************************************************************
Name : atsmb2_config_desc
Function : To config PDMA descriptor setting.
Calls :
Called by :
Parameter :
Author : Janshiue Wu
History :
***********************************************************************/
static inline void atsmb2_config_desc(unsigned long *DescAddr,
unsigned long *BufferAddr,
unsigned long Blk_Cnt)
{
int i = 0 ;
unsigned long *CurDes = DescAddr;
DBG("[%s] s\n",__func__);
/* the first Descriptor store for 1 Block data
* (512 bytes)
*/
for (i = 0 ; i < 3 ; i++) {
atsmb2_init_short_desc(CurDes, 0x80, BufferAddr, 0);
BufferAddr += 0x20;
CurDes += sizeof(struct SD_PDMA_DESC_S)/4;
}
if (Blk_Cnt > 1) {
atsmb2_init_long_desc(CurDes, 0x80, BufferAddr, CurDes + (sizeof(struct SD_PDMA_DESC_L)/4), 0);
BufferAddr += 0x20;
CurDes += sizeof(struct SD_PDMA_DESC_L)/4;
/* the following Descriptor store the rest Blocks data
* (Blk_Cnt - 1) * 512 bytes
*/
atsmb2_init_long_desc(CurDes, (Blk_Cnt - 1) * 512,
BufferAddr, CurDes + (sizeof(struct SD_PDMA_DESC_L)/4), 1);
} else {
atsmb2_init_long_desc(CurDes, 0x80, BufferAddr, CurDes + (sizeof(struct SD_PDMA_DESC_L)/4), 1);
}
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_config_dma
Function : To set des/src address, byte count to transfer, and DMA channel settings,
and DMA ctrl. register.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static inline void atsmb2_config_dma(unsigned long config_dir,
unsigned long dma_mask,
struct atsmb_host *host)
{
DBG("[%s] s\n",__func__);
/* Enable DMA */
*ATSMB2_PDMA_GCR = SD_PDMA_GCR_DMA_EN;
*ATSMB2_PDMA_GCR = SD_PDMA_GCR_SOFTRESET;
*ATSMB2_PDMA_GCR = SD_PDMA_GCR_DMA_EN;
/*open interrupt*/
*ATSMB2_PDMA_IER = SD_PDMA_IER_INT_EN;
/*Make sure host could co-work with DMA*/
*ATSMB2_SD_STS_2 |= ATSMB_CLK_FREEZ_EN;
/*Set timer timeout value*/
/*If clock is 390KHz*/
if (host->current_clock < 400000)
*ATSMB2_TIMER_VAL = 0x200; /*1024*512*(1/390K) second*/
else
*ATSMB2_TIMER_VAL = 0x1fff; /*why not to be 0xffff?*/
/*clear all DMA INT status for safety*/
*ATSMB2_PDMA_ISR |= SD_PDMA_IER_INT_STS;
/* hook desc */
*ATSMB2_PDMA_DESPR = host->DescPhyAddr;
if (config_dir == DMA_CFG_WRITE)
*ATSMB2_PDMA_CCR &= SD_PDMA_CCR_IF_to_peripheral;
else
*ATSMB2_PDMA_CCR |= SD_PDMA_CCR_peripheral_to_IF;
host->DmaIntMask = dma_mask; /*save success event*/
*ATSMB2_PDMA_CCR |= SD_PDMA_CCR_RUN;
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_prep_cmd
Function :
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static inline void atsmb2_prep_cmd(struct atsmb_host *host,
u32 opcode,
u32 arg,
unsigned int flags,
u16 blk_len,
u16 blk_cnt,
unsigned char int_maks_0,
unsigned char int_mask_1,
unsigned char cmd_type,
unsigned char op)
{
unsigned long spl_flags; /* add by th */
DBG("[%s] s\n",__func__);
/*set cmd operation code and arguments.*/
host->opcode = opcode;
*ATSMB2_CMD_IDX = opcode; /* host->opcode is set for further use in ISR.*/
*ATSMB2_CMD_ARG = arg;
#if 0 /* Fixme to support SPI mode, James Tian*/
if ((flags && MMC_RSP_NONE) == MMC_RSP_NONE)
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R0;
else if ((flags && MMC_RSP_R1) == MMC_RSP_R1)
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R1;
else if ((flags && MMC_RSP_R1B) == MMC_RSP_R1B)
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R1b;
else if ((flags && MMC_RSP_R2) == MMC_RSP_R2)
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R2;
else if ((flags && MMC_RSP_R3) == MMC_RSP_R3)
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R3;
else if ((flags && MMC_RSP_R6) == MMC_RSP_R6)
*ATSMB2_RSP_TYPE = ((opcode != SD_SEND_IF_COND) ? ATMSB_TYPE_R6 : ATMSB_TYPE_R7);
else
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R0;
#endif
#if 1
/*set cmd response type*/
switch (flags) {
case MMC_RSP_NONE | MMC_CMD_AC:
case MMC_RSP_NONE | MMC_CMD_BC:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R0;
break;
case MMC_RSP_R1 | MMC_CMD_ADTC:
case MMC_RSP_R1 | MMC_CMD_AC:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R1;
break;
case MMC_RSP_R1B | MMC_CMD_AC:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R1b;
break;
case MMC_RSP_R2 | MMC_CMD_BCR:
case MMC_RSP_R2 | MMC_CMD_AC:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R2;
break;
case MMC_RSP_R3 | MMC_CMD_BCR:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R3;
break;
case MMC_RSP_R6 | MMC_CMD_BCR: /*MMC_RSP_R6 = MMC_RSP_R7.*/
*ATSMB2_RSP_TYPE = ((opcode != SD_SEND_IF_COND) ?
ATMSB_TYPE_R6 : ATMSB_TYPE_R7);
break;
case MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC:
case MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R5;
break;
case MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R4;
break;
default:
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R0;
break;
}
#endif
/*SDIO cmd 52 , 53*/
if ((opcode == SD_IO_RW_DIRECT) ||
(opcode == SD_IO_RW_EXTENDED)) {
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R5;
*ATSMB2_RSP_TYPE |= BIT6;
}
/*SDIO cmd 5*/
if ((opcode == SD_IO_SEND_OP_COND) &&
((flags & (MMC_RSP_PRESENT|
MMC_RSP_136|
MMC_RSP_CRC|
MMC_RSP_BUSY|
MMC_RSP_OPCODE)) == MMC_RSP_R4)) {
*ATSMB2_RSP_TYPE = ATMSB_TYPE_R4;
*ATSMB2_RSP_TYPE |= BIT6;
}
/*reset Response FIFO*/
*ATSMB2_CTL |= 0x08;
/* SD Host enable Clock */
*ATSMB2_BUS_MODE |= ATSMB_CST;
/*Set Cmd-Rsp Timeout to be maximum value.*/
*ATSMB2_RSP_TOUT = 0xFE;
/*clear all status registers for safety*/
*ATSMB2_SD_STS_0 |= 0xff;
*ATSMB2_SD_STS_1 |= 0xff;
*ATSMB2_SD_STS_2 |= 0xff;
//*ATSMB2_SD_STS_2 |= 0x7f;
*ATSMB2_SD_STS_3 |= 0xff;
//set block length and block count for cmd requesting data
*ATSMB2_BLK_LEN &=~(0x07ff);
*ATSMB2_BLK_LEN |= blk_len;
//*ATSMB2_SHDW_BLKLEN = blk_len;
*ATSMB2_BLK_CNT = blk_cnt;
*ATSMB2_INT_MASK_0 |= int_maks_0;
/* spin_lock add by th for fix sdio irq*/
spin_lock_irqsave(&host->lock, spl_flags);
*ATSMB2_INT_MASK_1 |= int_mask_1;
spin_unlock_irqrestore(&host->lock, spl_flags);
//Set Auto stop for Multi-block access
if(cmd_type == 3 || cmd_type == 4)
{
//auto stop command set.
*ATSMB2_EXT_CTL |= 0x01;
/*
* Enable transaction abort.
* When CRC error occurs, CMD 12 would be automatically issued.
* That is why we cannot enable R/W CRC error INTs.
* If we enable CRC error INT, we would handle this INT in ISR and then issue CMD 12 via software.
*/
*ATSMB2_BLK_LEN |= 0x0800;
}
/*Set read or write*/
if (op == 1)
*ATSMB2_CTL &= ~(0x04);
else if (op == 2)
*ATSMB2_CTL |= 0x04;
/*for Non data access command, command type is 0.*/
*ATSMB2_CTL &= 0x0F;
*ATSMB2_CTL |= (cmd_type<<4);
DBG("[%s] e\n",__func__);
}
static inline void atsmb2_issue_cmd(void)
{
*ATSMB2_CTL |= ATSMB_START;
wmb();
}
/**********************************************************************
Name : atsmb2_request_end
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static void
atsmb2_request_end(struct atsmb_host *host, struct mmc_request *mrq)
{
DBG("[%s] s\n",__func__);
/*
* Need to drop the host lock here; mmc_request_done may call
* back into the driver...
*/
//kevin delete spin lock
//spin_unlock(&host->lock);
/*DBG("100");*/
mmc_request_done(host->mmc, mrq);
/*DBG("101\n");*/
//spin_lock(&host->lock);
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_data_done
Function :
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
void atsmb2_wait_done(void *data)
{
struct atsmb_host *host = (struct atsmb_host *) data;
DBG("[%s] s\n",__func__);
WARN_ON(host->done_data == NULL);
complete(host->done_data);
host->done_data = NULL;
host->done = NULL;
DBG("[%s] e\n",__func__);
}
/**********************************************************************
wait_data_busy
waiting for previous data transfer done
**********************************************************************/
void wait_data_busy(struct mmc_command *cmd)
{
int wait_count = 500000;
switch(cmd->opcode) {
case MMC_GO_IDLE_STATE:
case MMC_STOP_TRANSMISSION:
case MMC_SEND_STATUS:
case MMC_GO_INACTIVE_STATE:
break;
default:
do {
if (*ATSMB2_CURBLK_CNT & (0x01<<24))
break;
udelay(1);
} while (--wait_count);
}
}
/**********************************************************************
Name : atsmb2_start_data
Function : If we start data, there must be only four cases.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static void atsmb2_start_data(struct atsmb_host *host)
{
DECLARE_COMPLETION(complete);
unsigned char cmd_type = 0;
unsigned char op = 0; /*0: non-operation; 1:read; 2: write*/
unsigned char mask_0 = 0;
unsigned char mask_1 = 0;
unsigned long dma_mask = 0;
struct mmc_data *data = host->data;
struct mmc_command *cmd = host->cmd;
struct scatterlist *sg = NULL;
unsigned int sg_len = 0;
unsigned int total_blks = 0; /*total block number to transfer*/
u32 card_addr = 0;
unsigned long dma_len = 0;
unsigned long total_dma_len = 0;
dma_addr_t dma_phy = 0; /* physical address used for DMA*/
unsigned int dma_times = 0; /*times dma need to transfer*/
unsigned int dma_loop = 0;
unsigned int sg_num = 0;
int loop_cnt = 10000;
unsigned int sg_transfer_len = 0; /*record each time dma transfer sg length */
DBG("[%s] s\n",__func__);
data->bytes_xfered = 0;
cmd->error = 0;
data->error = 0;
/*for loop*/
sg = data->sg;
sg_len = data->sg_len;
dma_times = (sg_len / MAX_DESC_NUM);
if (sg_len % MAX_DESC_NUM)
dma_times++;
DBG("dma_times = %d sg_len = %d sg = %x\n", dma_times, sg_len, sg);
card_addr = cmd->arg; /*may be it is block-addressed, or byte-addressed.*/
total_blks = data->blocks;
dma_map_sg(&(host->mmc->class_dev), sg, sg_len,
((data->flags)&MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
for (dma_loop = 1 ; dma_loop <= dma_times; dma_loop++, sg_len -= sg_transfer_len) {
DBG("dma_loop = %d sg_len = %d sg_transfer_len = %d\n", dma_loop, sg_len, sg_transfer_len);
if (dma_loop < dma_times)
sg_transfer_len = MAX_DESC_NUM;
else
sg_transfer_len = sg_len;
DBG("sg_transfer_len = %d\n", sg_transfer_len);
/*
*Firstly, check and wait till card is in the transfer state.
*For our hardware, we can not consider
*the card has successfully tranfered its state from data/rcv to trans,
*when auto stop INT occurs.
*/
loop_cnt = 10000;
do {
if (host->cmd->opcode == SD_IO_RW_EXTENDED)
break;
loop_cnt--;
WARN_ON(loop_cnt == 0);
host->done_data = &complete;
host->done = &atsmb2_wait_done;
host->soft_timeout = 1;
atsmb2_prep_cmd(host,
MMC_SEND_STATUS,
(host->mmc->card->rca)<<16,
MMC_RSP_R1 | MMC_CMD_AC,
0,
0,
0, /*mask_0*/
ATSMB_RSP_DONE_EN
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN,
0, /*cmd type*/
0); /*read or write*/
atsmb2_issue_cmd();
DBG("%16s = 0x%8x |", "INT_MASK_1", *ATSMB2_INT_MASK_1);
DBG("%16s = 0x%8x \n", "SD_STS_1", *ATSMB2_SD_STS_1);
/*ISR would completes it.*/
wait_for_completion_timeout(&complete, ATSMB_TIMEOUT_TIME);
WARN_ON(host->soft_timeout == 1);
if (host->soft_timeout == 1) {
DBG("%s soft_timeout.\n", __func__);
atsmb2_dump_reg(host);
}
if (cmd->error != MMC_ERR_NONE) {
cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("Getting Status failed.\n");
goto end;
}
} while ((cmd->resp[0] & 0x1f00) != 0x900 && loop_cnt > 0); /*wait for trans state.*/
/*
* Now, we can safely issue read/write command.
* We can not consider this request as multi-block acess or single one via opcode,
* as request is splitted into sgs.
* Some sgs may be single one, some sgs may be multi one.
*/
dma_phy = sg_dma_address(sg);
dma_len = sg_dma_len(sg);
DBG("dma_len = %d data->blksz = %d sg_len = %d\n", dma_len, data->blksz, sg_len);
/*SDIO read/write*/
if (host->cmd->opcode == SD_IO_RW_EXTENDED) {
/*Single Block read/write*/
if ((dma_len / (data->blksz)) == 1 && (sg_len == 1)) {
/* read operation*/
if (data->flags & MMC_DATA_READ) {
host->opcode = SD_IO_RW_EXTENDED;
cmd_type = 2;
op = 1;
mask_0 = 0; /*BLOCK_XFER_DONE INT skipped, we use DMA TC INT*/
mask_1 = (//ATSMB_SDIO_EN
ATSMB_READ_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = SD_PDMA_CCR_Evt_success;
DBG("[%s]SR opcode = %d type = 0x%x op = 0x%x m_0 = 0x%x m_1 = 0x%x d_m = 0x%x\n",
__func__,host->opcode,cmd_type,op,mask_0,mask_1,dma_mask);
} else {
/* write operation*/
host->opcode = SD_IO_RW_EXTENDED;
cmd_type = 1;
op = 2;
/*====That is what we want===== DMA TC INT skipped*/
mask_0 = ATSMB_BLOCK_XFER_DONE_EN;
mask_1 = (//ATSMB_SDIO_EN
ATSMB_WRITE_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = 0;
DBG("[%s]SW opcode = %d type = 0x%x op = 0x%x m_0 = 0x%x m_1 = 0x%x d_m = 0x%x\n",
__func__,host->opcode,cmd_type,op,mask_0,mask_1,dma_mask);
}
} else {
/*Multiple Block read/write*/
/* read operation*/
if (data->flags & MMC_DATA_READ) {
host->opcode = SD_IO_RW_EXTENDED;
cmd_type = 6;
op = 1;
mask_0 = 0; /*MULTI_XFER_DONE_EN skipped*/
mask_1 = (//ATSMB_SDIO_EN /*====That is what we want=====*/
ATSMB_READ_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = SD_PDMA_CCR_Evt_success;
DBG("[%s]MR opcode = %d type = 0x%x op = 0x%x m_0 = 0x%x m_1 = 0x%x d_m = 0x%x\n",
__func__,host->opcode,cmd_type,op,mask_0,mask_1,dma_mask);
} else {
/* write operation*/
host->opcode = SD_IO_RW_EXTENDED;
cmd_type = 5;
op = 2;
mask_0 = ATSMB_MULTI_XFER_DONE_EN;//ATSMB_BLOCK_XFER_DONE_EN; /*MULTI_XFER_DONE INT skipped*/
mask_1 = (//ATSMB_SDIO_EN /*====That is what we want=====*/
ATSMB_WRITE_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = 0;
DBG("[%s]MR opcode = %d type = 0x%x op = 0x%x m_0 = 0x%x m_1 = 0x%x d_m = 0x%x\n",
__func__,host->opcode,cmd_type,op,mask_0,mask_1,dma_mask);
}
}
} else {
if ((dma_len / (data->blksz)) == 1 && (sg_len == 1)) {
if (data->flags & MMC_DATA_READ) {/* read operation*/
host->opcode = MMC_READ_SINGLE_BLOCK;
cmd_type = 2;
op = 1;
mask_0 = 0; /*BLOCK_XFER_DONE INT skipped, we use DMA TC INT*/
mask_1 = (ATSMB_READ_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = SD_PDMA_CCR_Evt_success;
} else {/*write operation*/
host->opcode = MMC_WRITE_BLOCK;
cmd_type = 1;
op = 2;
/*====That is what we want===== DMA TC INT skipped*/
mask_0 = ATSMB_BLOCK_XFER_DONE_EN;
mask_1 = (ATSMB_WRITE_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = 0;
}
} else { /*more than one*/
if (data->flags&MMC_DATA_READ) {/* read operation*/
host->opcode = MMC_READ_MULTIPLE_BLOCK;
cmd_type = 4;
op = 1;
mask_0 = 0; /*MULTI_XFER_DONE_EN skipped*/
mask_1 = (ATSMB_AUTO_STOP_EN /*====That is what we want=====*/
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = 0;
} else {/*write operation*/
host->opcode = MMC_WRITE_MULTIPLE_BLOCK;
cmd_type = 3;
op = 2;
mask_0 = 0; /*MULTI_XFER_DONE INT skipped*/
mask_1 = (ATSMB_AUTO_STOP_EN /*====That is what we want=====*/
|ATSMB_DATA_TIMEOUT_EN
/*Data Timeout and CRC error */
|ATSMB_RSP_CRC_ERR_EN
|ATSMB_RSP_TIMEOUT_EN);
/*Resp Timeout and CRC error */
dma_mask = 0;
}
}
}
/*To controller every sg done*/
host->done_data = &complete;
host->done = &atsmb2_wait_done;
/*sleep till ISR wakes us*/
host->soft_timeout = 1; /*If INT comes early than software timer, it would be cleared.*/
total_dma_len = 0;
DBG("host->DescVirAddr = %x host->DescSize=%x\n", host->DescVirAddr, host->DescSize);
memset(host->DescVirAddr, 0, host->DescSize);
for (sg_num = 0 ; sg_num < sg_transfer_len ; sg++, sg_num++) {
/*
* Now, we can safely issue read/write command.
* We can not consider this request as multi-block acess or single one via opcode,
* as request is splitted into sgs.
* Some sgs may be single one, some sgs may be multi one.
*/
dma_phy = sg_dma_address(sg);
dma_len = sg_dma_len(sg);
total_dma_len = total_dma_len + dma_len;
DBG("sg_num=%d sg_transfer_len=%d sg=%x sg_len=%d total_dma_len=%d dma_len=%d\n",
sg_num, sg_transfer_len, sg, sg_len, total_dma_len, dma_len);
/*2009/01/15 janshiue add*/
if (sg_num < sg_transfer_len - 1) {
/* means the last descporitor */
atsmb2_init_short_desc(
host->DescVirAddr + (sg_num * sizeof(struct SD_PDMA_DESC_S)/4),
dma_len, (unsigned long *)dma_phy, 0);
} else {
atsmb2_init_short_desc(
host->DescVirAddr + (sg_num * sizeof(struct SD_PDMA_DESC_S)/4),
dma_len, (unsigned long *)dma_phy, 1);
}
/*2009/01/15 janshiue add*/
}
/*operate our hardware*/
atsmb2_prep_cmd(host,
host->opcode,
/*arg, may be byte addressed, may be block addressed.*/
card_addr,
cmd->flags,
data->blksz - 1, /* in fact, it is useless.*/
/* for single one, it is useless. but for multi one, */
/* it would be used to tell auto stop function whether it is done.*/
total_dma_len/(data->blksz),
mask_0,
mask_1,
cmd_type,
op);
atsmb2_config_dma((op == 1) ? DMA_CFG_READ : DMA_CFG_WRITE,
dma_mask,
host);
wait_data_busy(cmd);
//sync from tp,fix broadcom 6181 wifi dongle crash.
//udelay(10);
atsmb2_issue_cmd();
wait_for_completion_timeout(&complete,
ATSMB_TIMEOUT_TIME*sg_transfer_len); /*ISR would completes it.*/
/* When the address of request plus length equal card bound,
* force this stop command response as pass. Eason 2012/4/20 */
if (cmd->resp[0] == 0x80000b00) {
/*This caes used for SD2.0 and after MMC4.1 version*/
if (card_addr+(total_dma_len/data->blksz) == host->mmc->card->csd.capacity) {
cmd->resp[0] = 0x00000b00;
/*printk("card_addr = %08X, card_length = %08X,card capacity = %08X\n",
card_addr,(total_dma_len/data->blksz),host->mmc->card->csd.capacity);
printk("card_resp[0]=%08X, addr = %08X\n",cmd->resp[0],cmd->resp);*/
}
/* This caes used for SD1.0 and before MMC 4.1 */
if ((card_addr/data->blksz)+(total_dma_len/data->blksz) == host->mmc->card->csd.capacity) {
cmd->resp[0] = 0x00000b00;
/*printk("Eason test: cmd->arg = %08X, data->blksz = %08X, length = %08X\n",
card_addr,data->blksz,(total_dma_len/data->blksz));*/
}
}
if (host->soft_timeout == 1) {
atsmb2_dump_reg(host);
//*ATSMB2_BUS_MODE |= ATSMB_SFTRST;
*ATSMB2_PDMA_GCR = SD_PDMA_GCR_SOFTRESET;
/*disable INT */
*ATSMB2_INT_MASK_0 &= ~(ATSMB_BLOCK_XFER_DONE_EN | ATSMB_MULTI_XFER_DONE_EN);
*ATSMB2_INT_MASK_1 &= ~(ATSMB_WRITE_CRC_ERR_EN|ATSMB_READ_CRC_ERR_EN|ATSMB_RSP_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN|ATSMB_AUTO_STOP_EN|ATSMB_RSP_TIMEOUT_EN|ATSMB_RSP_DONE_EN);
cmd->error = -ETIMEDOUT;
data->error = -ETIMEDOUT;
}
WARN_ON(host->soft_timeout == 1);
/*check everything goes okay or not*/
if (cmd->error != MMC_ERR_NONE
|| data->error != MMC_ERR_NONE) {
DBG("CMD or Data failed error=%X DescVirAddr=%8X dma_phy=%8X dma_mask = %x\n",
cmd->error, host->DescVirAddr, dma_phy, host->DmaIntMask);
goto end;
}
// card_addr += total_dma_len>>(mmc_card_blockaddr(host->mmc->card_selected) ? 9 : 0);
card_addr += total_dma_len>>(mmc_card_blockaddr(host->mmc->card) ? 9 : 0); //zhf: modified by James Tian
data->bytes_xfered += total_dma_len;
}
// dma_unmap_sg(&(host->mmc->class_dev), data->sg, data->sg_len,
// ((data->flags)&MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
WARN_ON(total_blks != (data->bytes_xfered / data->blksz));
host->opcode = 0;
end:
dma_unmap_sg(&(host->mmc->class_dev), data->sg, data->sg_len,
((data->flags)&MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
//kevin delete spin lock
//spin_lock(&host->lock);
atsmb2_request_end(host, host->mrq);
//spin_unlock(&host->lock);
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_cmd_with_data_back
Function :
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static void atsmb2_cmd_with_data_back(struct atsmb_host *host)
{
DECLARE_COMPLETION(complete);
struct scatterlist *sg = NULL;
unsigned int sg_len = 0;
DBG("[%s] s\n",__func__);
/*for loop*/
sg = host->data->sg;
sg_len = host->data->sg_len;
/*To controller every sg done*/
host->done_data = &complete;
host->done = &atsmb2_wait_done;
dma_map_sg(&(host->mmc->class_dev), sg, sg_len, DMA_FROM_DEVICE);
/*2009/01/15 janshiue add*/
memset(host->DescVirAddr, 0, host->DescSize);
atsmb2_init_long_desc(host->DescVirAddr, sg_dma_len(sg), (unsigned long *)sg_dma_address(sg), 0, 1);
/*2009/01/15 janshiue add*/
/*prepare for cmd*/
atsmb2_prep_cmd(host, /*host*/
host->cmd->opcode, /*opcode*/
host->cmd->arg, /*arg*/
host->cmd->flags, /*flags*/
sg_dma_len(sg)-1, /*block length*/
0, /*block size: It looks like useless*/
0, /*int_mask_0*/
(ATSMB_RSP_CRC_ERR_EN|ATSMB_RSP_TIMEOUT_EN
|ATSMB_READ_CRC_ERR_EN |ATSMB_DATA_TIMEOUT_EN), /*int_mask_1*/
2, /*cmd_type*/
1); /*op*/
atsmb2_config_dma(DMA_CFG_READ,
SD_PDMA_CCR_Evt_success,
host);
atsmb2_issue_cmd();
/*ISR would completes it.*/
wait_for_completion_timeout(&complete, ATSMB_TIMEOUT_TIME);
dma_unmap_sg(&(host->mmc->class_dev), host->data->sg, host->data->sg_len,
((host->data->flags)&MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
spin_lock(&host->lock);
atsmb2_request_end(host, host->mrq);
spin_unlock(&host->lock);
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_start_cmd
Function :
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static void atsmb2_start_cmd(struct atsmb_host *host)
{
unsigned char int_mask_0,int_mask_1;
int_mask_0 = 0;
int_mask_1 = ATSMB_RSP_DONE_EN|ATSMB_RSP_CRC_ERR_EN|ATSMB_RSP_TIMEOUT_EN;
DBG("[%s] s\n",__func__);
atsmb2_prep_cmd(host,
host->cmd->opcode,
host->cmd->arg,
host->cmd->flags,
0, /*useless*/
0, /*useless*/
int_mask_0, /*mask_0*/
int_mask_1, /*mask_1*/
0, /*cmd type*/
0); /*read or write*/
atsmb2_issue_cmd();
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_fmt_check_rsp
Function :
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static inline void atsmb2_fmt_check_rsp(struct atsmb_host *host)
{
u8 tmp_resp[4] = {0};
int i, j, k;
DBG("[%s] s\n",__func__);
if (host->cmd->flags != (MMC_RSP_R2 | MMC_CMD_AC)
&& host->cmd->flags != (MMC_RSP_R2 | MMC_CMD_BCR)) {
for (j = 0, k = 1; j < 4; j++, k++)
tmp_resp[j] = *(REG8_PTR(_RSP_0 + MMC_ATSMB2_BASE+k));
host->cmd->resp[0] = (tmp_resp[0] << 24) |
(tmp_resp[1]<<16) | (tmp_resp[2]<<8) | (tmp_resp[3]);
} else {
for (i = 0, k = 1; i < 4; i++) { /*R2 has 4 u32 response.*/
for (j = 0; j < 4; j++) {
if (k < 16)
tmp_resp[j] = *(REG8_PTR(_RSP_0 + MMC_ATSMB2_BASE+k));
else /* k =16*/
tmp_resp[j] = *(ATSMB2_RSP_0);
k++;
}
host->cmd->resp[i] = (tmp_resp[0]<<24) | (tmp_resp[1]<<16) |
(tmp_resp[2]<<8) | (tmp_resp[3]);
}
}
/*
* For the situation that we need response,
* but response registers give us all zeros, we consider this operation timeout.
*/
if (host->cmd->flags != (MMC_RSP_NONE | MMC_CMD_AC)
&& host->cmd->flags != (MMC_RSP_NONE | MMC_CMD_BC)) {
if (host->cmd->resp[0] == 0 && host->cmd->resp[1] == 0
&& host->cmd->resp[2] == 0 && host->cmd->resp[3] == 0) {
host->cmd->error = -ETIMEDOUT; //zhf: modified by James Tian
}
}
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_get_slot_status
Function : Our host only supports one slot.
Calls :
Called by :
Parameter :
returns : 1: in slot; 0: not in slot.
Author : Leo Lee
History :
***********************************************************************/
static int atsmb2_get_slot_status(struct mmc_host *mmc)
{
// struct atsmb_host *host = mmc_priv(mmc);
unsigned char status_0 = 0;
// unsigned long flags;
unsigned long ret = 0;
DBG("[%s] s\n",__func__);
// spin_lock_irqsave(&host->lock, flags); // Vincent Li mark out for CONFIG_PREEMPT_RT
status_0 = *ATSMB2_SD_STS_0;
// spin_unlock_irqrestore(&host->lock, flags); // Vincent Li mark out for CONFIG_PREEMPT_RT
/* after WM3400 A1 ATSMB_CARD_IN_SLOT_GPI = 1 means not in slot*/
if (MMC2_DRIVER_VERSION >= MMC_DRV_3426_A0) {
ret = ((status_0 & ATSMB_CARD_NOT_IN_SLOT_GPI) ? 0 : 1);
DBG("[%s] e1\n",__func__);
return ret;
} else {
ret = ((status_0 & ATSMB_CARD_NOT_IN_SLOT_GPI) ? 1 : 0);
DBG("[%s] e2\n",__func__);
return ret;
}
DBG("[%s] e3\n",__func__);
return 0;
}
/**********************************************************************
Name : atsmb2_init_short_desc
Function :
Calls :
Called by :
Parameter :
Author : Janshiue Wu
History :
***********************************************************************/
int atsmb2_init_short_desc(unsigned long *DescAddr, unsigned int ReqCount, unsigned long *BufferAddr, int End)
{
struct SD_PDMA_DESC_S *CurDes_S;
DBG("[%s] s\n",__func__);
CurDes_S = (struct SD_PDMA_DESC_S *) DescAddr;
CurDes_S->ReqCount = ReqCount;
CurDes_S->i = 0;
CurDes_S->format = 0;
CurDes_S->DataBufferAddr = BufferAddr;
if (End) {
CurDes_S->end = 1;
CurDes_S->i = 1;
}
DBG("[%s] e\n",__func__);
return 0;
}
/**********************************************************************
Name : atsmb2_init_long_desc
Function :
Calls :
Called by :
Parameter :
Author : Janshiue Wu
History :
***********************************************************************/
int atsmb2_init_long_desc(unsigned long *DescAddr, unsigned int ReqCount,
unsigned long *BufferAddr, unsigned long *BranchAddr, int End)
{
struct SD_PDMA_DESC_L *CurDes_L;
DBG("[%s] s\n",__func__);
CurDes_L = (struct SD_PDMA_DESC_L *) DescAddr;
CurDes_L->ReqCount = ReqCount;
CurDes_L->i = 0;
CurDes_L->format = 1;
CurDes_L->DataBufferAddr = BufferAddr;
CurDes_L->BranchAddr = BranchAddr;
if (End) {
CurDes_L->end = 1;
CurDes_L->i = 1;
}
DBG("[%s] e\n",__func__);
return 0;
}
/**********************************************************************
Name : atsmb2_dma_isr
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static irqreturn_t atsmb2_dma_isr(int irq, void *dev_id)
{
struct atsmb_host *host = dev_id;
u8 status_0, status_1, status_2, status_3;
u32 pdma_event_code = 0;
DBG("[%s] s\n",__func__);
disable_irq_nosync(irq);
spin_lock(&host->lock);
/*Get INT status*/
status_0 = *ATSMB2_SD_STS_0;
status_1 = *ATSMB2_SD_STS_1;
status_2 = *ATSMB2_SD_STS_2;
status_3 = *ATSMB2_SD_STS_3;
/* after WM3426 A0 using PDMA */
if (MMC2_DRIVER_VERSION >= MMC_DRV_3426_A0) {
pdma_event_code = *ATSMB2_PDMA_CCR & SD_PDMA_CCR_EvtCode;
/* clear INT status to notify HW clear EventCode*/
*ATSMB2_PDMA_ISR |= SD_PDMA_IER_INT_STS;
/*printk("dma_isr event code = %X\n", *ATSMB2_PDMA_CCR);*/
/*We expect cmd with data sending back or read single block cmd run here.*/
if (pdma_event_code == SD_PDMA_CCR_Evt_success) {
/*means need to update the data->error and cmd->error*/
if (host->DmaIntMask == SD_PDMA_CCR_Evt_success) {
if (host->data != NULL) {
host->data->error = MMC_ERR_NONE;
host->cmd->error = MMC_ERR_NONE;
} else {
DBG("dma_isr2 hodt->data is NULL\n");
/*disable INT*/
*ATSMB2_PDMA_IER &= ~SD_PDMA_IER_INT_EN;
goto out;
}
}
/*else do nothing*/
DBG("dma_isr PDMA OK\n");
/*atsmb2_dump_reg(host);*/
}
/*But unluckily, we should also be prepare for all kinds of error situation.*/
else {
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBGR("** dma_isr PDMA fail** event code = %X\n", *ATSMB2_PDMA_CCR);
atsmb2_dump_reg(host);
}
if (host->DmaIntMask == SD_PDMA_CCR_Evt_success)
atsmb2_fmt_check_rsp(host);
/*disable INT*/
*ATSMB2_PDMA_IER &= ~SD_PDMA_IER_INT_EN;
}
/*wake up some one who is sleeping.*/
if ((pdma_event_code != SD_PDMA_CCR_Evt_success) || (host->DmaIntMask == SD_PDMA_CCR_Evt_success)) {
if (host->done_data) {/* We only use done_data when requesting data.*/
host->soft_timeout = 0;
host->done(host);
} else
atsmb2_request_end(host, host->mrq); /*for cmd with data sending back.*/
}
out:
spin_unlock(&host->lock);
enable_irq(irq);
DBG("[%s] e\n",__func__);
return IRQ_HANDLED;
}
/* add by th for ack interrupt */
void eagle_ack_interrupt(void)
{
*ATSMB2_SD_STS_1 |= ATSMB_SDIO_INT;
}
EXPORT_SYMBOL(eagle_ack_interrupt);
/**********************************************************************
Name : atsmb2_regular_isr
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
//static irqreturn_t atsmb2_regular_isr(int irq, void *dev_id, struct pt_regs *regs)
irqreturn_t atsmb2_regular_isr(int irq, void *dev_id)
{
struct atsmb_host *host = dev_id;
u8 status_0, status_1, status_2, status_3,mask_0,mask_1;
u32 pdma_sts;
DBG("[%s] s\n",__func__);
WARN_ON(host == NULL);
disable_irq_nosync(irq);
spin_lock(&host->lock);
/*Get INT status*/
status_0 = *ATSMB2_SD_STS_0;
status_1 = *ATSMB2_SD_STS_1;
status_2 = *ATSMB2_SD_STS_2;
status_3 = *ATSMB2_SD_STS_3;
mask_0 = *ATSMB2_INT_MASK_0;
mask_1 = *ATSMB2_INT_MASK_1;
/*******************************************************
card insert interrupt
********************************************************/
if ((status_0 & ATSMB_DEVICE_INSERT) /*Status Set and IRQ enabled*/
/*To aviod the situation that we intentionally disable IRQ to do rescan.*/
&& (*ATSMB2_INT_MASK_0 & 0x80)) {
if (host->mmc->ops->get_slot_status(host->mmc)) {
host->mmc->scan_retry = 3;
host->mmc->card_scan_status = false;
} else {
host->mmc->scan_retry = 0;
host->mmc->card_scan_status = false;
}
mmc_detect_change(host->mmc, HZ/2);
/*Taipei Side Request: Disable INSERT IRQ when doing rescan.*/
//*ATSMB2_INT_MASK_0 &= (~0x80);/* or 40?*/ //zhf: marked by James Tian
/*Then we clear the INT status*/
//iowrite32(ATSMB_DEVICE_INSERT, host->base+_SD_STS_0);
*ATSMB2_SD_STS_0 |= ATSMB_DEVICE_INSERT;
spin_unlock(&host->lock);
enable_irq(irq);
DBG("[%s] e1\n",__func__);
return IRQ_HANDLED;
}
if ((status_1 & mask_1)& ATSMB_SDIO_INT) {
spin_unlock(&host->lock);
mmc_signal_sdio_irq(host->mmc);
if (((status_1 & mask_1) == ATSMB_SDIO_INT) && ((status_0 & mask_0) == 0)) {
enable_irq(irq);
DBG("[%s] e2\n",__func__);
return IRQ_HANDLED;
}
spin_lock(&host->lock);
}
pdma_sts = *ATSMB2_PDMA_CCR;
if (((status_0 & mask_0) | (status_1 & mask_1)) == 0) {
spin_unlock(&host->lock);
enable_irq(irq);
DBG("[%s] e3\n",__func__);
return IRQ_HANDLED;
}
/*******************************************************
command interrupt.
*******************************************************/
/*for write single block*/
if (host->opcode == MMC_WRITE_BLOCK) {
if ((status_0 & ATSMB_BLOCK_XFER_DONE)
&& (status_1 & ATSMB_RSP_DONE)) {
host->data->error = MMC_ERR_NONE;
host->cmd->error = MMC_ERR_NONE;
/* okay, what we want.*/
} else {
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err1\n",__func__);
atsmb2_dump_reg(host);
}
} else if (host->opcode == MMC_WRITE_MULTIPLE_BLOCK
|| host->opcode == MMC_READ_MULTIPLE_BLOCK) {
if ((status_1 & (ATSMB_AUTO_STOP|ATSMB_RSP_DONE))
&& (status_0 & ATSMB_MULTI_XFER_DONE)) {
/*If CRC error occurs, I think this INT would not occrs.*/
/*okay, that is what we want.*/
host->data->error = MMC_ERR_NONE;
host->cmd->error = MMC_ERR_NONE;
} else {
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err2\n",__func__);
atsmb2_dump_reg(host);
}
} else if (host->opcode == MMC_READ_SINGLE_BLOCK) {/* we want DMA TC, run here, must be error.*/
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err3\n",__func__);
atsmb2_dump_reg(host);
} else if (host->opcode == SD_IO_RW_EXTENDED){
/*Write operation*/
if (*ATSMB2_CTL & BIT2) {
if ((*ATSMB2_CTL & 0xf0) == 0x10) { /*single block write*/
if ((status_0 & ATSMB_BLOCK_XFER_DONE)
&& (status_1 & ATSMB_RSP_DONE)) {
host->data->error = MMC_ERR_NONE;
host->cmd->error = MMC_ERR_NONE;
/* okay, what we want.*/
} else {
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err4 status_0 = %x status_1 = %x\n",__func__,status_0,status_1);
}
} else if ((*ATSMB2_CTL & 0xf0) == 0x50) {
if ((status_0 & ATSMB_MULTI_XFER_DONE)
&& (status_1 & ATSMB_RSP_DONE)) {
host->data->error = MMC_ERR_NONE;
host->cmd->error = MMC_ERR_NONE;
/* okay, what we want.*/
} else {
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err4-2 status_0 = %x status_1 = %x\n",__func__,status_0,status_1);
}
} else {
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err4-3 status_0 = %x status_1 = %x\n",__func__,status_0,status_1);
}
}
else {
/*Read operation*/
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err5\n",__func__);
}
} else {
/* command, not request data*/
/* the command which need data sending back,*/
/* like switch_function, send_ext_csd, send_scr, send_num_wr_blocks.*/
/* NOTICE: we also send status before reading or writing data, so SEND_STATUS should be excluded.*/
if (host->data && host->opcode != MMC_SEND_STATUS) {
host->data->error = -EIO; //MMC_ERR_FAILED;
host->cmd->error = -EIO; //MMC_ERR_FAILED;
DBG("[%s] err6\n",__func__);
atsmb2_dump_reg(host);
} else { /* Just command, no need data sending back.*/
if (status_1 & ATSMB_RSP_DONE) {
/*Firstly, check data-response is busy or not.*/
if (host->cmd->flags == (MMC_RSP_R1B | MMC_CMD_AC)) {
int i = 10000;
while (status_2 & ATSMB_RSP_BUSY) {
status_2 = *ATSMB2_SD_STS_2;
if (--i == 0)
break;
DBG(" IRQ:Status_2 = %d, busy!\n", status_2);
}
if (i == 0)
printk("[MMC driver] Error :SD data-response always busy!");
}
#if 1
/*for our host, even no card in slot, for SEND_STATUS also returns no error.*/
/*The protocol layer depends on SEND_STATUS to check whether card is in slot or not.*/
/*In fact, we can also avoid this situation by checking the response whether they are all zeros.*/
if (!atsmb2_get_slot_status(host->mmc) && host->opcode == MMC_SEND_STATUS) {
host->cmd->retries = 0; /* No retry.*/
// host->cmd->error = MMC_ERR_INVALID;
host->cmd->error = -EINVAL;
} else
#endif
host->cmd->error = MMC_ERR_NONE;
} else {
if (status_1 & ATSMB_RSP_TIMEOUT) {/* RSP_Timeout .*/
// host->cmd->error = MMC_ERR_TIMEOUT;
host->cmd->error = -ETIMEDOUT;
DBG("[%s] err7\n",__func__);
} else {/*or RSP CRC error*/
// host->cmd->error = MMC_ERR_BADCRC;
host->cmd->error = -EILSEQ;
DBG("[%s] err8\n",__func__);
}
atsmb2_dump_reg(host);
}
}
}
atsmb2_fmt_check_rsp(host);
/*disable INT */
*ATSMB2_INT_MASK_0 &= ~(ATSMB_BLOCK_XFER_DONE_EN | ATSMB_MULTI_XFER_DONE_EN);
*ATSMB2_INT_MASK_1 &= ~(ATSMB_WRITE_CRC_ERR_EN|ATSMB_READ_CRC_ERR_EN|ATSMB_RSP_CRC_ERR_EN
|ATSMB_DATA_TIMEOUT_EN|ATSMB_AUTO_STOP_EN|ATSMB_RSP_TIMEOUT_EN|ATSMB_RSP_DONE_EN);
/*clear INT status. In fact, we will clear again before issuing new command.*/
*ATSMB2_SD_STS_0 |= status_0;
*ATSMB2_SD_STS_1 |= status_1;
//*ATSMB2_SD_STS_1 |= ((status_1 & mask_1) | (status_1 & (~ATSMB_SDIO_INT)));
/* when read CRC error occur, and the status can't write one to clear.
* To clear read CRC error status , can do software reset. This is HW bug. 2013/3/21*/
if ((*ATSMB2_SD_STS_1 & BIT6) == 0x40) {
DBG("[%s] host2 CMD%d Read CRC error occur\n",__func__,host->cmd->opcode);
/* Save SD card register */
atsmb2_copy_reg(0);
/* Software reset */
*ATSMB2_BUS_MODE |= BIT7;
/* restore SD card register */
atsmb2_copy_reg(1);
}
if (*ATSMB2_PDMA_ISR & SD_PDMA_IER_INT_STS)
*ATSMB2_PDMA_ISR |= SD_PDMA_IER_INT_STS;
/*wake up some one who is sleeping.*/
if (host->done_data) { /* We only use done_data when requesting data.*/
host->soft_timeout = 0;
host->done(host);
} else
atsmb2_request_end(host, host->mrq); /*for cmd without data.*/
spin_unlock(&host->lock);
enable_irq(irq);
DBG("[%s] e4\n",__func__);
return IRQ_HANDLED;
}
EXPORT_SYMBOL(atsmb2_regular_isr);
/**********************************************************************
Name : atsmb2_get_ro
Function :.
Calls :
Called by :
Parameter :
returns : 0 : write protection is disabled. 1: write protection is enabled.
Author : Leo Lee
History :
***********************************************************************/
int atsmb2_get_ro(struct mmc_host *mmc)
{
struct atsmb_host *host = mmc_priv(mmc);
unsigned char status_0 = 0;
unsigned long flags;
unsigned long ret = 0;
DBG("[%s] s\n",__func__);
spin_lock_irqsave(&host->lock, flags);
status_0 = *ATSMB2_SD_STS_0;
spin_unlock_irqrestore(&host->lock, flags);
DBG("[%s]\nstatus_0 = 0x%x\n", __func__,status_0);
ret = ((status_0 & ATSMB_WRITE_PROTECT) ? 0 : 1);
DBG("[%s] e\n",__func__);
return ret;
}
/**********************************************************************
Name : atsmb2_dump_host_regs
Function :
Calls :
Called by :
Parameter :
returns :
Author : Leo Lee
History :
***********************************************************************/
void atsmb2_dump_host_regs(struct mmc_host *mmc)
{
struct atsmb_host *host = mmc_priv(mmc);
DBG("[%s] s\n",__func__);
atsmb2_dump_reg(host);
DBG("[%s] e\n",__func__);
}
EXPORT_SYMBOL(atsmb2_dump_host_regs);
#if MMC2_SDIO_EXT_IRQ
static irqreturn_t mtk6620_sdio_ext_irq (int irq, void *dev_id)
{
struct mmc_host *mmc = dev_id;
if(!is_gpio_irqenable(wmt_mtk6620_intr) || !gpio_irqstatus(wmt_mtk6620_intr))
return IRQ_NONE;
smp_rmb();
if (unlikely(test_bit(IN_SUSPEND, &mmc2_sdio_ext_irq_flags))) {
//disable_irq_nosync(mmc2_sdio_ext_irq);
wmt_gpio_mask_irq(wmt_mtk6620_intr);
set_bit(IRQ_IN_SUSPEND, &mmc2_sdio_ext_irq_flags);
smp_wmb();
printk( "%s: irq in suspend, defer & disable_irq(%d)\n",
mmc_hostname(mmc), mmc2_sdio_ext_irq);
wmt_gpio_ack_irq(wmt_mtk6620_intr);
return IRQ_HANDLED;
}
//printk( "%s: irq happen, call mmc_signal_sdio_irq\n", mmc_hostname(mmc));
mmc_signal_sdio_irq(mmc);
wmt_gpio_ack_irq(wmt_mtk6620_intr);
return IRQ_HANDLED;
}
static void mtk6620_setup_sdio_ext_irq (struct mmc_host *mmc)
{
int ret;
printk(KERN_WARNING "setup sdio ext irq but mmc2_sdio_ext_irq(%d) != 0!\n", mmc2_sdio_ext_irq);
if (0 != mmc2_sdio_ext_irq) {
return;
}
wmt_gpio_setpull(wmt_mtk6620_intr,WMT_GPIO_PULL_UP);
// s3c_gpio_setpull(MMC2_SDIO_EINT_PIN, S3C_GPIO_PULL_NONE);
// s3c_gpio_cfgpin(MMC2_SDIO_EINT_PIN, S3C_GPIO_SFN(0xF));
mmc2_sdio_ext_irq = 1;//gpio_to_irq(MMC2_SDIO_EINT_PIN);
wmt_gpio_mask_irq(wmt_mtk6620_intr);
//clear_gpio26_irq_state();
wmt_gpio_ack_irq(wmt_mtk6620_intr);
ret = request_irq(IRQ_GPIO, mtk6620_sdio_ext_irq, IRQF_SHARED,"mmc2_sdio_ext_irq", mmc);
if (ret) {
printk( "request_irq(%d, 0x%p, IRQF_TRIGGER_LOW) fail(%d)!!\n",
mmc2_sdio_ext_irq, mtk6620_sdio_ext_irq, ret);
mmc2_sdio_ext_irq = 0;
return;
}
/*clear int status register before enable this int pin*/
wmt_gpio_ack_irq(wmt_mtk6620_intr);
/*enable this int pin*/
wmt_gpio_unmask_irq(wmt_mtk6620_intr);
// enable_irq_wake(mmc2_sdio_ext_irq);
printk("%s: using sdio_ext_irq and enable_irq_wake(%d) gpio(%d) IRQ_EINT(%d)\n",
mmc_hostname(mmc), mmc2_sdio_ext_irq, wmt_mtk6620_intr,
(IRQ_GPIO));
return;
}
#endif /* MMC2_SDIO_EXT_IRQ */
/**********************************************************************
Name : atsmb2_enable_sdio_irq
Function :
Calls :
Called by :
Parameter :
returns :
Author : Tommy Huang
History :
***********************************************************************/
static void atsmb2_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct atsmb_host *host = mmc_priv(mmc);
unsigned long flags;
DBG("[%s] s enable = %d *ATSMB2_INT_MASK_1 = %x\n",__func__,enable,*ATSMB2_INT_MASK_1);
spin_lock_irqsave(&host->lock, flags);
#if MMC2_SDIO_EXT_IRQ
if (is_mtk6620) {
if (enable&&enable_wifi_irq) {
if (likely(0 != mmc2_sdio_ext_irq)) {
//enable_irq(mmc2_sdio_ext_irq);
wmt_gpio_unmask_irq(wmt_mtk6620_intr);
}
else {
mtk6620_setup_sdio_ext_irq(mmc);
}
}
else {
smp_rmb();
if (unlikely(test_and_clear_bit(IRQ_IN_SUSPEND, &mmc2_sdio_ext_irq_flags))) {
smp_wmb();
printk("%s: irq already disabled when being deferred(%d)\n",
mmc_hostname(host->mmc), mmc2_sdio_ext_irq);
/* do nothing */
}
else {
//disable_irq_nosync(mmc2_sdio_ext_irq);
wmt_gpio_mask_irq(wmt_mtk6620_intr);
}
}
goto out;
}
#endif /* MMC2_SDIO_EXT_IRQ */
if (enable) {
*ATSMB2_INT_MASK_1 |= ATSMB_SDIO_EN;
} else {
*ATSMB2_INT_MASK_1 &= ~ATSMB_SDIO_EN;
}
out:
spin_unlock_irqrestore(&host->lock, flags);
DBG("[%s] e\n",__func__);
}
void wmt_detect_sdio2(void){
if(mmc2_host_attr!=NULL){
struct atsmb_host *host = mmc_priv(mmc2_host_attr);
mmc_detect_change(host->mmc, HZ/2);
}
}
void force_remove_sdio2(void)
{
if(mmc2_host_attr!=NULL)
mmc_force_remove_card(mmc2_host_attr);
}
void rda_mci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
enable_wifi_irq = (unsigned int )enable;
atsmb2_enable_sdio_irq(mmc, enable);
}
EXPORT_SYMBOL(rda_mci_enable_sdio_irq);
EXPORT_SYMBOL(wmt_detect_sdio2);
EXPORT_SYMBOL(force_remove_sdio2);
/**********************************************************************
Name : atsmb2_request
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static void atsmb2_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct atsmb_host *host = mmc_priv(mmc);
DBG("[%s] s\n",__func__);
/* May retry process comes here.*/
host->mrq = mrq;
host->data = mrq->data;
host->cmd = mrq->cmd;
host->done_data = NULL;
host->done = NULL;
/*for data request*/
if (host->data) {
if (host->cmd->opcode == MMC_WRITE_BLOCK
|| host->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK
|| host->cmd->opcode == MMC_READ_SINGLE_BLOCK
|| host->cmd->opcode == MMC_READ_MULTIPLE_BLOCK
|| host->cmd->opcode == SD_IO_RW_EXTENDED) {
atsmb2_start_data(host);
} else {
atsmb2_cmd_with_data_back(host);
}
} else {
atsmb2_start_cmd(host);
}
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_set_clock
Function :.
Calls :
Called by :
Parameter :
Author : Eason Chien
History :2012/7/19
***********************************************************************/
static int atsmb2_set_clock(struct mmc_host *mmc, unsigned int clock)
{
if (clock == mmc->f_min) {
DBG("[%s]ATSMB2 Host 400KHz\n", __func__);
return auto_pll_divisor(DEV_SDMMC2, SET_DIV, 1, 390);
} else if (clock >= 50000000) {
DBG("[%s]ATSMB2 Host 50MHz\n", __func__);
return auto_pll_divisor(DEV_SDMMC2, SET_DIV, 2, 45);
} else if ((clock >= 25000000) && (clock < 50000000)) {
DBG("[%s]ATSMB2 Host 25MHz\n", __func__);
return auto_pll_divisor(DEV_SDMMC2, SET_DIV, 2, 24);
} else if ((clock >= 20000000) && (clock < 25000000)) {
DBG("[%s]ATSMB2 Host 20MHz\n", __func__);
return auto_pll_divisor(DEV_SDMMC2, SET_DIV, 2, 20);
} else {
DBG("[%s]ATSMB2 Host 390KHz\n", __func__);
return auto_pll_divisor(DEV_SDMMC2, SET_DIV, 1, 390);
}
}
/**********************************************************************
Name : atsmb2_set_ios
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static void atsmb2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct atsmb_host *host = mmc_priv(mmc);
unsigned long flags;
DBG("[%s] s\n",__func__);
spin_lock_irqsave(&host->lock, flags);
#if MMC2_SDIO_EXT_IRQ
if(is_mtk6620){
if ((ios->power_mode == MMC_POWER_OFF)) {
if (0 != mmc2_sdio_ext_irq) {
printk("%s: set mmc2 MMC_POWER_OFF disable_irq_wake & free_irq(%d)!\n", __func__, mmc2_sdio_ext_irq);
//disable_irq_wake(mmc2_sdio_ext_irq);
wmt_gpio_mask_irq(wmt_mtk6620_intr);
free_irq(IRQ_GPIO, host->mmc);
mmc2_sdio_ext_irq = 0;
}
}
}
#endif /* MMC2_SDIO_EXT_IRQ */
if (ios->power_mode == MMC_POWER_OFF) {
if (MMC2_DRIVER_VERSION == MMC_DRV_3498) {
/* stop SD output clock */
*ATSMB2_BUS_MODE &= ~(ATSMB_CST);
/* disable SD Card power */
/*set SD2 power pin as GPO pin*/
if (SD2_function == SDIO_WIFI)
;//GPIO_OC_GP0_BYTE_VAL |= BIT6;
else {
GPIO_CTRL_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
GPIO_OC_GP19_SD2_BYTE_VAL|= GPIO_SD2_POWER;
}
/*set internal pull up*/
PULL_CTRL_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
/*set internal pull enable*/
PULL_EN_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
/*disable SD2 power*/
if (SD2_function == SDIO_WIFI)
GPIO_OD_GP0_BYTE_VAL &= ~BIT6;
else
GPIO_OD_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
/* Disable Pull up/down resister of SD Bus */
PULL_CTRL_GP19_SD2_BYTE_VAL &= ~SD2_PIN;
/* Config SD2 to GPIO */
GPIO_CTRL_GP19_SD2_BYTE_VAL |= SD2_PIN;
/* SD2 all pins output low */
GPIO_OD_GP19_SD2_BYTE_VAL &= ~SD2_PIN;
/* Config SD2 to GPIO */
GPIO_OC_GP19_SD2_BYTE_VAL |= SD2_PIN;
}
} else if (ios->power_mode == MMC_POWER_UP) {
if (MMC2_DRIVER_VERSION == MMC_DRV_3498) {
/* disable SD Card power */
/*set SD2 power pin as GPO pin*/
if (SD2_function == SDIO_WIFI)
;//GPIO_OC_GP0_BYTE_VAL |= BIT6;
else {
GPIO_CTRL_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
GPIO_OC_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
}
/*set internal pull up*/
PULL_CTRL_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
/*set internal pull enable*/
PULL_EN_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
/*disable SD2 power*/
if (SD2_function == SDIO_WIFI)
GPIO_OD_GP0_BYTE_VAL &= ~BIT6;
else
GPIO_OD_GP19_SD2_BYTE_VAL |= GPIO_SD2_POWER;
/* Config SD PIN share */
//kevin delete for dvfs
//PIN_SHARING_SEL_4BYTE_VAL |= GPIO_SD2_PinShare;
/* do not config GPIO_SD2_CD because ISR has already run,
* config card detect will issue ISR storm.
*/
/* Config SD to GPIO */
GPIO_CTRL_GP19_SD2_BYTE_VAL |= SD2_PIN;
/* SD all pins output low */
GPIO_OD_GP19_SD2_BYTE_VAL &= ~SD2_PIN;
/* Config SD to GPO */
GPIO_OC_GP19_SD2_BYTE_VAL |= SD2_PIN;
/* Pull up/down resister of SD Bus */
/*Disable Clock & CMD Pull enable*/
PULL_EN_GP19_SD2_BYTE_VAL &= ~(GPIO_SD2_Clock );
/*Set CD ,WP ,DATA pin pull up*/
if (SD2_function == SDIO_WIFI) {
PULL_CTRL_GP23_I2C3_BYTE_VAL &= ~GPIO_SD2_CD; /*pull down card always in slot*/
PULL_CTRL_GP19_SD2_BYTE_VAL |= (GPIO_SD2_Data | GPIO_SD2_WriteProtect|GPIO_SD2_Command);
} else {
PULL_CTRL_GP23_I2C3_BYTE_VAL |= GPIO_SD2_CD;
PULL_CTRL_GP19_SD2_BYTE_VAL |= (GPIO_SD2_Data | GPIO_SD2_WriteProtect|GPIO_SD2_Command);
}
/*Enable CD ,WP ,DATA internal pull*/
PULL_EN_GP23_I2C3_BYTE_VAL |= GPIO_SD2_CD;
PULL_EN_GP19_SD2_BYTE_VAL |= (GPIO_SD2_Data | GPIO_SD2_WriteProtect|GPIO_SD2_Command);
spin_unlock_irqrestore(&host->lock, flags);
msleep(100);
spin_lock_irqsave(&host->lock, flags);
/* enable SD Card Power */
if (SD2_function == SDIO_WIFI)
GPIO_OD_GP0_BYTE_VAL |= BIT6;
else
GPIO_OD_GP19_SD2_BYTE_VAL &= ~GPIO_SD2_POWER;
/* enable SD output clock */
*ATSMB2_BUS_MODE |= ATSMB_CST;
spin_unlock_irqrestore(&host->lock, flags);
msleep(10);
spin_lock_irqsave(&host->lock, flags);
/* Config SD0 back to function */
GPIO_CTRL_GP23_I2C3_BYTE_VAL &= ~GPIO_SD2_CD;
GPIO_CTRL_GP19_SD2_BYTE_VAL &= ~SD2_PIN;
}
} else {
/*nothing to do when powering on.*/
}
host->current_clock = atsmb2_set_clock(mmc,ios->clock);
if (ios->bus_width == MMC_BUS_WIDTH_8) {
*ATSMB2_EXT_CTL |= (0x04);
} else if (ios->bus_width == MMC_BUS_WIDTH_4) {
*ATSMB2_BUS_MODE |= ATSMB_BUS_WIDTH_4;
*ATSMB2_EXT_CTL &= ~(0x04);
} else {
*ATSMB2_BUS_MODE &= ~(ATSMB_BUS_WIDTH_4);
*ATSMB2_EXT_CTL &= ~(0x04);
}
#if 1
if (ios->timing == MMC_TIMING_SD_HS)
*ATSMB2_EXT_CTL |= 0x80; /*HIGH SPEED MODE*/
else
*ATSMB2_EXT_CTL &= ~(0x80);
#endif
#if 0 //zhf: marked by James Tian
if (ios->ins_en == MMC_INSERT_IRQ_EN)
*ATSMB2_INT_MASK_0 |= (0x80);/* or 40?*/
else
*ATSMB2_INT_MASK_0 &= (~0x80);/* or 40?*/
#endif
spin_unlock_irqrestore(&host->lock, flags);
DBG("[%s] e\n",__func__);
}
static const struct mmc_host_ops atsmb2_ops = {
.request = atsmb2_request,
.set_ios = atsmb2_set_ios,
.get_ro = atsmb2_get_ro,
.get_slot_status = atsmb2_get_slot_status,
.dump_host_regs = atsmb2_dump_host_regs,
.enable_sdio_irq = atsmb2_enable_sdio_irq,
};
extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen);
/**********************************************************************
Name :atsmb2_probe
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static int __init atsmb2_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mmc_host *mmc_host = NULL;
struct atsmb_host *atsmb_host = NULL;
struct resource *resource = NULL;
int irq[2] = {0};
int ret = 0;
DBG("[%s] s\n",__func__);
if (!pdev) {
ret = -EINVAL; /* Invalid argument */
goto the_end;
}
/*Enable SD host clock*/
auto_pll_divisor(DEV_SDMMC2, CLK_ENABLE, 0, 0);
if (MMC2_DRIVER_VERSION == MMC_DRV_3498) {
/* enable SD1 PIN share */
//kevin delete for dvfs
//PIN_SHARING_SEL_4BYTE_VAL |= GPIO_SD2_PinShare;
/* Pull up/down resister of SD CD */
if (SD2_function == SDIO_WIFI) {
PULL_CTRL_GP23_I2C3_BYTE_VAL &= ~GPIO_SD2_CD; /*pull down CD*/
} else {
PULL_CTRL_GP23_I2C3_BYTE_VAL |= GPIO_SD2_CD; /*pull up CD*/
}
PULL_EN_GP23_I2C3_BYTE_VAL |= GPIO_SD2_CD;
/* config CardDetect pin to SD function */
GPIO_CTRL_GP23_I2C3_BYTE_VAL &= ~GPIO_SD2_CD;
}
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!resource) {
ret = -ENXIO; /* No such device or address */
printk(KERN_ALERT "[MMC/SD driver] Getting platform resources failed!\n");
goto the_end;
}
#if 0
if (!request_mem_region(resource->start, SZ_1K, MMC2_DRIVER_NAME)) {
ret = -EBUSY;
printk(KERN_ALERT "[MMC/SD driver] Request memory region failed!\n");
goto the_end ;
}
#endif
irq[0] = platform_get_irq(pdev, 0); /*get IRQ for device*/;
irq[1] = platform_get_irq(pdev, 1); /*get IRQ for dma*/;
if (irq[0] == NO_IRQ || irq[1] == NO_IRQ) {
ret = -ENXIO;/* No such device or address */
printk(KERN_ALERT "[MMC/SD driver] Get platform IRQ failed!\n");
goto rls_region;
}
/*allocate a standard msp_host structure attached with a atsmb structure*/
mmc_host = mmc_alloc_host(sizeof(struct atsmb_host), dev);
if (!mmc_host) {
ret = -ENOMEM;
printk(KERN_ALERT "[MMC/SD driver] Allocating driver's data failed!\n");
goto rls_region;
}
mmc_host->wmt_host_index = 2; /*to identify host number*/
dev_set_drvdata(dev, (void *)mmc_host); /* mmc_host is driver data for the atsmb dev.*/
atsmb_host = mmc_priv(mmc_host);
mmc_host->ops = &atsmb2_ops;
mmc_host->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
mmc_host->f_min = 390425; /*390.425Hz = 400MHz/64/16*/
mmc_host->f_max = 50000000; /* in fact, the max frequency is 400MHz( = 400MHz/1/1)*/
if(!is_nmc1000){
mmc_host->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SDIO_IRQ| MMC_CAP_NONREMOVABLE;
}else{
mmc_host->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SDIO_IRQ| MMC_CAP_NONREMOVABLE;
}
/* |MMC_CAP_8_BIT_DATA;*/ //zhf: marked by James Tian
mmc_host->pm_caps |= MMC_PM_KEEP_POWER;
mmc_host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;//add by kevinguan for mtk6620
mmc_host->max_segs = 128; /*we use software sg. so we could manage even larger number.*/
/*1MB per each request */
/*we have a 16 bit block number register, and block length is 512 bytes.*/
mmc_host->max_req_size = 16*512*(mmc_host->max_segs);
mmc_host->max_seg_size = 65024; /* 0x7F*512 PDMA one descriptor can transfer 64K-1 byte*/
mmc_host->max_blk_size = 2048; /* our block length register is 11 bits.*/
mmc_host->max_blk_count = (mmc_host->max_req_size)/512;
/*set the specified host -- ATSMB*/
#ifdef CONFIG_MMC_UNSAFE_RESUME
mmc_host->card_attath_status = card_attach_status_unchange;
#endif
sema_init(&mmc_host->req_sema,1); /*initial request semaphore*/
#if 0
atsmb_host->base = ioremap(resource->start, SZ_1K);
if (!atsmb_host->base) {
printk(KERN_ALERT "[MMC/SD driver] IO remap failed!\n");
ret = -ENOMEM;
goto fr_host;
}
#endif
atsmb_host->base = (void *)resource->start;
atsmb_host->mmc = mmc_host;
spin_lock_init(&atsmb_host->lock);
atsmb_host->res = resource;/* for atsmb2_remove*/
/*disable all interrupt and clear status by resetting controller.*/
*ATSMB2_BUS_MODE |= ATSMB_SFTRST;
*ATSMB2_BLK_LEN &= ~(0xa000);
*ATSMB2_SD_STS_0 |= 0xff;
*ATSMB2_SD_STS_1 |= 0xff;
/* WM3437 A0 default not output clock, after SFTRST need to enable SD clock */
//if (MMC2_DRIVER_VERSION >= MMC_DRV_3437_A0) /* including 3429 */
*ATSMB2_BUS_MODE |= ATSMB_CST;
atsmb_host->regular_irq = irq[0];
atsmb_host->dma_irq = irq[1];
ret = request_irq(atsmb_host->regular_irq,
atsmb2_regular_isr,
IRQF_SHARED, //SA_SHIRQ, /*SA_INTERRUPT, * that is okay?*/ //zhf: modified by James Tian, should be IRQF_SHARED?
MMC2_DRIVER_NAME,
(void *)atsmb_host);
if (ret) {
printk(KERN_ALERT "[MMC/SD driver] Failed to register regular ISR!\n");
goto unmap;
}
ret = request_irq(atsmb_host->dma_irq,
atsmb2_dma_isr,
IRQF_DISABLED, // SA_INTERRUPT, //zhf: modified by James Tian
MMC2_DRIVER_NAME,
(void *)atsmb_host);
if (ret) {
printk(KERN_ALERT "[MMC/SD driver] Failed to register DMA ISR!\n");
goto fr_regular_isr;
}
/*wait card detect status change*/
//msleep(10);
/*enable card insertion interrupt and enable DMA and its Global INT*/
*ATSMB2_BLK_LEN |= (0xa000); /* also, we enable GPIO to detect card.*/
*ATSMB2_SD_STS_0 |= 0xff;
*ATSMB2_INT_MASK_0 |= 0x80; /*or 0x40?*/
/*allocation dma descriptor*/
ret = atsmb2_alloc_desc(atsmb_host, sizeof(struct SD_PDMA_DESC_S) * MAX_DESC_NUM);
if (ret == -1) {
printk(KERN_ALERT "[MMC/SD driver] Failed to allocate DMA descriptor!\n");
goto fr_dma_isr;
}
printk(KERN_INFO "WMT ATSMB2 (AHB To SD/MMC2 Bus) controller registered!\n");
if (SD2_function == SDIO_WIFI){
if(set_bus_dead){
mmc_host->bus_dead = 1; //fix this problem: mmc1: error -110 during resume (card was removed?)
}else
printk("dont set bus dead in probe function\n");
mmc_add_host(mmc_host,false);
}else
mmc_add_host(mmc_host,true);
mmc2_host_attr = mmc_host;
DBG("[%s] e1\n",__func__);
return 0;
fr_dma_isr:
free_irq(atsmb_host->dma_irq, atsmb_host);
fr_regular_isr:
free_irq(atsmb_host->regular_irq, atsmb_host);
unmap:
//iounmap(atsmb_host->base);
//fr_host:
dev_set_drvdata(dev, NULL);
mmc_free_host(mmc_host);
rls_region:
//release_mem_region(resource->start, SZ_1K);
the_end:
printk(KERN_ALERT "[MMC/SD driver] ATSMB2 probe Failed!\n") ;
DBG("[%s] e2\n",__func__);
return ret;
}
/**********************************************************************
Name : atsmb2_remove
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static int atsmb2_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mmc_host *mmc_host = (struct mmc_host *)dev_get_drvdata(dev);
struct atsmb_host *atsmb_host;
DBG("[%s] s\n",__func__);
atsmb_host = mmc_priv(mmc_host);
if (!mmc_host || !atsmb_host) {
printk(KERN_ALERT "[MMC/SD driver] ATSMB2 remove method failed!\n");
DBG("[%s] e1\n",__func__);
return -ENXIO;
}
mmc_remove_host(mmc_host);
/*disable interrupt by resetting controller -- for safey*/
*ATSMB2_BUS_MODE |= ATSMB_SFTRST;
*ATSMB2_BLK_LEN &= ~(0xa000);
*ATSMB2_SD_STS_0 |= 0xff;
*ATSMB2_SD_STS_1 |= 0xff;
(void)free_irq(atsmb_host->regular_irq, atsmb_host);
(void)free_irq(atsmb_host->dma_irq, atsmb_host);
(void)iounmap(atsmb_host->base);
(void)release_mem_region(atsmb_host->res->start, SZ_1K);
dev_set_drvdata(dev, NULL);
/*free dma descriptor*/
dma_free_coherent(atsmb_host->mmc->parent, atsmb_host->DescSize,
atsmb_host->DescVirAddr, atsmb_host->DescPhyAddr);
(void)mmc_free_host(mmc_host);/* also free atsmb_host.*/
DBG("[%s] e2\n",__func__);
return 0;
}
/**********************************************************************
Name : atsmb2_shutdown
Function :.
Calls :
Called by :
Parameter :
Author : Tommy Huang
History :
***********************************************************************/
static void atsmb2_shutdown(struct platform_device *pdev)
{
/*atsmb2_shutdown don't be used now.*/
/*struct device *dev = &pdev->dev;
struct mmc_host *mmc_host = (struct mmc_host *)dev_get_drvdata(dev);*/
DBG("[%s] s\n",__func__);
if (SD2_function != SDIO_WIFI) {
/*Disable card detect interrupt*/
*ATSMB2_INT_MASK_0 &= ~0x80;
}
DBG("[%s] e\n",__func__);
}
/**********************************************************************
Name : atsmb2_suspend
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
int SD2_card_state = 0; /*0: card remove >=1: card enable*/
#ifdef CONFIG_PM
static int atsmb2_suspend(struct platform_device *pdev, pm_message_t state)
{
struct device *dev = &pdev->dev;
struct mmc_host *mmc = (struct mmc_host *)dev_get_drvdata(dev);
int ret = 0;
DBG("[%s] s\n",__func__);
if (mmc) {
if (SD2_function == SDIO_WIFI) {
if (SD2_card_state > 0)
mmc_force_remove_card(mmc2_host_attr);
}
#if MMC2_SDIO_EXT_IRQ
if (is_mtk6620) {
if (0 != mmc2_sdio_ext_irq) {
//if (mmc_try_claim_host(host->mmc)) {
if(true){
set_bit(IN_SUSPEND, &mmc2_sdio_ext_irq_flags);
printk(KERN_INFO "%s:in_suspend++\n", mmc_hostname(mmc));
}
else {
printk(KERN_INFO "%s:claim host fail in suspend, return -EBUSY\n", mmc_hostname(mmc));
return -EBUSY;
}
}
}
#endif /* MMC2_SDIO_EXT_IRQ */
/*struct atsmb_host *host = mmc_priv(mmc);*/
ret = mmc_suspend_host(mmc);
#if MMC2_SDIO_EXT_IRQ
if (is_mtk6620) {
if (ret) {
printk("\n\n\n\n\n\n%s:mmc_suspend_host fail(%d)\n\n\n\n\n\n",
mmc_hostname(mmc), ret);
if (0 != mmc2_sdio_ext_irq) {
clear_bit(IN_SUSPEND, &mmc2_sdio_ext_irq_flags);
//mmc_release_host(host->mmc);
}
return ret;
}
}
#endif
if (ret == 0) {
/*disable all interrupt and clear status by resetting controller. */
*ATSMB2_BUS_MODE |= ATSMB_SFTRST;
*ATSMB2_BLK_LEN &= ~(0xa000);
*ATSMB2_SD_STS_0 |= 0xff;
*ATSMB2_SD_STS_1 |= 0xff;
}
/*disable source clock*/
auto_pll_divisor(DEV_SDMMC2, CLK_DISABLE, 0, 0);
#ifdef CONFIG_MMC_UNSAFE_RESUME
/*clean SD card attatch status change*/
//PMCWS_VAL |= BIT19;
mmc->card_attath_status = card_attach_status_unchange;
#endif
}
DBG("[%s] e\n",__func__);
return ret;
}
/**********************************************************************
Name : atsmb2_resume
Function :.
Calls :
Called by :
Parameter :
Author : Leo Lee
History :
***********************************************************************/
static int atsmb2_resume(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mmc_host *mmc = (struct mmc_host *)dev_get_drvdata(dev);
int ret = 0;
DBG("[%s] s\n",__func__);
/*
* enable interrupt, DMA, etc.
* Supply power to slot.
*/
if (mmc) {
/*enable source clock*/
auto_pll_divisor(DEV_SDMMC2, CLK_ENABLE, 0, 0);
udelay(1);
/*enable card insertion interrupt and enable DMA and its Global INT*/
*ATSMB2_BUS_MODE |= ATSMB_SFTRST;
*ATSMB2_BLK_LEN |= (0xa000);
*ATSMB2_INT_MASK_0 |= 0x80; /* or 40?*/
#ifdef CONFIG_MMC_UNSAFE_RESUME
/*modify SD card attatch status change*/
//if ((PMCWS_VAL & BIT19) && !mmc->bus_dead) {
/*card change when suspend mode*/
mmc->card_attath_status = card_attach_status_change;
/*clean SD card attatch status change*/
// PMCWS_VAL |= BIT19;
//}
#endif
if (SD2_function == SDIO_WIFI) {
PULL_CTRL_GP23_I2C3_BYTE_VAL &= ~GPIO_SD2_CD; /*pull down CD*/
PULL_EN_GP23_I2C3_BYTE_VAL |= GPIO_SD2_CD;
//kevin add for suspend&resume
PULL_CTRL_GP19_SD2_BYTE_VAL |= GPIO_SD2_Data | GPIO_SD2_Command;
PULL_CTRL_GP19_SD2_BYTE_VAL &= ~GPIO_SD2_WriteProtect;
PULL_EN_GP19_SD2_BYTE_VAL |= (GPIO_SD2_Data | GPIO_SD2_WriteProtect|GPIO_SD2_Command);
PULL_EN_GP19_SD2_BYTE_VAL &= ~GPIO_SD2_Clock;
GPIO_CTRL_GP19_SD2_BYTE_VAL &= ~(GPIO_SD2_Data | GPIO_SD2_WriteProtect | GPIO_SD2_Command | GPIO_SD2_Clock);
*ATSMB2_BUS_MODE |= ATSMB_CST;
msleep(20);
if (SD2_card_state > 0) {
printk("[%s] mmc_resume_host s\n",__func__);
mmc_detect_change(mmc2_host_attr, 1*HZ/2);
printk("[%s] mmc_resume_host e\n",__func__);
}
#if MMC2_SDIO_EXT_IRQ
if (is_mtk6620) {
if (0 != mmc2_sdio_ext_irq) {
/* sdhc goes back to normal working mode, after mtk6620_init() */
clear_bit(IN_SUSPEND, &mmc2_sdio_ext_irq_flags);
//mmc_release_host(host->mmc);
printk(KERN_INFO "%s:in_suspend--\n", mmc_hostname(mmc));
}
}
#endif /* MMC2_SDIO_EXT_IRQ */
//kevin add for suspend&resume
ret = mmc_resume_host(mmc);
#if MMC2_SDIO_EXT_IRQ
/* after mmc_resume_host(), sdio_func is also resumed */
if (is_mtk6620) {
if (0 != mmc2_sdio_ext_irq) {
/* signal deferred irq if any */
smp_rmb();
if (unlikely(test_bit(IRQ_IN_SUSPEND, &mmc2_sdio_ext_irq_flags))) {
printk(KERN_INFO "%s: signal deferred irq(%d)\n",
mmc_hostname(mmc), mmc2_sdio_ext_irq);
/* signal the deferred irq */
mmc_signal_sdio_irq(mmc);
}
}
}
#endif
} else {
ret = mmc_resume_host(mmc);
}
}
DBG("[%s] e\n",__func__);
return ret;
}
#else
#define atsmb2_suspend NULL
#define atsmb2_resume NULL
#endif
static struct platform_driver atsmb2_driver = {
.driver.name = "sdmmc2",
//.probe = atsmb2_probe,
.remove = atsmb2_remove,
.shutdown = atsmb2_shutdown,
.suspend = atsmb2_suspend,
.resume = atsmb2_resume,
};
static struct platform_device wmt_sdmmc2_device = {
.name = "sdmmc2",
.id = 0,
.dev = {
.dma_mask = &wmt_sdmmc2_dma_mask,
.coherent_dma_mask = ~0,
.release = atsmb2_release,
},
.num_resources = ARRAY_SIZE(wmt_sdmmc2_resources),
.resource = wmt_sdmmc2_resources,
};
static DEFINE_MUTEX(SD2_state_lock);
static ssize_t atsmb2_state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int ret = 0;
DBG("[%s] s\n",__func__);
ret = sprintf(buf, "%d\n", SD2_card_state);
DBG("[%s]SD2_card_state = %d\n",__func__,SD2_card_state);
DBG("[%s] e\n",__func__);
return ret;
}
static ssize_t atsmb2_state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int val;
DBG("[%s] s\n",__func__);
mutex_lock(&SD2_state_lock);
if (sscanf(buf, "%d", &val) == 1) {
DBG("[%s] val = %d\n",__func__,val);
if (val == 1) {
SD2_card_state++;
DBG("[%s] add state SD2_card_state = %d\n",__func__,SD2_card_state);
if ((SD2_card_state == 1) && (mmc2_host_attr->card == NULL)) {
DBG("[%s]add card\n",__func__);
mmc_detect_change(mmc2_host_attr, 0);
msleep(1000);
} else {
msleep(1000); /*to prevent the card not init ready*/
}
} else if (val == 0) {
SD2_card_state--;
printk("[%s] sub state SD2_card_state = %d\n",__func__,SD2_card_state);
if (SD2_card_state < 0) {
SD2_card_state = 0;
printk("[%s] set 0 SD2_card_state = %d\n",__func__,SD2_card_state);
}
if ((SD2_card_state == 0) && (mmc2_host_attr->card != NULL)) {
DBG("[%s]remove card\n",__func__);
mmc_force_remove_card(mmc2_host_attr);
}
}
DBG("[%s] e1\n",__func__);
mutex_unlock(&SD2_state_lock);
return n;
}
mutex_unlock(&SD2_state_lock);
DBG("[%s] e2\n",__func__);
return -EINVAL;
}
static struct kobj_attribute atsmb2_state_attr = { \
.attr = { \
.name = __stringify(state), \
.mode = 0755, \
}, \
.show = atsmb2_state_show, \
.store = atsmb2_state_store, \
};
#if MMC2_SDIO_EXT_IRQ
static ssize_t atsmb2_intr_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int ret = 0;
ret = sprintf(buf, "%d\n", is_mtk6620);
printk("[%s]is_mtk6620 = %d\n",__func__,is_mtk6620);
return ret;
}
static ssize_t atsmb2_intr_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
int val;
if (sscanf(buf, "%d", &val) == 1) {
printk("[%s] val = %d\n",__func__,val);
if (val == 1) {
if (gpio_request(wmt_mtk6620_intr, "mtk6620_wifi_intr") < 0) {
printk("gpio(%d) mtk6620_wifi_intr gpio request fail\n", wmt_mtk6620_intr);
is_mtk6620 = 0;
return -1;
}
} else if (val == 0) {
gpio_free(wmt_mtk6620_intr);
}
is_mtk6620 = val;
return n;
}
return -EINVAL;
}
static struct kobj_attribute atsmb2_intr_attr = { \
.attr = { \
.name = __stringify(intr), \
.mode = 0755, \
}, \
.show = atsmb2_intr_show, \
.store = atsmb2_intr_store, \
};
#endif
static struct attribute * g2[] = {
&atsmb2_state_attr.attr,
#if MMC2_SDIO_EXT_IRQ
&atsmb2_intr_attr.attr,
#endif
NULL,
};
static struct attribute_group attr2_group = {
.attrs = g2,
};
static int __init atsmb2_init(void)
{
int ret;
int retval;
unsigned char buf[80];
unsigned char buf1[80];
int varlen = 80;
char *varname = "wmt.sd2.param";
int temp = 0, sd_enable = 0; /*0 :disable 1:enable*/
DBG("[%s] s\n",__func__);
#ifdef CONFIG_MTD_WMT_SF
/*Read system param to identify host function 0: SD/MMC 1:SDIO wifi*/
retval = wmt_getsyspara(varname, buf, &varlen);
if (retval == 0) {
sscanf(buf,"%x:%d", &temp, &SD2_function);
printk(KERN_ALERT "wmt.sd2.param = %x:%d\n", temp, SD2_function);
sd_enable = temp & 0xf;
SDXC2_function = (temp >> 4) & 0xf;
printk(KERN_ALERT "SD2 ebable = %x, SDXC = %x, function = %x\n",
sd_enable, SDXC2_function, SD2_function);
if (SD2_function < 0 || SD2_function >= SD_MAX_FUN)
return -ENODEV;
} else {
//default is enable sdio wifi
temp = sd_enable = 1;
SD2_function = 1;
printk(KERN_ALERT "Default wmt.sd2.param = %x:%d\n", temp, SD2_function);
}
memset(buf,0,sizeof(buf));
varlen = 80;
retval = wmt_getsyspara("wmt.init.rc", buf, &varlen);
if (retval == 0) {
printk(KERN_ALERT"wmt.init.rc:%s\n",buf);
if(!strcmp(buf,"init_nmc1000.rc")){
is_nmc1000 = 0x01;
printk("is_nmc1000:%d",is_nmc1000);
}
if (!strcmp(buf, "init.bcm6330.rc"))
{
is_ap6330 = 1;
printk("is_ap6330:%d\n", is_ap6330);
}
if (!strcmp(buf, "init.rda5991.rc"))
{
printk("is rda5991\n");
set_bus_dead = 0;
}
if (!strcmp(buf, "init.mtk6620.rc"))
{
printk("is mtk6620\n");
set_bus_dead = 0;
}
} else {
printk(KERN_ALERT "wmt.init.rc do not exit\n");
}
#endif
/*SD function disable*/
if (sd_enable != 1)
return -ENODEV;
get_driver_version2();
if (platform_device_register(&wmt_sdmmc2_device))//add by jay,for modules support
return -1;
//ret = platform_driver_register(&atsmb2_driver);
ret = platform_driver_probe(&atsmb2_driver, atsmb2_probe);
atsmb2_kobj = kobject_create_and_add("mmc2", NULL);
if (!atsmb2_kobj)
return -ENOMEM;
return sysfs_create_group(atsmb2_kobj, &attr2_group);
DBG("[%s] e\n",__func__);
return ret;
}
static void __exit atsmb2_exit(void)
{
DBG("[%s] s\n",__func__);
(void)platform_driver_unregister(&atsmb2_driver);
(void)platform_device_unregister(&wmt_sdmmc2_device);//add by jay,for modules support
DBG("[%s] e\n",__func__);
}
module_init(atsmb2_init);
module_exit(atsmb2_exit);
module_param(fmax2, uint, 0444);
MODULE_AUTHOR("WonderMedia Technologies, Inc.");
MODULE_DESCRIPTION("WMT [AHB to SD/MMC2 Bridge] driver");
MODULE_LICENSE("GPL");