📄 s3c_mmc.c
字号:
mod_timer( &mmc_data->sd_detect_timer, jiffies + (250 * HZ) / 1000 );}/******* * Prototype * static void s3c_mmc_split_status( struct s3c_mmc_data *sd, u16 status[], int timeout ) * Purpose: * 1. status[SDI_CMD]= command status * status[SDI_DATA]= data status * status[SDI_FIFO]= FIFO status * 2. collect the same sorts of interrupts. * Entry * Exit * timeout not used * Exceptions: * where is timeout routine ? * rSDIIMSK = S3C_ALL_MMC_INT_MASK is right ? * * 1. In MMC mode, MAX clock rate is 10 Mhz * 2. In MMC write mode(response only, ex cmd1), the CRC error is * occured in spite of correct writing. * 3. In case of long response, the CRC error should be detected after receiving * exact response data from SD device. User should check the CRC of received response * by software. * 4. Supposed that RESPONSE State and DATA RECEIVE don't appeare simultaneously. ********************************************************************************************/static void s3c_mmc_split_status( struct s3c_mmc_data *sd, u16 status[], int timeout ){ int retval = MMC_NO_ERROR; int rsp_done = 0,msk_reg =0,data_busy=0; if ((status[SDI_CMD] & MMC_STATUS_RESPONSE_TIMEOUT ) || (status[SDI_DATA]& MMC_STATUS_DATA_BUSY_TIMEOUT) ) { MMC_DEBUG(0," MMC_ERROR_TIMEOUT "); /*s3c_mmc_get_response(sd->request);*/ retval = MMC_ERROR_TIMEOUT; goto terminate_int; } if ((status[SDI_CMD] & MMC_STATUS_CRC_RESPONSE_ERROR ) || (status[SDI_DATA]& (MMC_STATUS_CRC_WRITE_ERROR|MMC_STATUS_CRC_READ_ERROR)) ) { if (/* Slot device is MMC && see Exception 2.*/ g_s3c_mmc_data.type == RT_RESPONSE_ONLY) { MMC_DEBUG(0," DUMMY MMC_ERROR_CRC "); } else { MMC_DEBUG(0," MMC_ERROR_CRC "); retval = MMC_ERROR_CRC; goto terminate_int; } } if ( status[SDI_CMD]& MMC_STATUS_RESPONSE_RECV_DONE && sd->request->result == MMC_NO_RESPONSE /* not yet response receveied. */ ) { rsp_done = 1; MMC_DEBUG(0," get_response "); s3c_mmc_get_response( sd->request ); } /** TODO :Fix me... * Read operation data error * Where do I handle "data error" not CRC Error--> I don't know */ if ((g_s3c_mmc_data.type==RT_READ) ){ if ( status[SDI_FIFO]&MMC_STATUS_FIFO_RX_FULL) { s3c_mmc_receive_data( sd->request,-1 ); } else { if (status[SDI_FIFO]&MMC_STATUS_FIFO_RX_LAST_DATA) { s3c_mmc_receive_data( sd->request, status[SDI_FIFO]&MMC_STATUS_FIFO_COUNT_MASK ); } else { if (rsp_done){ } else { MMC_DEBUG(0,": ERROR READ COMMAND 0x%08X,0x%08X,0x%08X" ,status[SDI_CMD],status[SDI_DATA],status[SDI_FIFO]); } } } } if (g_s3c_mmc_data.type==RT_WRITE && status[SDI_DATA]&MMC_STATUS_DATA_BUSY_TIMEOUT) { MMC_DEBUG(2," Not BUSY Finish"); data_busy =1; } if ((g_s3c_mmc_data.type==RT_WRITE)&& !rsp_done && !data_busy) { MMC_DEBUG(2, "nob=%d, counter=0x%08X", sd->request->nob, rSDIDCNT); if (!(status[SDI_DATA]&MMC_STATUS_DATA_TRANSFER_DONE)) { if (sd->request->nob>0) { if ((status[SDI_FIFO]&MMC_STATUS_FIFO_TX_HALF_FULL) || (status[SDI_FIFO]&MMC_STATUS_FIFO_EMPTY)) { s3c_mmc_transmit_data(sd->request); } } } } switch (g_s3c_mmc_data.type) { case RT_NO_RESPONSE: MMC_DEBUG(2," RT_NO_RESPONSE "); msk_reg = S3C_ALL_MMC_INT_MASK; break; case RT_RESPONSE_ONLY: MMC_DEBUG(2," RT_RESPONSE_ONLY "); if ( sd->request->result < 0 ) { printk(KERN_INFO "%s: illegal interrupt - command hasn't finished\n", __FUNCTION__); retval = MMC_ERROR_TIMEOUT; } msk_reg = S3C_ALL_MMC_INT_MASK; break; case RT_READ: if(sd->request->nob <=0 ) { msk_reg = S3C_ALL_MMC_INT_MASK; break; } /* It is impossible to get both response and data */ if ( sd->request->nob ) { msk_reg = S3C_RX_FIFO_LAST_DATA_INT_ON | S3C_RX_FIFO_FULL_INT_ON; MMC_DEBUG(2,": read SDIIMSK mask=0x%08x",rSDIIMSK); /*mod_timer( &sd->irq_timer, jiffies + MMC_IRQ_TIMEOUT); */ /*s3c_mmc_start_clock();*/ /* redundancy ? */ rSDIIMSK = msk_reg; return; } if(rsp_done) { MMC_DEBUG(2,": READ RESPONSE DONE "); MMC_DEBUG(2,": REGS sdicon 0x%08X sdiccon %08X sdidcon 0x%08X" ,rSDICON,rSDICCON,rSDIDCON); MMC_DEBUG(2,": REGS cmd 0x%08X data 0x%08X fifo 0x%08X",rSDICSTA,rSDIDSTA,rSDIFSTA); MMC_DEBUG(2,": REGS cmd 0x%08X data 0x%08X fifo 0x%08X",rSDICSTA,rSDIDSTA,rSDIFSTA); msk_reg = S3C_RX_FIFO_LAST_DATA_INT_ON | S3C_RX_FIFO_FULL_INT_ON; rSDIIMSK = msk_reg; return; } MMC_DEBUG(2," Unexpected Error "); break; case RT_WRITE: if( sd->request->nob <= 0) { if (!(status[SDI_DATA]& MMC_STATUS_DATA_TRANSFER_DONE)) { msk_reg = S3C_DATA_CNT_ZERO_INT_ON| S3C_BUSY_COMPLETE_INT_ON; s3c_mmc_start_clock(); /* redundancy ? */ rSDIIMSK = msk_reg; return; } else { msk_reg = S3C_ALL_MMC_INT_MASK; break; } } if( (sd->request->nob || !(status[SDI_DATA]& MMC_STATUS_DATA_TRANSFER_DONE)) && !data_busy ){ msk_reg = S3C_DATA_CNT_ZERO_INT_ON|S3C_BUSY_COMPLETE_INT_ON| S3C_TX_FIFO_EMPTY_INT_ON; MMC_DEBUG(2,": write SDIIMSK mask=0x%08x",rSDIIMSK); /*mod_timer( &sd->irq_timer, jiffies + MMC_IRQ_TIMEOUT); */ s3c_mmc_start_clock(); /* redundancy ? */ rSDIIMSK = msk_reg; return; } if(data_busy) { msk_reg = S3C_BUSY_COMPLETE_INT_ON; } else { msk_reg = S3C_ALL_MMC_INT_MASK; } break; } /*MMC_DEBUG(2,": terminating status=0x%04x", S3C_ASIC1_MMC_Status );*/terminate_int: rSDIIMSK = msk_reg; /*del_timer_sync( &sd->irq_timer );*/ sd->request->result = retval; MMC_DEBUG(2," Before mmc_cmd_complete "); mmc_cmd_complete( sd->request ); sd->request = NULL;}/******* * Prototype * static void s3c_mmc_general_mmc_interrupt(int irq, void *dev_id, struct pt_regs *regs) * * Purpose: * 1. Analysis Command,Data and Fifo Status of SD/MMC registers * Entry * Exit * Exceptions: *****************************************************/static void s3c_mmc_general_mmc_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct s3c_mmc_data *sd = (struct s3c_mmc_data *) dev_id; /* Disable irq */ disable_irq( irq ); u16 status[SDI_STATUS_LENGTH]= {0}; status[SDI_CMD] = rSDICSTA; status[SDI_DATA] = rSDIDSTA; status[SDI_FIFO] = rSDIFSTA; sd_status_clear(2); MMC_DEBUG(2, " PRE %x rSDIIMSK 0x%08X cmd 0x%08X data 0x%08X fifo 0x%08X",rSDIPRE,rSDIIMSK,status[0],status[1],status[2]); MMC_DEBUG(2, " rSDICON 0x%08X CCON 0x%08X DCONdata 0x%08X ",rSDICON,rSDICCON,rSDIDCON); /* this can happen if there is an old interrupt hanging around when the irq is registered */ if (sd->request == NULL) { MMC_DEBUG(2, " rSDIIMSK 0x%08X cmd 0x%08X data 0x%08X fifo 0x%08X",rSDIIMSK,status[0],status[1],status[2]); MMC_DEBUG(2, " rSDICON 0x%08X CCON 0x%08X DCONdata 0x%08X ",rSDICON,rSDICCON,rSDIDCON); rSDIFSTA = rSDIFSTA; printk ("ignoring mmc irq with null request\n"); enable_irq( irq ); return; } s3c_mmc_split_status( sd, status, 0 ); /* Clear the interrupt. Must write a one to the src pending register then the interrupt pending register. */ SRCPND |= (1 << IRQ_SDI); INTPND |= (1 << IRQ_SDI); /* Enable the irq */ enable_irq( irq );}static int s3c_mmc_slot_is_empty( int slot ){ int is_empty = lowlevel->slot_is_empty(lowlevel); return is_empty?1:0;}/************************************************************************** * * Hardware initialization * *************************************************************************/static void s3c_mmc_slot_up( void ) { MMC_DEBUG(1," before slot_up"); /* Set up timers */ g_s3c_mmc_data.sd_detect_timer.function = s3c_mmc_detect_handler; g_s3c_mmc_data.sd_detect_timer.data = (unsigned long) &g_s3c_mmc_data; init_timer(&g_s3c_mmc_data.sd_detect_timer); /* Important notice for MMC test condition */ /* Cmd & Data lines must be enabled pull up resister */ rSDICON = SDMMC_RESET; /* Set block size to 512 bytes */ rSDIBSIZE = 0x200; rSDIDTIMER=0x7fffff; /* Disable SDI interrupt */ rSDIIMSK = 0x0; /* Type B, FIFO reset, SD clock enable */ rSDICON = CLOCK_TYPE_MMC | BYTE_ORDER_TYPE_L |CLOCK_OUT_EN ; rSDIFSTA=rSDIFSTA|(1<<16); /* clear all */ rSDIDSTA = MMC_STATUS_DATA_ALL; s3c_mmc_set_clock(MMC_CLOCK_SLOW); /* Enable SMDK SD clock */ CLKCON |= lowlevel->sdi_clock; /* Wait for power-up */ mdelay(125);}static void s3c_mmc_slot_down( void ){ del_timer_sync(&g_s3c_mmc_data.sd_detect_timer); if (lowlevel->slot_cleanup) lowlevel->slot_cleanup(lowlevel); /* Disable SMDK SD clock */ CLKCON &= ~(lowlevel->sdi_clock);}static int s3c_mmc_suspend(void){ s3c_mmc_stop_clock(); s3c_mmc_slot_down(); return 0;}static void s3c_mmc_resume(void){ s3c_mmc_slot_up(); /*s3c_mmc_set_clock(g_s3c_mmc_data.clock);*/ /* Set up timers */ g_s3c_mmc_data.sd_detect_timer.function = s3c_mmc_detect_handler; g_s3c_mmc_data.sd_detect_timer.data = (unsigned long) &g_s3c_mmc_data; init_timer(&g_s3c_mmc_data.sd_detect_timer); enable_irq ( lowlevel->sdi_irq ); enable_irq ( lowlevel->detect_irq ); /* Enable card detect IRQ */}#ifdef CONFIG_PMstatic int s3c_mmc_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data){ switch(req) { case PM_SUSPEND: mmc_eject(0); s3c_mmc_suspend(); break; case PM_RESUME: s3c_mmc_resume(); s3c_mmc_fix_sd_detect(0); break; } return 0;}#endif/*=>CEE LDM*/static int s3c_mmc_dpm_suspend(struct device *dev, u32 state, u32 level){ printk("s3c dpm suspend state %d, level %d",state,level); switch(level) { case SUSPEND_POWER_DOWN: /* Turn off power to MMC */ s3c_mmc_suspend(); break; } return 0;}static int s3c_mmc_dpm_resume(struct device *dev, u32 level){ printk("s3c dpm resume level %d",level); switch(level) { case RESUME_POWER_ON: /* Turn on power to MMC */ s3c_mmc_resume(); mmc_eject(0); s3c_mmc_fix_sd_detect(0); break; } return 0;}/*CEE LDM=>*/static int s3c_mmc_slot_init( void ){ int retval; MMC_DEBUG(1,"s3c_mmc_slot_init"); /* Basic service interrupt */ s3c_mmc_slot_up(); retval = request_irq( lowlevel->sdi_irq, s3c_mmc_general_mmc_interrupt, SA_INTERRUPT, "s3c_mmc_int", &g_s3c_mmc_data ); if ( retval ) { printk(KERN_CRIT "%s: unable to grab MMC IRQ %d\n", __FUNCTION__, lowlevel->sdi_irq); return retval; } retval = write_gpio_bit(lowlevel->sdi_gpio_cd,0); set_external_irq(lowlevel->detect_irq, lowlevel->detect_irq_edge, lowlevel->detect_irq_pullup); retval = request_irq( lowlevel->detect_irq, s3c_mmc_detect_isr, SA_INTERRUPT, "s3c_mmc_sd_detect", &g_s3c_mmc_data ); if ( retval ) { printk(KERN_CRIT "%s: unable to grab SD_DETECT IRQ %d\n", __FUNCTION__, lowlevel->detect_irq); free_irq(lowlevel->sdi_irq, &g_s3c_mmc_data); } return retval;}static void s3c_mmc_slot_cleanup( void ){ mmc_eject(0); s3c_mmc_slot_down(); free_irq(lowlevel->sdi_irq, &g_s3c_mmc_data); free_irq(lowlevel->detect_irq, &g_s3c_mmc_data);}/***********************************************************/static struct mmc_slot_driver dops = { .owner = THIS_MODULE, .name = "S3C24X0 MMC", .ocr = 0x00ff8000, .flags = MMC_SDFLAG_MMC_MODE, .init = s3c_mmc_slot_init, .cleanup = s3c_mmc_slot_cleanup, .is_empty = s3c_mmc_slot_is_empty, .send_cmd = s3c_mmc_send_command, .set_clock = s3c_mmc_set_clock,};static int __init s3c_mmc_init(void){ int retval; /*=>CEE LDM*/ s3c_mmc_ldm_register(); /*CEE LDM=>*/#ifdef CONFIG_PM pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, s3c_mmc_pm_callback);#endif#if defined(CONFIG_ARCH_S3C24A0) lowlevel = &s3c24a0_mmc_lowlevel;#elif defined(CONFIG_ARCH_S3C2440) lowlevel = &s3c2440_mmc_lowlevel;#endif retval = mmc_register_slot_driver(&dops, 1); if ( retval < 0 ) printk(KERN_INFO "MMC: unable to register slot\n"); return retval;}static void __exit s3c_mmc_cleanup(void){#ifdef CONFIG_PM pm_unregister_all(s3c_mmc_pm_callback);#endif /*=>CEE LDM*/ s3c_mmc_ldm_unregister(); /*CEE LDM=>*/ mmc_unregister_slot_driver(&dops);}module_init(s3c_mmc_init);module_exit(s3c_mmc_cleanup);MODULE_AUTHOR("Naushad");MODULE_DESCRIPTION("Samsung S3C24X0 MMC Controller");MODULE_LICENSE("GPL");EXPORT_NO_SYMBOLS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -