/* * linux/drivers/mmc/s3c-hsmmc.c - Samsung S3C24XX HS-MMC driver * * $Id: s3c-hsmmc.c,v 1.73 2008/04/13 23:56:19 jsgood Exp $ * * Copyright (C) 2006 Samsung Electronics, All Rights Reserved. * by Suh, Seung-Chull * * This driver is made for High Speed MMC interface. This interface * is adopted and implemented since s3c2443 was made. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Modified by Ryu,Euiyoul * Modified by Suh, Seung-chull to support s3c6400 * * Note: s3c-hsmmc.c can be used for 1 channel only at same time. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //Qisda, Asaku Chen, 2009/08/20, for wifi power #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*Qisda , wenny cheng , 20091224 , board id info {*/ #include <../include/asm-arm/plat-s3c24xx/common-smdk.h> /*Qisda , wenny cheng , 20091224 , board id info }*/ /*Trevor add for debug*/ /*#define CONFIG_S3CMMC_DEBUG*/ #ifdef CONFIG_S3CMMC_DEBUG #define DBG(x...) printk(PFX x) #else #define DBG(x...) do { } while (0) #endif #include "s3c-hsmmc.h" #define DRIVER_NAME "s3c-hsmmc" #define PFX DRIVER_NAME ": " #define MULTICARD_ON_SINGLEBUS_SUPPORT #undef MULTICARD_ON_SINGLEBUS_SUPPORT #define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) #if defined(CONFIG_PM) struct s3c_hsmmc_host *global_host[3]; #endif static void s3c_hsmmc_tasklet_finish (unsigned long param); /*Qisda , wenny cheng , 20091224 , board id info {*/ extern int board_id; /*Qisda , wenny cheng , 20091224 , board id info }*/ static int inResume = 0; //howard, 2009/12/22, fix resume card-detect /*Qisda,2009/7/28,Leo SJ Yang {*/ /*Fix: Must plug-in twice ,SD is reconginzed by system*/ /* GEORGE 20090608 update for module on board.*/ static int card_detect = 0; static int card_detect2 = 2; /* GEORGE 20090608 update for module on board.*/ /*}Qisda,2009/7/28,Leo SJ Yang*/ /* Qisda , howard hsu, 2010/01/16 , op_mode-delay key event {*/ //extern void op_mode_delay_message_to_ap(); /* } Qisda , howard hsu, 2010/01/16 , op_mode-delay key event */ /* Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card { */ // disable it first, wait integration test #ifdef CONFIG_SD_SW_DEBOUNCE static int SDCardStatus = 16; #define SD_INSERTED 0 #define SD_REMOVED 2 #define CHECK_MSEC 50 #define INSERT_MSEC 800 #define REMOVE_MSEC 800 static struct timer_list sd_detect_timer; static void sdcard_debounce_timer(unsigned long data); static int ReadSDCardStatus() { int SDStatus= 0; #if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) SDStatus = readl(S3C2410_GPFDAT); SDStatus &= SD_REMOVED; /* GPF1 */ #elif defined(CONFIG_CPU_S3C6410) SDStatus = readl(S3C_GPNDAT); SDStatus &= 0x2000; /* GPN13 */ #endif return SDStatus; } static void sdcard_debounce_timer(unsigned long data) { static unsigned int StayCount = 0; static unsigned int MaxCount = 2*INSERT_MSEC/CHECK_MSEC; static unsigned int MidCount = INSERT_MSEC/CHECK_MSEC; static unsigned int MinCount = INSERT_MSEC/CHECK_MSEC/2; static int LastSDCardStatus = 0xF; int RawStatus = ReadSDCardStatus(); /* stay count in case of resume in progress */ if (inResume == 1) { mod_timer(&sd_detect_timer ,jiffies+msecs_to_jiffies(CHECK_MSEC)); return; } if ( SDCardStatus == RawStatus ) { StayCount++; if (StayCount >= MaxCount) { StayCount = 0; LastSDCardStatus = SDCardStatus; del_timer(&sd_detect_timer); } else if (StayCount == MidCount) { struct s3c_hsmmc_host *s3c_host = global_host[0]; struct mmc_host *host = s3c_host->mmc; if (SDCardStatus == LastSDCardStatus ) { //StayCount = 0; //LastSDCardStatus = 0xF; } mmc_detect_change(host, 1); mod_timer(&sd_detect_timer, jiffies+msecs_to_jiffies(CHECK_MSEC)); } else if ( StayCount==MinCount ) { /* call detect twice for SD_INSERTED case */ //if (SDCardStatus == LastSDCardStatus ) if (SDCardStatus == SD_INSERTED ) { struct s3c_hsmmc_host *s3c_host = global_host[0]; struct mmc_host *host = s3c_host->mmc; mmc_detect_change(host, 1); } mod_timer(&sd_detect_timer, jiffies+msecs_to_jiffies(CHECK_MSEC)); } else { mod_timer(&sd_detect_timer, jiffies+msecs_to_jiffies(CHECK_MSEC)); } } else { SDCardStatus = RawStatus; if (SDCardStatus==SD_INSERTED) { MaxCount = 2*INSERT_MSEC/CHECK_MSEC; MidCount = INSERT_MSEC/CHECK_MSEC; } else { MaxCount = 2*REMOVE_MSEC/CHECK_MSEC; MidCount = REMOVE_MSEC/CHECK_MSEC; } StayCount = 0; mod_timer(&sd_detect_timer ,jiffies+msecs_to_jiffies(CHECK_MSEC)); } } #endif /* CONFIG_SD_SW_DEBOUNCE */ /* } Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card */ /*****************************************************************************\ * * * Low level functions * * * \*****************************************************************************/ static struct s3c_hsmmc_cfg s3c_hsmmc_platform = { .hwport = 0, .enabled = 0, .host_caps = (MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE), .base = NULL, .highspeed = 0, /* ctrl for mmc */ .fd_ctrl[0] = { .ctrl2 = 0xC0004100, /* ctrl2 for mmc */ .ctrl3[0] = 0x80808080, /* ctrl3 for low speed */ .ctrl3[1] = 0x00000080, /* ctrl3 for high speed */ .ctrl4 = 0, }, /* ctrl for sd */ .fd_ctrl[1] = { .ctrl2 = 0xC0000100, /* ctrl2 for sd */ .ctrl3[0] = 0, /* ctrl3 for low speed */ .ctrl3[1] = 0, /* ctrl3 for high speed */ .ctrl4 = 0, }, }; static int s3c_hsmmc_power_switch(int channel, int arg) { //printk("\n== s3c_hsmmc_power_switch, channel: %d, power: %d\n", channel, arg); if(arg){ if(channel == 0){ //Power on #ifdef CONFIG_QISDA_QD090B00 /*Qisda , wenny cheng , 20091224 , board id info {*/ //if((readl(S3C2416_GPKDAT) & (0x3<<3))){ if(board_id!=QD090B00_S02){ s3c2410_gpio_cfgpin(S3C2410_GPD14, S3C2410_GPD14_OUTP); s3c2410_gpio_setpin(S3C2410_GPD14, 1); } else{ s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP); s3c2410_gpio_setpin(S3C2410_GPG2, 1); } /*Qisda , wenny cheng , 20091224 , board id info }*/ #endif #ifdef CONFIG_QISDA_QD060B00 #ifdef CONFIG_QISDA_BK060B00 s3c2410_gpio_cfgpin(S3C2410_GPH4, S3C2410_GPH4_OUTP); s3c2410_gpio_setpin(S3C2410_GPH4, 1); #else s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP); s3c2410_gpio_setpin(S3C2410_GPG2, 1); #endif #endif //Signal s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2450_GPE5_SD0_CLK); s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2450_GPE6_SD0_CMD); s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2450_GPE7_SD0_DAT0); s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2450_GPE8_SD0_DAT1); s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2450_GPE9_SD0_DAT2); s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2450_GPE10_SD0_DAT3); } else if(channel == 1){ s3c2410_gpio_cfgpin(S3C2443_GPL8, S3C2450_GPL8_SD1CMD); s3c2410_gpio_cfgpin(S3C2443_GPL9, S3C2450_GPL9_SD1CLK); s3c2410_gpio_cfgpin(S3C2443_GPL0, S3C2450_GPL0_SD1DAT0); s3c2410_gpio_cfgpin(S3C2443_GPL1, S3C2450_GPL1_SD1DAT1); s3c2410_gpio_cfgpin(S3C2443_GPL2, S3C2450_GPL2_SD1DAT2); s3c2410_gpio_cfgpin(S3C2443_GPL3, S3C2450_GPL3_SD1DAT3); } } else{ if(channel == 0){ //Power off #ifdef CONFIG_QISDA_QD090B00 /*Qisda , wenny cheng , 20091224 , board id info {*/ //if((readl(S3C2416_GPKDAT) & (0x3<<3))){ if(board_id!=QD090B00_S02){ s3c2410_gpio_cfgpin(S3C2410_GPD14, S3C2410_GPD14_OUTP); s3c2410_gpio_setpin(S3C2410_GPD14, 0); } else{ s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP); s3c2410_gpio_setpin(S3C2410_GPG2, 0); } /*Qisda , wenny cheng , 20091224 , board id info }*/ #endif #ifdef CONFIG_QISDA_QD060B00 #ifdef CONFIG_QISDA_BK060B00 s3c2410_gpio_cfgpin(S3C2410_GPH4, S3C2410_GPH4_OUTP); s3c2410_gpio_setpin(S3C2410_GPH4, 0); #else s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP); s3c2410_gpio_setpin(S3C2410_GPG2, 0); #endif #endif //Signal s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_INP); s3c2410_gpio_pullup(S3C2410_GPE5, 1); s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_INP); s3c2410_gpio_pullup(S3C2410_GPE6, 1); s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_INP); s3c2410_gpio_pullup(S3C2410_GPE7, 1); s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_INP); s3c2410_gpio_pullup(S3C2410_GPE8, 1); s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_INP); s3c2410_gpio_pullup(S3C2410_GPE9, 1); s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_INP); s3c2410_gpio_pullup(S3C2410_GPE10, 1); } else if(channel == 1){ s3c2410_gpio_cfgpin(S3C2443_GPL8, S3C2443_GPL8_INP); s3c2410_gpio_pullup(S3C2443_GPL8, 0); s3c2410_gpio_cfgpin(S3C2443_GPL9, S3C2443_GPL9_INP); s3c2410_gpio_pullup(S3C2443_GPL9, 1); s3c2410_gpio_cfgpin(S3C2443_GPL0, S3C3443_GPL0_INP); s3c2410_gpio_pullup(S3C2443_GPL0, 0); s3c2410_gpio_cfgpin(S3C2443_GPL1, S3C2443_GPL1_INP); s3c2410_gpio_pullup(S3C2443_GPL1, 0); s3c2410_gpio_cfgpin(S3C2443_GPL2, S3C2443_GPL2_INP); s3c2410_gpio_pullup(S3C2443_GPL2, 0); s3c2410_gpio_cfgpin(S3C2443_GPL3, S3C2443_GPL3_INP); s3c2410_gpio_pullup(S3C2443_GPL3, 0); } } } /* s3c_hsmmc_get_platdata * * get the platform data associated with the given device, or return * the default if there is none */ static struct s3c_hsmmc_cfg *s3c_hsmmc_get_platdata (struct device *dev) { if (dev->platform_data != NULL) return (struct s3c_hsmmc_cfg *)dev->platform_data; return &s3c_hsmmc_platform; } static void s3c_hsmmc_reset (struct s3c_hsmmc_host *host, u8 mask) { unsigned long timeout; s3c_hsmmc_writeb(mask, S3C_HSMMC_SWRST); if (mask & S3C_HSMMC_RESET_ALL) host->clock = (uint)-1; /* Wait max 100 ms */ timeout = 100; /* hw clears the bit when it's done */ while (s3c_hsmmc_readb(S3C_HSMMC_SWRST) & mask) { if (timeout == 0) { printk("%s: Reset 0x%x never completed. \n", mmc_hostname(host->mmc), (int)mask); return; } timeout--; mdelay(1); } } static void s3c_hsmmc_ios_init (struct s3c_hsmmc_host *host) { u32 intmask; s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL); intmask = S3C_HSMMC_INT_BUS_POWER | S3C_HSMMC_INT_DATA_END_BIT | S3C_HSMMC_INT_DATA_CRC | S3C_HSMMC_INT_DATA_TIMEOUT | S3C_HSMMC_INT_INDEX | S3C_HSMMC_INT_END_BIT | S3C_HSMMC_INT_CRC | S3C_HSMMC_INT_TIMEOUT | S3C_HSMMC_INT_CARD_REMOVE | S3C_HSMMC_INT_CARD_INSERT | S3C_HSMMC_INT_DATA_AVAIL | S3C_HSMMC_INT_SPACE_AVAIL | S3C_HSMMC_INT_DATA_END | S3C_HSMMC_INT_RESPONSE; #ifdef CONFIG_HSMMC_SCATTERGATHER intmask |= S3C_HSMMC_INT_DMA_END; #endif s3c_hsmmc_writel(intmask, S3C_HSMMC_NORINTSTSEN); s3c_hsmmc_writel(intmask, S3C_HSMMC_NORINTSIGEN); } /*****************************************************************************\ * * * Tasklets * * * \*****************************************************************************/ static void s3c_hsmmc_tasklet_card (ulong param) { struct s3c_hsmmc_host *host; unsigned long iflags; host = (struct s3c_hsmmc_host*)param; spin_lock_irqsave(&host->lock, iflags); if (!(s3c_hsmmc_readl(S3C_HSMMC_PRNSTS) & S3C_HSMMC_CARD_PRESENT)) { if (host->mrq) { printk(KERN_ERR "%s: Card removed during transfer!\n", mmc_hostname(host->mmc)); printk(KERN_ERR "%s: Resetting controller.\n", mmc_hostname(host->mmc)); s3c_hsmmc_reset(host, S3C_HSMMC_RESET_CMD); s3c_hsmmc_reset(host, S3C_HSMMC_RESET_DATA); host->mrq->cmd->error = MMC_ERR_FAILED; /* tasklet_schedule(&host->finish_tasklet); */ s3c_hsmmc_tasklet_finish((unsigned long) host); } } spin_unlock_irqrestore(&host->lock, iflags); /* Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card { */ #ifdef CONFIG_SD_SW_DEBOUNCE int exist = timer_pending(&sd_detect_timer); if (exist == 0) { sd_detect_timer.expires = jiffies+msecs_to_jiffies(CHECK_MSEC); add_timer(&sd_detect_timer); } #else mmc_detect_change(host->mmc, msecs_to_jiffies(500)); #endif /* CONFIG_SD_SW_DEBOUNCE */ /* } Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card */ } static void s3c_hsmmc_activate_led(struct s3c_hsmmc_host *host) { #if 0 u8 ctrl; ctrl = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL); ctrl |= S3C_HSMMC_CTRL_LED; s3c_hsmmc_writeb(ctrl, S3C_HSMMC_HOSTCTL); #endif #if 0 // for 6400 s3c_gpio_cfgpin(S3C_GPJ13, S3C_GPJ13_SD0LED); #endif } static void s3c_hsmmc_deactivate_led(struct s3c_hsmmc_host *host) { #if 0 u8 ctrl; ctrl = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL); ctrl &= ~S3C_HSMMC_CTRL_LED; s3c_hsmmc_writeb(ctrl, S3C_HSMMC_HOSTCTL); #endif #if 0 // for 6400 s3c_gpio_cfgpin(S3C_GPJ13, S3C_GPJ13_INP); #endif } /*****************************************************************************\ * * * Core functions * * * \*****************************************************************************/ #ifdef CONFIG_HSMMC_SCATTERGATHER static inline uint s3c_hsmmc_build_dma_table (struct s3c_hsmmc_host *host, struct mmc_data *data) { #if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443) uint i, j = 0, sub_num = 0; dma_addr_t addr; uint size, length, end; int boundary, xor_bit; struct scatterlist * sg = data->sg; /* build dma table except last one */ for (i=0; i<(host->sg_len-1); i++) { addr = sg[i].dma_address; size = sg[i].length; DBG("%d - addr: %08x, size: %08x\n", i, addr, size); for (; (jdblk[j].dma_address = addr; length = (end & ~((1<sub_block[sub_num] + S3C_HSMMC_MALLOC_SIZE - length; if (data->flags & MMC_DATA_WRITE) { /* write */ memcpy(dest, phys_to_virt(addr), length); } host->dblk[j].original = phys_to_virt(addr); host->dblk[j].dma_address = dma_map_single(NULL, dest, length, host->dma_dir); sub_num++; } } host->dblk[j].length = length; host->dblk[j].boundary = boundary; DBG(" %d: %08x, %08x %x\n", j, addr, length, boundary); addr += length; size -= length; } } /* the last one */ host->dblk[j].dma_address = sg[i].dma_address; host->dblk[j].length = sg[i].length; host->dblk[j].boundary = 0x7; return (j+1); #elif defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) uint i; struct scatterlist * sg = data->sg; /* build dma table except the last one */ for (i=0; i<(host->sg_len-1); i++) { host->sdma_descr_tbl[i].dma_address = sg[i].dma_address; host->sdma_descr_tbl[i].length_attr = (sg[i].length << 16) | (S3C_HSMMC_ADMA_ATTR_ACT_TRAN) | (S3C_HSMMC_ADMA_ATTR_VALID); DBG(" ADMA2 descr table[%d] - addr: %08x, size+attr: %08x\n", i, host->sdma_descr_tbl[i].dma_address, host->sdma_descr_tbl[i].length_attr); } /* the last one */ host->sdma_descr_tbl[i].dma_address = sg[i].dma_address; host->sdma_descr_tbl[i].length_attr = (sg[i].length << 16) | (S3C_HSMMC_ADMA_ATTR_ACT_TRAN) | (S3C_HSMMC_ADMA_ATTR_END | S3C_HSMMC_ADMA_ATTR_VALID); DBG(" ADMA2 descr table[%d] - addr: %08x, size+attr: %08x\n", i, host->sdma_descr_tbl[i].dma_address, host->sdma_descr_tbl[i].length_attr); return (i); #endif } #endif static inline void s3c_hsmmc_prepare_data (struct s3c_hsmmc_host *host, struct mmc_command *cmd) { #if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) u8 reg8; #endif u32 reg; struct mmc_data *data = cmd->data; if (data == NULL) { reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN) | S3C_HSMMC_NIS_CMDCMP; s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN); return; } reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN) & ~S3C_HSMMC_NIS_CMDCMP; s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN); host->dma_dir = (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; #ifdef CONFIG_HSMMC_SCATTERGATHER host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir); reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN); if (host->sg_len == 1) { reg &= ~S3C_HSMMC_NIS_DMA; } else { reg |= S3C_HSMMC_NIS_DMA; } s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN); #if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) reg8 = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL); reg8 |= S3C_HSMMC_CTRL_ADMA2_32; s3c_hsmmc_writeb(reg8, S3C_HSMMC_HOSTCTL); DBG("HOSTCTL(0x28) = 0x%02x\n", s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL)); #endif DBG("data->flags(direction) = 0x%x\n", data->flags); DBG("data->blksz: %d\n", data->blksz); DBG("data->blocks: %d\n", data->blocks); DBG("data->sg_len: %d\n", data->sg_len); DBG("data->sg->addr: 0x%x\n", data->sg->dma_address); DBG("data->sg->length: 0x%x\n", data->sg->length); host->dma_blk = s3c_hsmmc_build_dma_table(host, data); host->next_blk = 0; #else DBG("data->flags(direction) = 0x%x\n", data->flags); DBG("data->blksz: %d\n", data->blksz); DBG("data->blocks: %d\n", data->blocks); DBG("data->sg_len: %d\n", data->sg_len); dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir); #endif } static inline void s3c_hsmmc_set_transfer_mode (struct s3c_hsmmc_host *host, struct mmc_data *data) { u16 mode; mode = S3C_HSMMC_TRNS_DMA; if (data->stop) mode |= S3C_HSMMC_TRNS_ACMD12; if (data->flags & MMC_DATA_MULTI) mode |= S3C_HSMMC_TRNS_MULTI | S3C_HSMMC_TRNS_BLK_CNT_EN; if (data->flags & MMC_DATA_READ) mode |= S3C_HSMMC_TRNS_READ; s3c_hsmmc_writew(mode, S3C_HSMMC_TRNMOD); } static inline void s3c_hsmmc_send_register (struct s3c_hsmmc_host *host) { struct mmc_command *cmd = host->cmd; struct mmc_data *data = cmd->data; u32 cmd_val; if (data) { #ifdef CONFIG_HSMMC_SCATTERGATHER #if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443) struct s3c_hsmmc_dma_blk *dblk; dblk = &host->dblk[0]; s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(dblk->boundary, data->blksz), S3C_HSMMC_BLKSIZE); s3c_hsmmc_writel(dblk->dma_address, S3C_HSMMC_SYSAD); #elif defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(0x7, data->blksz), S3C_HSMMC_BLKSIZE); s3c_hsmmc_writel(virt_to_phys(host->sdma_descr_tbl), S3C_HSMMC_ADMASYSADDR); DBG("S3C_HSMMC_ADMASYSADDR(0x58) = 0x%08x\n", s3c_hsmmc_readl(S3C_HSMMC_ADMASYSADDR)); #endif #else s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(0x7, data->blksz), S3C_HSMMC_BLKSIZE); s3c_hsmmc_writel(sg_dma_address(data->sg), S3C_HSMMC_SYSAD); #endif s3c_hsmmc_writew(data->blocks, S3C_HSMMC_BLKCNT); s3c_hsmmc_set_transfer_mode(host, data); } s3c_hsmmc_writel(cmd->arg, S3C_HSMMC_ARGUMENT); cmd_val = (cmd->opcode << 8); if (cmd_val == (12<<8)) cmd_val |= (3 << 6); if (cmd->flags & MMC_RSP_136) /* Long RSP */ cmd_val |= S3C_HSMMC_CMD_RESP_LONG; else if (cmd->flags & MMC_RSP_BUSY) /* R1B */ cmd_val |= S3C_HSMMC_CMD_RESP_SHORT_BUSY; else if (cmd->flags & MMC_RSP_PRESENT) /* Normal RSP */ cmd_val |= S3C_HSMMC_CMD_RESP_SHORT; if (cmd->flags & MMC_RSP_OPCODE) cmd_val |= S3C_HSMMC_CMD_INDEX; if (cmd->flags & MMC_RSP_CRC) cmd_val |= S3C_HSMMC_CMD_CRC; if (data) cmd_val |= S3C_HSMMC_CMD_DATA; s3c_hsmmc_writew(cmd_val, S3C_HSMMC_CMDREG); } static inline void s3c_hsmmc_send_command (struct s3c_hsmmc_host *host, struct mmc_command *cmd) { u32 mask=1; ulong timeout; #if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) while (s3c_hsmmc_readl(S3C_HSMMC_CONTROL4) & mask); #endif DBG("Sending cmd=(%d), arg=0x%x\n", cmd->opcode, cmd->arg); /* Wait max 10 ms */ timeout = 10; mask = S3C_HSMMC_CMD_INHIBIT; if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) mask |= S3C_HSMMC_DATA_INHIBIT; while (s3c_hsmmc_readl(S3C_HSMMC_PRNSTS) & mask) { printk("########### waiting controller wakeup\n"); if (timeout == 0) { printk(KERN_ERR "%s: Controller never released " "inhibit bit(s).\n", mmc_hostname(host->mmc)); cmd->error = MMC_ERR_FAILED; /* tasklet_schedule(&host->finish_tasklet); */ s3c_hsmmc_tasklet_finish((unsigned long) host); return; } timeout--; mdelay(1); } mod_timer(&host->timer, jiffies + 10 * HZ); host->cmd = cmd; s3c_hsmmc_prepare_data(host, cmd); s3c_hsmmc_send_register(host); } static void s3c_hsmmc_finish_data (struct s3c_hsmmc_host *host) { struct mmc_data *data; u16 blocks; //BUG_ON(!host->data); if(!host->data) return; data = host->data; host->data = NULL; dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); /* * Controller doesn't count down when in single block mode. */ if ((data->blocks == 1) && (data->error == MMC_ERR_NONE)) blocks = 0; else { blocks = s3c_hsmmc_readw(S3C_HSMMC_BLKCNT); } data->bytes_xfered = data->blksz * (data->blocks - blocks); if ((data->error == MMC_ERR_NONE) && blocks) { printk(KERN_ERR "%s: Controller signalled completion even " "though there were blocks left. : %d\n", mmc_hostname(host->mmc), blocks); data->error = MMC_ERR_FAILED; } DBG("data->flags(direction) FINISHED = %d\n", data->flags); DBG("data->blksz FINISHED = %d\n", data->blksz); DBG("data->blocks FINISHED = %d\n", data->blocks); DBG("Not FINISHED blocks = %d\n", blocks); DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered); DBG("\n"); /* tasklet_schedule(&host->finish_tasklet); */ s3c_hsmmc_tasklet_finish((unsigned long) host); } static void s3c_hsmmc_finish_command (struct s3c_hsmmc_host *host) { int i; //BUG_ON(host->cmd == NULL); if(!host->cmd) return; if (host->cmd->flags & MMC_RSP_PRESENT) { if (host->cmd->flags & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ for (i=0; i<4; i++) { host->cmd->resp[i] = s3c_hsmmc_readl(S3C_HSMMC_RSPREG0+ (3-i)*4) << 8; if (i != 3) host->cmd->resp[i] |= s3c_hsmmc_readb(S3C_HSMMC_RSPREG0 + (3-i)*4-1); DBG("cmd (%d) resp[%d] = 0x%x\n", host->cmd->opcode, i, host->cmd->resp[i]); } } else { host->cmd->resp[0] = s3c_hsmmc_readl(S3C_HSMMC_RSPREG0); DBG("cmd (%d) resp[%d] = 0x%x\n", host->cmd->opcode, 0, host->cmd->resp[0]); } } host->cmd->error = MMC_ERR_NONE; DBG("Ending cmd (%d)\n", host->cmd->opcode); DBG("\n"); if (host->cmd->data) host->data = host->cmd->data; else /* tasklet_schedule(&host->finish_tasklet); */ s3c_hsmmc_tasklet_finish((unsigned long) host); host->cmd = NULL; } static void s3c_hsmmc_tasklet_finish (unsigned long param) { struct s3c_hsmmc_host *host; unsigned long iflags; struct mmc_request *mrq; host = (struct s3c_hsmmc_host*)param; //BUG_ON(!host->mrq); if(!host->mrq) return; spin_lock_irqsave(&host->lock, iflags); del_timer(&host->timer); mrq = host->mrq; udelay(10); /* * The controller needs a reset of internal state machines * upon error conditions. */ if ((mrq->cmd->error != MMC_ERR_NONE) || (mrq->data && (mrq->data->error != MMC_ERR_NONE)) ) { s3c_hsmmc_reset(host, S3C_HSMMC_RESET_CMD); s3c_hsmmc_reset(host, S3C_HSMMC_RESET_DATA); } host->mrq = NULL; host->cmd = NULL; host->data = NULL; s3c_hsmmc_deactivate_led(host); mmiowb(); spin_unlock_irqrestore(&host->lock, iflags); mmc_request_done(host->mmc, mrq); } /***************************************************************************** * * * Interrupt handling * * * *****************************************************************************/ static void s3c_hsmmc_cmd_irq (struct s3c_hsmmc_host *host, u32 intmask) { if (!host->cmd) { printk(KERN_ERR "%s: Got command interrupt 0x%08x even " "though no command operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); return; } if (intmask & S3C_HSMMC_INT_TIMEOUT) host->cmd->error = MMC_ERR_TIMEOUT; else if (intmask & S3C_HSMMC_INT_CRC) host->cmd->error = MMC_ERR_BADCRC; else if (intmask & (S3C_HSMMC_INT_END_BIT | S3C_HSMMC_INT_INDEX)) host->cmd->error = MMC_ERR_FAILED; else host->cmd->error = MMC_ERR_INVALID; /* tasklet_schedule(&host->finish_tasklet); */ s3c_hsmmc_tasklet_finish((unsigned long) host); } static void s3c_hsmmc_data_irq (struct s3c_hsmmc_host *host, u32 intmask) { if (!host->data) { /* * A data end interrupt is sent together with the response * for the stop command. */ if (intmask & S3C_HSMMC_INT_DATA_END) return; printk(KERN_ERR "%s: Got data interrupt even though no " "data operation was in progress.\n", mmc_hostname(host->mmc)); return; } if (intmask & S3C_HSMMC_INT_DATA_TIMEOUT) host->data->error = MMC_ERR_TIMEOUT; else if (intmask & S3C_HSMMC_INT_DATA_CRC) host->data->error = MMC_ERR_BADCRC; else if (intmask & S3C_HSMMC_INT_DATA_END_BIT) host->data->error = MMC_ERR_FAILED; if (host->data->error != MMC_ERR_NONE) s3c_hsmmc_finish_data(host); } /*****************************************************************************\ * * * Interrupt handling * * * \*****************************************************************************/ /* * ISR for SDI Interface IRQ * Communication between driver and ISR works as follows: * host->mrq points to current request * host->complete_what tells the ISR when the request is considered done * * 1) Driver sets up host->mrq and host->complete_what * 2) Driver prepares the transfer * 3) Driver enables interrupts * 4) Driver starts transfer * 5) Driver waits for host->complete_rquest * 6) ISR checks for request status (errors and success) * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error * 7) ISR completes host->complete_request * 8) ISR disables interrupts * 9) Driver wakes up and takes care of the request */ static irqreturn_t s3c_hsmmc_irq (int irq, void *dev_id) { irqreturn_t result = 0; struct s3c_hsmmc_host *host = dev_id; struct mmc_request *mrq; u32 intsts; #ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND uint i, org_irq_sts; #endif spin_lock(&host->lock); mrq = host->mrq; intsts = s3c_hsmmc_readw(S3C_HSMMC_NORINTSTS); /* Sometimes, hsmmc does not update its status bit immediately * when it generates irqs. by scsuh. */ #ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND for (i=0; i<0x1000; i++) { if ((intsts = s3c_hsmmc_readw(S3C_HSMMC_NORINTSTS))) break; } #endif if (unlikely(!intsts)) { result = IRQ_NONE; goto out; } intsts = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTS); #ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND org_irq_sts = intsts; #endif DBG("Got interrupt = 0x%08x\n", intsts); if (unlikely(intsts & S3C_HSMMC_INT_CARD_CHANGE)) { u32 reg16; if (intsts & S3C_HSMMC_INT_CARD_INSERT) printk(PFX "card inserted.\n"); else if (intsts & S3C_HSMMC_INT_CARD_REMOVE) printk(PFX "card removed.\n"); reg16 = s3c_hsmmc_readw(S3C_HSMMC_NORINTSTSEN); s3c_hsmmc_writew(reg16 & ~S3C_HSMMC_INT_CARD_CHANGE, S3C_HSMMC_NORINTSTSEN); s3c_hsmmc_writew(S3C_HSMMC_INT_CARD_CHANGE, S3C_HSMMC_NORINTSTS); s3c_hsmmc_writew(reg16, S3C_HSMMC_NORINTSTSEN); intsts &= ~S3C_HSMMC_INT_CARD_CHANGE; tasklet_schedule(&host->card_tasklet); goto insert; } if (likely(!(intsts & S3C_HSMMC_NIS_ERR))) { s3c_hsmmc_writel(intsts, S3C_HSMMC_NORINTSTS); if (intsts & S3C_HSMMC_NIS_CMDCMP) { DBG("command done\n"); s3c_hsmmc_finish_command(host); } if (intsts & S3C_HSMMC_NIS_TRSCMP) { DBG("transfer done\n\n"); s3c_hsmmc_finish_command(host); s3c_hsmmc_finish_data(host); intsts &= ~S3C_HSMMC_NIS_DMA; } #ifdef CONFIG_HSMMC_SCATTERGATHER #if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443) if (intsts & S3C_HSMMC_NIS_DMA) { struct s3c_hsmmc_dma_blk *dblk; dblk = &host->dblk[host->next_blk]; if (dblk->original) { /* on read */ if (host->dma_dir == DMA_FROM_DEVICE) { memcpy(dblk->original, phys_to_virt(dblk->dma_address), dblk->length); } dma_unmap_single(NULL, dblk->dma_address, dblk->length, host->dma_dir); dblk->original = 0; } host->next_blk++; dblk = &host->dblk[host->next_blk]; if (host->next_blk == (host->dma_blk-1)) { u32 reg; reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN); reg &= ~S3C_HSMMC_NIS_DMA; s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN); } /* We do not handle DMA boundaries, so set it to max (512 KiB) */ s3c_hsmmc_writew((dblk->boundary<<12 | 0x200), S3C_HSMMC_BLKSIZE); s3c_hsmmc_writel(dblk->dma_address, S3C_HSMMC_SYSAD); } #endif #endif } else { DBG("command FAIL : found bad irq [0x%8x]\n", intsts); DBG("\n"); if (intsts & S3C_HSMMC_INT_CMD_MASK) { s3c_hsmmc_writel(intsts & S3C_HSMMC_INT_CMD_MASK, S3C_HSMMC_NORINTSTS); s3c_hsmmc_cmd_irq(host, intsts & S3C_HSMMC_INT_CMD_MASK); } if (intsts & S3C_HSMMC_INT_DATA_MASK) { s3c_hsmmc_writel(intsts & S3C_HSMMC_INT_DATA_MASK, S3C_HSMMC_NORINTSTS); s3c_hsmmc_finish_command(host); s3c_hsmmc_data_irq(host, intsts & S3C_HSMMC_INT_DATA_MASK); } intsts &= ~(S3C_HSMMC_INT_CMD_MASK | S3C_HSMMC_INT_DATA_MASK); } /* XXX: fix later by scsuh */ #if 0 if (intsts & S3C_HSMMC_INT_BUS_POWER) { printk(KERN_ERR "%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); s3c_hsmmc_writel(S3C_HSMMC_INT_BUS_POWER, S3C_HSMMC_NORINTSTS); } intsts &= S3C_HSMMC_INT_BUS_POWER; if (intsts) { printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n", mmc_hostname(host->mmc), intsts); s3c_hsmmc_writel(intsts, S3C_HSMMC_NORINTSTS); } #endif #ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND for (i=0; i<0x1000; i++) { if (org_irq_sts != s3c_hsmmc_readl(S3C_HSMMC_NORINTSTS)) break; } #endif insert: result = IRQ_HANDLED; mmiowb(); out: spin_unlock(&host->lock); return result; } static irqreturn_t s3c_hsmmc_irq_cd (int irq, void *dev_id) { struct s3c_hsmmc_host *host = dev_id; int ext_CD_int = 0; CyEvent_t CyEvent = NEW_CYEVENT(CYEVENT_TYPE_SYSTEM); CyEvent.flags |= CYEVENT_FLAG_SD_MESSAGE; #if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) ext_CD_int = readl(S3C2410_GPFDAT); ext_CD_int &= 0x2; /* GPF1 */ #elif defined(CONFIG_CPU_S3C6410) ext_CD_int = readl(S3C_GPNDAT); ext_CD_int &= 0x2000; /* GPN13 */ #endif //printk("\ns3c_hsmmc_irq_cd, ext_CD_int: %d, card_detect: %d, card_detect2: %d\n", ext_CD_int, card_detect, card_detect2); if(ext_CD_int && card_detect) { printk("s3c-hsmmc channel-0(EXT): card removed.\n"); /*Qisda Qube for sd card detect20091124*/ //set_irq_type(host->irq_cd, IRQT_FALLING); set_irq_type(host->irq_cd, IRQT_LOW); /*Qisda Qube for sd card detect20091124*/ card_detect = 0; //Qisda, Asaku Chen, 2009/11/03, SD power off s3c_hsmmc_power_switch(0, 0); card_detect2 = 2; /* Send CyIO event */ /* SD OUT */ CyEvent_PushNewUniqueEvent(&CyEvent); } else if(!ext_CD_int && !card_detect) { printk("s3c-hsmmc channel-0(EXT): card inserted.\n"); /*Qisda Qube for sd card detect20091124*/ //set_irq_type(host->irq_cd, IRQT_RISING); set_irq_type(host->irq_cd, IRQT_HIGH); /*Qisda Qube for sd card detect20091124*/ //Qisda, Asaku Chen, 2009/11/03, SD power on if (inResume!=1)//howard, 2009/12/22, fix resume card-detect { card_detect = 1; s3c_hsmmc_power_switch(0, 1); } if(card_detect2 == 3){ printk("resume host first\n"); s3c_hsmmc_ios_init(host); mmc_resume_host(host->mmc); card_detect2 = 2; } /* Send CyIO event */ /* SD IN */ CyEvent.flags |= CYEVENT_FLAG_SYS_STATUS; CyEvent_PushNewUniqueEvent(&CyEvent); } //Qisda, Asaku Chen, 2009/11/03 { else if(card_detect2 == 1){ printk("SD power on\n"); s3c_hsmmc_power_switch(0, 1); card_detect2 = 2; } else if(card_detect2 == 0){ printk("SD power off\n"); s3c_hsmmc_power_switch(0, 0); card_detect2 = -1; } //Qisda, Asaku Chen, 2009/11/03 } else return IRQ_HANDLED; tasklet_schedule(&host->card_tasklet); mmiowb(); spin_unlock(&host->lock); return IRQ_HANDLED; } static void s3c_hsmmc_check_status (unsigned long data) { struct s3c_hsmmc_host *host = (struct s3c_hsmmc_host *)data; s3c_hsmmc_irq(0, host); } static void s3c_hsmmc_request (struct mmc_host *mmc, struct mmc_request *mrq) { struct s3c_hsmmc_host *host = mmc_priv(mmc); unsigned long flags; DBG("hsmmc(%d) request: [CMD] opcode:%d arg:0x%08x flags:0x%02x retries:%u\n", host->plat_data->hwport, mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries); spin_lock_irqsave(&host->lock, flags); WARN_ON(host->mrq != NULL); s3c_hsmmc_activate_led(host); host->mrq = mrq; if ((s3c_hsmmc_readl(S3C_HSMMC_PRNSTS) & S3C_HSMMC_CARD_PRESENT) || card_detect) { s3c_hsmmc_send_command(host, mrq->cmd); } else { host->mrq->cmd->error = MMC_ERR_TIMEOUT; /* tasklet_schedule(&host->finish_tasklet); */ s3c_hsmmc_tasklet_finish((unsigned long) host); } mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } /* return 0: OK * return -1: error */ static int s3c_set_bus_width (struct s3c_hsmmc_host *host, uint width) { u8 reg; reg = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL); switch (width) { case MMC_BUS_WIDTH_1: reg &= ~(S3C_HSMMC_CTRL_4BIT | S3C_HSMMC_CTRL_8BIT); DBG("bus width: 1 bit\n"); break; case MMC_BUS_WIDTH_4: DBG("bus width: 4 bit\n"); reg &= ~(S3C_HSMMC_CTRL_8BIT); reg |= S3C_HSMMC_CTRL_4BIT; break; default: DBG("bus width: Error\n"); return -1; } s3c_hsmmc_writeb(reg, S3C_HSMMC_HOSTCTL); DBG("HOSTCTL(0x28) = 0x%02x\n", s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL)); return 0; } static void s3c_hsmmc_set_clock (struct s3c_hsmmc_host *host, ulong clock) { struct s3c_hsmmc_cfg *cfg = host->plat_data; //int cardtype = host->mmc->mode; int cardtype = MMC_MODE_MMC; u32 val = 0, tmp_clk = 0, clk_src = 0, i, j; u16 div = (u16)-1; ulong timeout; u8 ctrl; /* if we already set, just out. */ if (clock == host->clock) { printk("%p:host->clock0 : %d Hz\n", host->base, host->clock); return; } /* before setting clock, clkcon must be disabled. */ s3c_hsmmc_writew(0, S3C_HSMMC_CLKCON); s3c_hsmmc_writeb(S3C_HSMMC_TIMEOUT_MAX, S3C_HSMMC_TIMEOUTCON); /* change the edge type according to frequency */ ctrl = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL); if (cfg->highspeed) ctrl |= S3C_HSMMC_CTRL_HIGHSPEED; else ctrl &= ~S3C_HSMMC_CTRL_HIGHSPEED; s3c_hsmmc_writeb(ctrl, S3C_HSMMC_HOSTCTL); if (clock == 0) { DBG(" In case of 0 Hz of clock, I'm afraid DO NOTHING ..\n"); return; } /* calculate optimal clock. by scsuh */ DBG("Requested clock is %ld Hz\n", clock); for (i=0; iclk[i])) <= clock) { if (tmp_clk >= val) { val = tmp_clk; div = 0; clk_src = i+1; } } for (j=0x1; j<=0x80; j <<= 1) { tmp_clk = clk_get_rate(host->clk[i]) / (j<<1); if ((val < tmp_clk) && (tmp_clk <= clock)) { val = tmp_clk; div = j; clk_src = i+1; break; } } DBG(" tmp_val[%d]: %d\n", i, val); } DBG("Optimal clock : %d Hz, div: 0x%x, SelBaseclk_src: %d\n", val, div, clk_src); /* CONTROL2 */ s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl2 | (clk_src << 4), S3C_HSMMC_CONTROL2); /* CONTROL3 */ if (clock > 25000000) s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl3[SPEED_HIGH], S3C_HSMMC_CONTROL3); else s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl3[SPEED_NORMAL], S3C_HSMMC_CONTROL3); #if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl4 << 16, S3C_HSMMC_CONTROL4); #endif s3c_hsmmc_writew(((div << 8) | S3C_HSMMC_CLOCK_INT_EN), S3C_HSMMC_CLKCON); timeout = 10; while (!((val = s3c_hsmmc_readw(S3C_HSMMC_CLKCON)) & S3C_HSMMC_CLOCK_INT_STABLE)) { if (!timeout) { printk("Error in INTERNAL clock stabilization: %08x\n", val); return; } timeout--; mdelay(1); } s3c_hsmmc_writew(val | S3C_HSMMC_CLOCK_CARD_EN, S3C_HSMMC_CLKCON); timeout = 10; while (!((val = s3c_hsmmc_readw(S3C_HSMMC_CLKCON)) & S3C_HSMMC_CLOCK_EXT_STABLE)) { if (!timeout) { printk("Error in EXTERNAL clock stabilization: %08x\n", val); return; } timeout--; mdelay(1); } } static void s3c_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct s3c_hsmmc_host *host = mmc_priv(mmc); unsigned long iflags; spin_lock_irqsave(&host->lock, iflags); /* * Reset the chip on each power off. * Should clear out any weird states. */ if (ios->power_mode == MMC_POWER_OFF) { s3c_hsmmc_writew(0, S3C_HSMMC_NORINTSIGEN); s3c_hsmmc_ios_init(host); //Qisda, Asaku Chen, 2009/11/03 { if(host->plat_data->hwport==0){ //printk("== s3c_hsmmc_set_ios: %d, MMC_POWER_OFF\n", host->plat_data->hwport); s3c_hsmmc_power_switch(0, 0); } } else{ //Enable SD power if((host->plat_data->hwport==0) && (card_detect2 > 0)){ //printk("== s3c_hsmmc_set_ios: %d, MMC_POWER_ON\n", host->plat_data->hwport); s3c_hsmmc_power_switch(0, 1); } } if((ios->power_mode == MMC_POWER_OFF) && (host->plat_data->hwport==0)){ //do nothing }else{ if (host->plat_data->enabled) hsmmc_set_gpio(host->plat_data->hwport, host->plat_data->bus_width); } if((host->plat_data->hwport == 1) && (ios->clock > 26000000)){ ios->clock = 26000000; } DBG("ios->clock: %d Hz\n", ios->clock); s3c_hsmmc_set_clock(host, ios->clock); DBG("ios->bus_width: %d\n", ios->bus_width); s3c_set_bus_width(host, ios->bus_width); DBG("S3C_HSMMC_CONTROL2(0x80) = 0x%08x\n", s3c_hsmmc_readl(S3C_HSMMC_CONTROL2)); DBG("S3C_HSMMC_CONTROL3(0x84) = 0x%08x\n", s3c_hsmmc_readl(S3C_HSMMC_CONTROL3)); DBG("S3C_HSMMC_CLKCON (0x2c) = 0x%04x\n", s3c_hsmmc_readw(S3C_HSMMC_CLKCON)); if (ios->power_mode == MMC_POWER_OFF) s3c_hsmmc_writeb(S3C_HSMMC_POWER_OFF, S3C_HSMMC_PWRCON); else s3c_hsmmc_writeb(S3C_HSMMC_POWER_ON_ALL, S3C_HSMMC_PWRCON); udelay(1000); spin_unlock_irqrestore(&host->lock, iflags); } static struct mmc_host_ops s3c_hsmmc_ops = { .request = s3c_hsmmc_request, .set_ios = s3c_hsmmc_set_ios, }; //Qisda, Asaku Chen, 2009/08/20, for wifi power { static int s3c_wifi_open(struct inode *inode, struct file *file) { return 0; } #define S3C_WIFI_POWER_ON 1 #define S3C_WIFI_POWER_OFF 2 #define S3C_READ_TILT_STATUS 3 #define S3C_READ_GPIO_VALUE 4 #define S3C_SET_GPIO_ADDR 5 #define S3C_WRITE_GPIO_VALUE 6 #define S3C_READ_HW_VERSION 7 #define S3C_TCON_ERASE 10 #define S3C_TCON_ID 11 #define S3C_TCON_READ 12 #define S3C_TCON_BYTE_COUNT 13 #define S3C_TCON_WRITE 14 #define MMC_DEVICE_POWER_STATE 20 #define S3C_BT_POWER_ON 1 #define S3C_BT_POWER_OFF 2 static unsigned char s3c_tcon_read_byte(void) { /* Unsigned char Get data () //Receive a byte from Serial flash { unsigned char k, temdata =0, i; for ( k = 0; k++; k< 8) { if ( SO == 1 ) { i = (0x80 >> k); temdata = (temdata || i); } SCLK = 1; SCLK = 0; } return (temdata); } */ unsigned char k, temdata =0, i=0; u32 read_pin; for(k=0; k<8; k++){ read_pin = (readl(S3C2410_GPEDAT) & (1<<11)); //printk(" [%d]: 0x%X 0x%X\n", k, read_pin, readl(S3C2410_GPEDAT)); if(read_pin){ i = (0x80 >> k); temdata = (temdata | i); } s3c2410_gpio_setpin(S3C2410_GPE13, 1); udelay(2); s3c2410_gpio_setpin(S3C2410_GPE13, 0); udelay(2); } //printk("s3c_tcon_read_byte: 0x%2X\n", temdata); return (temdata); } static void s3c_tcon_write_byte(char w_data) { /* Send data (unsigned char indata) // Send 1 byte data to Serial flash { unsigned char k; for ( k = 0; k++; k< 8) { if( (indata & 0x80) == 0x80) SI = 1; else SI = 0; SCLK = 1; SCLK = 0; indata = (indata << 1); } } */ unsigned char k; //printk("s3c_tcon_write_byte: 0x%2X\n", w_data); for(k=0; k<8; k++){ if((w_data & 0x80)) s3c2410_gpio_setpin(S3C2410_GPE12, 1); else s3c2410_gpio_setpin(S3C2410_GPE12, 0); s3c2410_gpio_setpin(S3C2410_GPE13, 1); udelay(2); s3c2410_gpio_setpin(S3C2410_GPE13, 0); udelay(2); w_data = (w_data << 1); } } extern void mmc_rescan(struct work_struct *work); static int s3c_wifi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static char s3c_power_off = 0; static unsigned int gpio_addr = 0; static unsigned int tcon_byte_count; static unsigned int tcon_address; unsigned char tcon_buffer[128]; unsigned char r_data; int ret, i; u32 read_pin; switch(cmd){ #ifdef CONFIG_QISDA_BK060B00 case S3C_WIFI_POWER_ON: printk("BCM4329-WIFI_BT_POWER_ON\n"); //TCON Flash disable, and select WiFi s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP); s3c2410_gpio_setpin(S3C2410_GPH6, 0); //bcm4329 module power on control //GPK7, WIFI PA 3.3V writel(((readl(S3C2416_GPKCON) & ~(1<<15)) | (1<<14)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) | (1<<7)), S3C2416_GPKDAT); //GPK6, PA 1.8V writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) | (1<<6)), S3C2416_GPKDAT); //GPK5, PA 3.3V writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) | (1<<5)), S3C2416_GPKDAT); //GPK9 (green light), pull high writel(((readl(S3C2416_GPKCON) & ~(1<<19)) | (1<<18)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) | (1<<9)), S3C2416_GPKDAT); //WIFI_BT_EN (GPD11) pull high s3c2410_gpio_cfgpin(S3C2410_GPD11, S3C2410_GPD11_OUTP); s3c2410_gpio_setpin(S3C2410_GPD11, 0); //writel(((readl(S3C2416_GPGCON) & ~(1<<9)) | (1<<8)), S3C2416_GPGCON); //writel((readl(S3C2416_GPGDAT) & ~(1<<4)), S3C2416_GPGDAT); mdelay(1); s3c2410_gpio_setpin(S3C2410_GPD11, 1); //writel((readl(S3C2416_GPGDAT) | (1<<4)), S3C2416_GPGDAT); mdelay(20); //EXT_N_WIFI_RST (GPD8) pull high s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP); s3c2410_gpio_setpin(S3C2410_GPD8, 0); //writel(((readl(S3C2416_GPDCON) & ~(1<<17)) | (1<<16)), S3C2416_GPDCON); //writel((readl(S3C2416_GPDDAT) & ~(1<<8)), S3C2416_GPDDAT); mdelay(1); s3c2410_gpio_setpin(S3C2410_GPD8, 1); //writel((readl(S3C2416_GPDDAT) | (1<<8)), S3C2416_GPDDAT); mdelay(20); //EXT_N_BT_RST (GPA2) pull high s3c2410_gpio_cfgpin(S3C2410_GPA2, S3C2410_GPA2_OUT); s3c2410_gpio_setpin(S3C2410_GPA2, 0); mdelay(1); s3c2410_gpio_setpin(S3C2410_GPA2, 1); mdelay(20); //UART1 CTS1 (GPH10) set s3c2410_gpio_cfgpin(S3C2410_GPH10, S3C2410_GPH10_CLKOUT1); //writel(((readl(S3C2416_GPHCON) | (1<<21)) & ~ (1<<20)), S3C2416_GPHCON); //writel(((readl(S3C2416_GPHUDP) & ~ (1<<21)) & ~ (1<<20)), S3C2416_GPHUDP); //writel((readl(S3C2416_GPHDAT) & ~(1<<10)), S3C2416_GPHDAT); mdelay(1); //UART1 RTS1 (GPH11) set s3c2410_gpio_cfgpin(S3C2410_GPH11, S3C2410_GPH11_nRTS); //writel(((readl(S3C2416_GPHCON) | (1<<23)) & ~(1<<22)), S3C2416_GPHCON); //writel((readl(S3C2416_GPHDAT) & ~(1<<11)), S3C2416_GPHDAT); //writel(((readl(S3C2416_GPHUDP) & ~ (1<<23)) & ~ (1<<22)), S3C2416_GPHUDP); mdelay(1); break; case S3C_WIFI_POWER_OFF: printk("BCM4329-WIFI_POWER_OFF\n"); //TCON Flash disable, and select WiFi s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP); s3c2410_gpio_setpin(S3C2410_GPH6, 0); //bcm4329 module power off control //EXT_N_WIFI_RST (GPD8) pull down s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP); s3c2410_gpio_setpin(S3C2410_GPD8, 0); //WIFI_BT_EN (GPD11) pull down s3c2410_gpio_cfgpin(S3C2410_GPD11, S3C2410_GPD11_OUTP); s3c2410_gpio_setpin(S3C2410_GPD11, 0); //EXT_N_BT_RST (GPA2) pull down s3c2410_gpio_cfgpin(S3C2410_GPA2, S3C2410_GPA2_OUT); s3c2410_gpio_setpin(S3C2410_GPA2, 0); mdelay(20); //GPK9 (green light), pull down writel(((readl(S3C2416_GPKCON) & ~(1<<19)) | (1<<18)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<9)), S3C2416_GPKDAT); //GPK7, WIFI PA 3.3V writel(((readl(S3C2416_GPKCON) & ~(1<<15)) | (1<<14)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<7)), S3C2416_GPKDAT); //GPK6, PA 1.8V writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<6)), S3C2416_GPKDAT); //GPK5, PA 3.3V writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<5)), S3C2416_GPKDAT); break; #else case S3C_WIFI_POWER_ON: printk("WIFI_POWER_ON\n"); //TCON Flash disable, and select WiFi s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP); s3c2410_gpio_setpin(S3C2410_GPH6, 0); //GPL13, SPI_CS s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_SS0); //SPI DI s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0); //SPI DO s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0); //SPI CLK s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0); //GPK6, PA 1.8V writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) | (1<<6)), S3C2416_GPKDAT); //GPK5, PA 3.3V writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) | (1<<5)), S3C2416_GPKDAT); //GPD8, RESET s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP); s3c2410_gpio_setpin(S3C2410_GPD8, 0); mdelay(1); s3c2410_gpio_setpin(S3C2410_GPD8, 1); mdelay(20); break; case S3C_WIFI_POWER_OFF: printk("WIFI_POWER_OFF\n"); //TCON Flash disable, and select WiFi s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP); s3c2410_gpio_setpin(S3C2410_GPH6, 0); //GPK5, PA 3.3V writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<5)), S3C2416_GPKDAT); //GPK6, PA 1.8V writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<6)), S3C2416_GPKDAT); //GPD8, RESET s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP); s3c2410_gpio_setpin(S3C2410_GPD8, 0); //GPL13, SPI_CS s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_OUTP); s3c2410_gpio_setpin(S3C2410_GPL13, 0); //SPI DI s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_INP); s3c2410_gpio_pullup(S3C2410_GPE11, 1); //SPI DO s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_INP); s3c2410_gpio_pullup(S3C2410_GPE12, 1); //SPI CLK s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_INP); s3c2410_gpio_pullup(S3C2410_GPE13, 1); break; #endif case S3C_READ_TILT_STATUS: if( ((readl(S3C2410_GPDDAT) & (0x3<<10)) >> 10) == 2){ return 270; } else if( ((readl(S3C2410_GPDDAT) & (0x3<<10)) >> 10) == 1){ return 90; } else if( ((readl(S3C2410_GPDDAT) & (0x3<<10)) >> 10) == 3){ return 180; } else{ return 0; } break; case S3C_READ_GPIO_VALUE: printk("S3C_READ_GPIO_VALUE\n"); printk("addr: 0x%x\n", arg+0x56000000); arg = arg + S3C24XX_VA_GPIO; printk("value: 0x%x\n", readl(arg)); break; case S3C_SET_GPIO_ADDR: printk("S3C_SET_GPIO_ADDR\n"); printk("set addr: 0x%x\n", arg + 0x56000000); gpio_addr = arg + S3C24XX_VA_GPIO; break; case S3C_WRITE_GPIO_VALUE: printk("S3C_WRITE_GPIO_VALUE\n"); printk("set addr: 0x%x, value: 0x%x\n", (gpio_addr-0xA0400000), arg); writel(arg, gpio_addr); break; case MMC_DEVICE_POWER_STATE: printk("MMC_DEVICE_POWER_STATE: %d\n", arg); if(arg == 0){ card_detect2 = 1; s3c_hsmmc_irq_cd (0, global_host[0]); } else if(arg == 3){ card_detect2 = 0; s3c_hsmmc_irq_cd (0, global_host[0]); } else{ printk("no such state\n"); } break; case S3C_READ_HW_VERSION: //printk("S3C_READ_HW_VERSION\n"); read_pin = readl(S3C2416_GPKDAT) & 0x1E; //printk(" read_pin: 0x%x\n", read_pin); ret = 0; if((read_pin & (1<<4))) ret = ret + 0x1000; if((read_pin & (1<<3))) ret = ret + 0x0100; if((read_pin & (1<<2))) ret = ret + 0x0010; if((read_pin & (1<<1))) ret = ret + 0x0001; printk("Board ID: 0x%04x\n", ret); return ret; break; case S3C_TCON_ERASE: printk("S3C_TCON_ERASE\n"); //WiFi off //GPK5, PA 3.3V off writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<5)), S3C2416_GPKDAT); //GPK6, PA 1.8V writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON); writel((readl(S3C2416_GPKDAT) & ~(1<<6)), S3C2416_GPKDAT); //GPD8, RESET s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP); s3c2410_gpio_setpin(S3C2410_GPD8, 0); //GPH6, TCON_FLASH enable, and select TCON flash s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP); s3c2410_gpio_setpin(S3C2410_GPH6, 1); mdelay(200); //Set GPIO for TCON update //Chip Select s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_OUTP); s3c2410_gpio_setpin(S3C2410_GPL13, 1); //GPE11 SPIMISO0 s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_INP); s3c2410_gpio_pullup(S3C2410_GPE11, 0); //GPE12 SPIMOSI0 s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_OUTP); s3c2410_gpio_setpin(S3C2410_GPE12, 1); //GPE13 SPI CLK s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_OUTP); s3c2410_gpio_setpin(S3C2410_GPE13, 0); msleep(50); //Erase TCON Flash s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(50); s3c_tcon_write_byte(0x06); //Setting Write Enable Latch bit s3c2410_gpio_setpin(S3C2410_GPL13, 1); udelay(50); s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(50); s3c_tcon_write_byte(0x60); //Write Chip Erase command s3c2410_gpio_setpin(S3C2410_GPL13, 1); msleep(500); do{ s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(50); s3c_tcon_write_byte(0x05); //Write Read Status command r_data = s3c_tcon_read_byte(); s3c2410_gpio_setpin(S3C2410_GPL13, 1); //printk("r_data: 0x%02x\n", r_data); msleep(500); }while(r_data & 0x01); tcon_byte_count = 0; tcon_address = 0; break; case S3C_TCON_ID: printk("S3C_TCON_ID: "); s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(50); s3c_tcon_write_byte(0x9F); r_data = s3c_tcon_read_byte(); printk("0x%02X ", r_data); r_data = s3c_tcon_read_byte(); printk("0x%02X ", r_data); r_data = s3c_tcon_read_byte(); printk("0x%02X\n", r_data); s3c2410_gpio_setpin(S3C2410_GPL13, 1); break; case S3C_TCON_READ: printk("S3C_TCON_READ\n"); printk("\n\ns3c_tcon_read_content 1\n"); s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(50); s3c_tcon_write_byte(0x03); s3c_tcon_write_byte(0x00); s3c_tcon_write_byte(0x00); s3c_tcon_write_byte(0x00); for(i=0; i<300; i++){ printk("0x%02x\n", s3c_tcon_read_byte()); } s3c2410_gpio_setpin(S3C2410_GPL13, 1); msleep(50); printk("\n\ns3c_tcon_read_content 2\n"); s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(50); s3c_tcon_write_byte(0x03); s3c_tcon_write_byte(0x02); s3c_tcon_write_byte(0x0F); s3c_tcon_write_byte(0xA0); for(i=0; i<40; i++){ printk("0x%02x\n", s3c_tcon_read_byte()); } s3c2410_gpio_setpin(S3C2410_GPL13, 1); break; case S3C_TCON_BYTE_COUNT: tcon_byte_count = arg; //printk("S3C_TCON_BYTE_COUNT: %d\n", tcon_byte_count); break; case S3C_TCON_WRITE: //printk("+ S3C_TCON_WRITE: %d\n ", tcon_address); copy_from_user(tcon_buffer, arg, tcon_byte_count); s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(20); s3c_tcon_write_byte(0x06); //Setting Write Enable Latch bit s3c2410_gpio_setpin(S3C2410_GPL13, 1); udelay(20); s3c2410_gpio_setpin(S3C2410_GPL13, 0); udelay(20); //printk("0x%x 0x%x 0x%x\n", (tcon_address & 0xFF0000) >> 16, (tcon_address & 0xFF00) >> 8, (tcon_address & 0xFF)); s3c_tcon_write_byte(0x02); //Write Page Program command s3c_tcon_write_byte( (tcon_address & 0xFF0000) >> 16 ); s3c_tcon_write_byte( (tcon_address & 0xFF00) >> 8 ); s3c_tcon_write_byte( (tcon_address & 0xFF) ); for(i=0; idev); if (!mmc) ret = -ENOMEM; plat_data = s3c_hsmmc_get_platdata(&pdev->dev); //Qisda, Asaku Chen, 2009/08/20, for wifi power { if(plat_data->hwport==1){ if (register_chrdev (S3C_WIFI_MAJOR, "s3c_wifi_cmd", &s3c_wifi_fops)) { printk("unable to get major S3C_WIFI_MAJOR\n"); } } //Qisda, Asaku Chen, 2009/08/20, for wifi power } #ifdef CONFIG_QISDA_BK060B00 //Qisda, Ralph Chang, 2010/02/09, for bt power { if(plat_data->hwport==1){ if (register_chrdev (S3C_BT_MAJOR, "s3c_bt_cmd", &s3c_bt_fops)) { printk("unable to get major S3C_BT_MAJOR\n"); } } //Qisda, Asaku Chen, 2009/08/20, for wifi power } #endif host = mmc_priv(mmc); host->mmc = mmc; host->plat_data = plat_data; host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->mem) { printk("Failed to get io memory region resouce.\n"); ret = -ENOENT; goto probe_free_host; } host->mem = request_mem_region(host->mem->start, RESSIZE(host->mem), pdev->name); if (!host->mem) { printk("Failed to request io memory region.\n"); ret = -ENOENT; goto probe_free_host; } host->base = ioremap(host->mem->start, (host->mem->end - host->mem->start)+1); if (host->base == NULL) { printk(KERN_ERR "Failed to remap register block\n"); return -ENOMEM; } host->irq = platform_get_irq(pdev, 0); if (host->irq == 0) { printk("Failed to get interrupt resouce.\n"); ret = -EINVAL; goto untasklet; } DBG("platform_device(pdev): pdev(0x%p), name(%s)\n", pdev, pdev->name); DBG("s3c_hsmmc_host(host): mem->start(0x%08x), base(0x%p), size(%d)\n", host->mem->start, host->base, RESSIZE(host->mem)); DBG("s3c_hsmmc_cfg(plat_data): enabled(%d) hwport(%d)\n", plat_data->enabled, plat_data->hwport); /* To detect a card inserted on channel 0, an external interrupt is used. */ /* Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card { */ #ifdef CONFIG_SD_SW_DEBOUNCE if( plat_data->hwport == 0) { init_timer(&sd_detect_timer); sd_detect_timer.function=sdcard_debounce_timer; } #endif /* CONFIG_SD_SW_DEBOUNCE */ /* } Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card */ /* Qisda, howard hsu, 2010/01/21, fix start slot for SD card { */ if( plat_data->hwport == 0) { mmc->first_devidx = 1; } else { mmc->first_devidx = 0; } /* } Qisda, howard hsu, 2010/01/21, fix start slot for SD card */ if ((plat_data->enabled == 1) && (plat_data->hwport == 0)) { host->irq_cd = platform_get_irq(pdev, 1); if (host->irq_cd == 0) { printk("Failed to get interrupt resouce.\n"); ret = -EINVAL; goto untasklet; } #if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) set_irq_type(host->irq_cd, IRQT_BOTHEDGE); #elif defined(CONFIG_CPU_S3C6410) set_irq_type(host->irq_cd, IRQT_LOW); #endif } host->flags |= S3C_HSMMC_USE_DMA; #ifdef CONFIG_HSMMC_SCATTERGATHER #if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443) for (i=0; isub_block[i] = kmalloc(S3C_HSMMC_MALLOC_SIZE, GFP_KERNEL); #endif #endif s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL); /* register some clock sources if exist */ for (i=0; iclk[i] = clk_get(&pdev->dev, plat_data->clk_name[i]); if (IS_ERR(host->clk[i])) { ret = PTR_ERR(host->clk[i]); host->clk[i] = ERR_PTR(-ENOENT); } if (clk_enable(host->clk[i])) { host->clk[i] = ERR_PTR(-ENOENT); } if (!IS_ERR(host->clk[i])) { DBG("MMC clock source[%d], %s is %ld Hz\n",i, plat_data->clk_name[i], clk_get_rate(host->clk[i])); } } mmc->ops = &s3c_hsmmc_ops; mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; mmc->f_min = 400 * 1000; /* at least 400kHz */ /* you must make sure that our hsmmc block can support * up to 52MHz. by scsuh */ //mmc->f_max = 100 * MHZ; mmc->f_max = 25 * MHZ; mmc->caps = plat_data->host_caps; DBG("mmc->caps: %08lx\n", mmc->caps); printk(KERN_INFO "mmc->caps: %08lx\n", mmc->caps); spin_lock_init(&host->lock); /* * Maximum number of segments. Hardware cannot do scatter lists. * XXX: must modify later. by scsuh */ #ifdef CONFIG_HSMMC_SCATTERGATHER mmc->max_hw_segs = CONFIG_S3C_HSMMC_MAX_HW_SEGS; mmc->max_phys_segs = CONFIG_S3C_HSMMC_MAX_HW_SEGS; #else mmc->max_hw_segs = 1; #endif /* * Maximum number of sectors in one transfer. Limited by DMA boundary * size (512KiB), which means (512 KiB/512=) 1024 entries. */ //mmc->max_sectors = 128; /* 65535/512=128 */ /* * Maximum segment size. Could be one segment with the maximum number * of sectors. */ mmc->max_blk_size = 512; mmc->max_seg_size = 128 * mmc->max_blk_size; mmc->max_blk_count = 128; mmc->max_req_size = mmc->max_seg_size; init_timer(&host->timer); host->timer.data = (unsigned long)host; host->timer.function = s3c_hsmmc_check_status; host->timer.expires = jiffies + HZ; /* * Init tasklets. */ tasklet_init(&host->card_tasklet, s3c_hsmmc_tasklet_card, (unsigned long)host); #if 0 tasklet_init(&host->finish_tasklet, s3c_hsmmc_tasklet_finish, (unsigned long)host); #endif ret = request_irq(host->irq, s3c_hsmmc_irq, 0, DRIVER_NAME, host); if (ret) goto untasklet; #if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) if ((plat_data->enabled == 1) && (plat_data->hwport == 0)) { ret = request_irq(host->irq_cd, s3c_hsmmc_irq_cd, 0, DRIVER_NAME, host); if (ret) goto untasklet; enable_irq_wake(host->irq_cd); } #endif s3c_hsmmc_ios_init(host); DBG("before mmc_add_host()\n"); mmc_add_host(mmc); DBG("after mmc_add_host()\n"); #if defined(CONFIG_PM) global_host[plat_data->hwport] = host; #endif printk(KERN_INFO "[s3c_hsmmc_probe]: %s.%d: at 0x%p with irq %d. clk src:", pdev->name, pdev->id, host->base, host->irq); for (i=0; iclk[i])) printk(" %s", host->clk[i]->name); } printk("\n"); return 0; untasklet: tasklet_kill(&host->card_tasklet); /* tasklet_kill(&host->finish_tasklet); */ for (i=0; iclk[i] != ERR_PTR(-ENOENT)) { clk_disable(host->clk[i]); clk_put(host->clk[i]); } } probe_free_host: mmc_free_host(mmc); return ret; } static int s3c_hsmmc_remove(struct platform_device *dev) { struct mmc_host *mmc = platform_get_drvdata(dev); struct s3c_hsmmc_host *host = mmc_priv(mmc); int i; mmc = host->mmc; mmc_remove_host(mmc); s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL); for (i=0; iclk[i]); clk_put(host->clk[i]); } free_irq(host->irq, host); del_timer_sync(&host->timer); tasklet_kill(&host->card_tasklet); /* tasklet_kill(&host->finish_tasklet); */ #ifdef CONFIG_HSMMC_SCATTERGATHER #if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443) kfree(host->sub_block); #endif #endif mmc_free_host(mmc); return 0; } #ifdef CONFIG_PM static int s3c_hsmmc_suspend(struct platform_device *pdev, pm_message_t state) { // struct mmc_card *card; struct s3c_hsmmc_host *s3c_host = global_host[pdev->id]; struct mmc_host *host = s3c_host->mmc; #if 0 /* * Fix me later by jsgood */ list_for_each_entry(card, &host->cards, node) { if (card->state & MMC_STATE_PRESENT) card->state &= ~MMC_STATE_PRESENT; } #endif //USB20_EN //writel((readl(S3C2410_GPHCON) & ~(1<<29) | 1<<28), S3C2410_GPHCON); //writel((readl(S3C2410_GPHDAT) &~(1<<14)), S3C2410_GPHDAT); if(((readl(S3C2410_GPGDAT) &(1<<1))==0)&&((readl(S3C2410_GPHDAT) &(1<<11))==0)) { //if((readl(S3C2410_GPHDAT) &(1<<11))==0) { printk("\nUSB_UDC_DEVICE_s3c_hsmmc_suspend: usb\n"); // writel((readl(S3C2410_GPHCON) & ~(1<<29) | (1<<28)), S3C2410_GPHCON); s3c2410_gpio_pullup(S3C2443_GPH14,1); s3c2410_gpio_cfgpin(S3C2443_GPH14, S3C2443_GPH14_OUTP); s3c2410_gpio_setpin(S3C2443_GPH14, 0); /* usb power enbale */ s3c2410_gpio_pullup(S3C2410_GPD13,1); s3c2410_gpio_cfgpin(S3C2410_GPD13, S3C2410_GPD13_INP); } } if((readl(S3C2410_GPHDAT) &(1<<11))==0) { printk("\nUSB_UDC_DEVICE_s3c_hsmmc_suspend: 3g\n"); s3c2410_gpio_pullup(S3C2410_GPH11,1); s3c2410_gpio_cfgpin(S3C2410_GPH11, S3C2410_GPH11_OUTP); s3c2410_gpio_setpin(S3C2410_GPH11, 0); /* usb power enbale */ s3c2410_gpio_pullup(S3C2410_GPH8,1); s3c2410_gpio_cfgpin(S3C2410_GPH8, S3C2410_GPH8_OUTP); s3c2410_gpio_setpin(S3C2410_GPH8, 0); /* usb power enbale */ s3c2410_gpio_cfgpin(S3C2410_GPH8, S3C2410_GPH9_OUTP); s3c2410_gpio_setpin(S3C2410_GPH9, 0); /* usb power enbale */ // s3c2410_gpio_pullup(S3C2410_GPG3,1); // s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_OUTP); // s3c2410_gpio_setpin(S3C2410_GPG3, 0); /* usb power enbale */ } mmc_suspend_host(host, state); //mmc suspend GPIO config s3c_hsmmc_power_switch(s3c_host->plat_data->hwport, 0); /* Qisda, Howard, 2009/12/22, fix resume card-detect { */ if(s3c_host->plat_data->hwport==0) { inResume = 1; card_detect2 = 2; } /* } Qisda, Howard, 2009/12/22, fix resume card-detect */ return 0; } static int s3c_hsmmc_resume(struct platform_device *pdev) { struct s3c_hsmmc_host *s3c_host = global_host[pdev->id]; struct mmc_host *host = s3c_host->mmc; int read_pin; //USB20_EN //writel((readl(S3C2410_GPHCON) & ~(1<<29) | 1<<28), S3C2410_GPHCON); //writel((readl(S3C2410_GPHDAT) |(1<<14)), S3C2410_GPHDAT); if((readl(S3C2410_GPGDAT) &(1<<1))!=0) { printk("\nUSB_UDC_DEVICE_POWER_STATE: %d\n"); writel((readl(S3C2410_GPHCON) & ~(1<<29) | 1<<28), S3C2410_GPHCON); s3c2410_gpio_pullup(S3C2443_GPH14,2); s3c2410_gpio_cfgpin(S3C2443_GPH14,S3C2443_GPH14_OUTP); s3c2410_gpio_setpin(S3C2443_GPH14,1); /* usb power enbale */ } //PWREN_SD if(s3c_host->plat_data->hwport==0){ read_pin = readl(S3C2410_GPFDAT) & (1<<1); if(read_pin && !card_detect){ printk("\nNO CARD\n"); s3c_hsmmc_power_switch(0, 0); /* Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger { */ //set_irq_type(s3c_host->irq_cd, IRQT_FALLING); set_irq_type(s3c_host->irq_cd, IRQT_LOW); /* } Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger */ card_detect2 = 3; } else{ if(read_pin){ printk("no card\n"); /* Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger { */ //set_irq_type(s3c_host->irq_cd, IRQT_FALLING); set_irq_type(s3c_host->irq_cd, IRQT_LOW); /* } Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger */ card_detect = 0; //s3c_hsmmc_power_switch(0, 0); } else{ printk("card in\n"); /* Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger { */ //set_irq_type(s3c_host->irq_cd, IRQT_RISING); set_irq_type(s3c_host->irq_cd, IRQT_HIGH); /* } Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger */ card_detect = 1; //s3c_hsmmc_power_switch(0, 1); } card_detect2 = 2; s3c_hsmmc_power_switch(0, 1); s3c_hsmmc_ios_init(s3c_host); mmc_resume_host(host); } inResume = 0; //howard, 2009/12/22, fix resume card-detect } else if(s3c_host->plat_data->hwport==1){ s3c_hsmmc_power_switch(1, 1); s3c_hsmmc_ios_init(s3c_host); mmc_resume_host(host); } #if 0 /* Qisda, Daniel Lee, 2009/07/23, e600 { */ // For EVT0 // Set MMC insert/remove irq back. if(card_detect) set_irq_type(s3c_host->irq_cd, IRQT_RISING); else set_irq_type(s3c_host->irq_cd, IRQT_FALLING); /* Qisda, Daniel Lee, 2009/07/23, e600 } */ #endif /*}Qisda,2009/7/28,Leo SJ Yang*/ //s3c_hsmmc_ios_init(s3c_host); //mmc_resume_host(host); /*Qisda,2009/7/28,Leo SJ Yang {*/ /* Add SD Card,WIFI,USB power management*/ if(s3c_host->plat_data->hwport==0) { //card_detect=1; } /*}Qisda,2009/7/28,Leo SJ Yang*/ return 0; } #else #define s3c_hsmmc_suspend NULL #define s3c_hsmmc_resume NULL #endif static struct platform_driver s3c_hsmmc_driver = { .probe = s3c_hsmmc_probe, .remove = s3c_hsmmc_remove, .suspend = s3c_hsmmc_suspend, .resume = s3c_hsmmc_resume, .driver = { .name = "s3c-hsmmc", .owner = THIS_MODULE, }, }; static int __init s3c_hsmmc_drv_init(void) { //Disable SD power s3c_hsmmc_power_switch(0, 0); /*Qisda,2009/7/28,Leo SJ Yang{*/ //s3c2410_gpio_cfgpin(S3C2410_GPF1,S3C2410_GPF1_EINT1); s3c2410_gpio_cfgpin(S3C2410_GPF1,S3C2410_GPF1_INP); s3c2410_gpio_pullup(S3C2410_GPF1, 2); /*}Qisda,2009/7/28,Leo SJ Yang*/ return platform_driver_register(&s3c_hsmmc_driver); } static void __exit s3c_hsmmc_drv_exit(void) { platform_driver_unregister(&s3c_hsmmc_driver); } void s3c_moviNAND_power_off(void) { struct s3c_hsmmc_host *host = global_host[1]; printk("s3c_moviNAND_power_off"); s3c_hsmmc_writeb(S3C_HSMMC_POWER_OFF, S3C_HSMMC_PWRCON); } module_init(s3c_hsmmc_drv_init); module_exit(s3c_hsmmc_drv_exit); MODULE_DESCRIPTION("S3C SD HOST I/F 1.0 and 2.0 Driver"); MODULE_LICENSE("GPL");