📄 mmc_samsung.c
字号:
/* * 2002 (C) Copyrigt SAMSUMNG ELECTRONICS * SW.LEE <hitchcar@sec.samsung.com> * * Low-level MMC functions for the S3CXX * * Copyright 2002 Hewlett-Packard Company * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is * preserved in its entirety in all copies and derived works. * * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS * FITNESS FOR ANY PARTICULAR PURPOSE. * * Many thanks to Alessandro Rubini and Jonathan Corbet! * * Author: Andrew Christian * 6 May 2002 * SW.LEE <hitchcar@sec.samsung.com> * Wrote MMC driver for S3C2410 * Jamey Hicks <jamey.hicks@hp.com> July 7, 2003 * Generalized the S3C2410 MMC driver for other instantiations of this samsung asic core. */#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <asm/irq.h> #include <asm/unaligned.h>#include <asm/arch/hardware.h>#include <asm/io.h> /* ioremap *///#include <linux/mmc/mmc_ll.h>#include "mmc_ll.h"//#include <asm/arch/h5400-gpio.h>//#include <asm/arch/h5400-asic.h>//#include <asm/arch/h5400-irqs.h>#include "mmc_samsung.h"#include "sdi_reg_s3c2410.h"#undef ASSERT#define ASSERT(expr) if(!(expr)) { printk(" Assertion failed ! %s, %s,%s,line=%d\n", #expr,__FILE__,__FUNCTION__,__LINE__);}struct s3c_mmc_lowlevel *lowlevel;/*#define mmc_enable_irq(x)#define mmc_disable_irq(x)*/struct response_info { int length; u16 flags;};static struct response_info rinfo[] = { { 0, MMC_CMD_DATA_CONT_FORMAT_NO_RESPONSE }, /* R0 */ { 6, MMC_CMD_DATA_CONT_FORMAT_R1 }, /* R1 */ { 6, MMC_CMD_DATA_CONT_FORMAT_R1 | MMC_CMD_DATA_CONT_BUSY_BIT }, /* R1b */ { 17, MMC_CMD_DATA_CONT_FORMAT_R2 }, /* R2 CID */ { 17, MMC_CMD_DATA_CONT_FORMAT_R2 }, /* R2 CSD */ { 6, MMC_CMD_DATA_CONT_FORMAT_R3 }, /* R3 */ { 6, MMC_CMD_DATA_CONT_FORMAT_R4 }, /* R4 */ { 6, MMC_CMD_DATA_CONT_FORMAT_R5 }, /* R5 */ { 6, MMC_CMD_DATA_CONT_FORMAT_R6 }, /* R6 */};enum s3c_request_type { RT_NO_RESPONSE, RT_RESPONSE_ONLY, RT_READ, RT_WRITE};struct s3c_mmc_data { struct timer_list sd_detect_timer; struct timer_list reset_timer; struct timer_list irq_timer; /* Panic timer - make sure we get a response */ u32 clock; /* Current clock frequency */ struct mmc_request *request; enum s3c_request_type type; /* used in s3c_set_transfer() */ u32 size2send; /* MAX(datasize)= request->nob * request->block_len */ u32 size2read; /* read byts from SD Card */ u32 insert ;};/*static*/ struct s3c_mmc_data g_s3c_mmc_data;EXPORT_SYMBOL(g_s3c_mmc_data);#define MMC_IRQ_TIMEOUT (3*HZ)static __inline__ void mmc_delay(void) { mdelay(100); }/************************************************************************** * Utility routines for debuging **************************************************************************/struct cmd_to_name { int id; char *name;};static struct cmd_to_name cmd_names[] = { { MMC_CIM_RESET, "CIM_RESET" }, { MMC_GO_IDLE_STATE, "GO_IDLE_STATE" }, { MMC_SEND_OP_COND, "SEND_OP_COND" }, { MMC_ALL_SEND_CID, "ALL_SEND_CID" }, { MMC_SET_RELATIVE_ADDR, "SET_RELATIVE_ADDR" }, { MMC_SET_DSR, "SET_DSR" }, { MMC_SELECT_CARD, "SELECT_CARD" }, { MMC_SEND_CSD, "SEND_CSD" }, { MMC_SEND_CID, "SEND_CID" }, { MMC_READ_DAT_UNTIL_STOP, "READ_DAT_UNTIL_STOP" }, { MMC_STOP_TRANSMISSION, "STOP_TRANSMISSION" }, { MMC_SEND_STATUS , "SEND_STATUS " }, { MMC_GO_INACTIVE_STATE, "GO_INACTIVE_STATE" }, { MMC_SET_BLOCKLEN, "SET_BLOCKLEN" }, { MMC_READ_SINGLE_BLOCK, "READ_SINGLE_BLOCK" }, { MMC_READ_MULTIPLE_BLOCK, "READ_MULTIPLE_BLOCK" }, { MMC_WRITE_DAT_UNTIL_STOP, "WRITE_DAT_UNTIL_STOP" }, { MMC_SET_BLOCK_COUNT, "SET_BLOCK_COUNT" }, { MMC_WRITE_BLOCK, "WRITE_BLOCK" }, { MMC_WRITE_MULTIPLE_BLOCK, "WRITE_MULTIPLE_BLOCK" }, { MMC_PROGRAM_CID, "PROGRAM_CID" }, { MMC_PROGRAM_CSD, "PROGRAM_CSD" }, { MMC_SET_WRITE_PROT, "SET_WRITE_PROT" }, { MMC_CLR_WRITE_PROT, "CLR_WRITE_PROT" }, { MMC_SEND_WRITE_PROT, "SEND_WRITE_PROT" }, { MMC_ERASE_GROUP_START, "ERASE_GROUP_START" }, { MMC_ERASE_GROUP_END, "ERASE_GROUP_END" }, { MMC_ERASE, "ERASE" }, { MMC_FAST_IO, "FAST_IO" }, { MMC_GO_IRQ_STATE, "GO_IRQ_STATE" }, { MMC_LOCK_UNLOCK, "LOCK_UNLOCK" }, { MMC_APP_CMD, "APP_CMD" }, { MMC_GEN_CMD, "GEN_CMD" },};static char * get_cmd_name( int cmd ){ int i; int len = sizeof(cmd_names) / sizeof(struct cmd_to_name); for ( i = 0 ; i < len ; i++ ) if ( cmd == cmd_names[i].id ) return cmd_names[i].name; return "UNKNOWN";}struct status_bit_to_name { u16 mask; int shift; char *name;};struct status_bit_to_name cmd_status_bit_names[] = { {MMC_STATUS_CRC_RESPONSE_ERROR ,-1," MMC_STATUS_CRC_RESPONSE_ERROR"}, {MMC_STATUS_CMD_SENT_DONE ,-1," MMC_STATUS_CMD_SENT_DONE"}, {MMC_STATUS_RESPONSE_TIMEOUT ,-1,"MMC_STATUS_RESPONSE_TIMEOUT"}, {MMC_STATUS_RESPONSE_RECV_DONE ,-1,"MMC_STATUS_RESPONSE_RECV_DONE"}, {MMC_STATUS_TRANS_ON ,-1,"MMC_STATUS_TRANS_ON"}, {MMC_STATUS_RESPONSE_INDEX ,-1,"MMC_STATUS_RESPONSE_INDEX"}};struct status_bit_to_name data_status_bit_names[] = { { MMC_STATUS_RD_WAIT ,-1, "MMC_STATUS_RD_WAIT"}, { MMC_STATUS_IO_INT_DETECT ,-1, "MMC_STATUS_RD_WAIT"}, { MMC_STATUS_IO_INT_DETECT ,-1, "MMC_STATUS_IO_INT_DETECT"}, { MMC_STATUS_FIFO_ERROR ,-1, "MMC_STATUS_IO_INT_DETECT"}, { MMC_STATUS_CRC_WRITE_ERROR ,-1, "MMC_STATUS_CRC_WRITE_ERROR"}, { MMC_STATUS_CRC_READ_ERROR ,-1, "MMC_STATUS_CRC_READ_ERROR"}, { MMC_STATUS_DATA_BUSY_TIMEOUT ,-1, "MMC_STATUS_DATA_BUSY_TIMEOUT"}, { MMC_STATUS_DATA_TRANSFER_DONE,-1, "MMC_STATUS_DATA_TRANSFER_DONE"}, { MMC_STATUS_BUSY_FINISH ,-1, "MMC_STATUS_BUSY_FINISH"}, { MMC_STATUS_STARTBIT_ERROR ,-1, "MMC_STATUS_BUSY_FINISH"}, { MMC_STATUS_TX_DATA_ON_PROGRESS,-1,"MMC_STATUS_BUSY_FINISH"}, { MMC_STATUS_RX_DATA_ON_PROGRESS,-1,"MMC_STATUS_BUSY_FINISH"}};struct status_bit_to_name fifo_status_bit_names[] = { { MMC_STATUS_FIFO_READY_FOR_TX ,-1,"MMC_STATUS_FIFO_READY_FOR_TX" }, { MMC_STATUS_FIFO_READY_FOR_RX ,-1,"MMC_STATUS_FIFO_READY_FOR_RX" }, { MMC_STATUS_FIFO_TX_HALF_FULL ,-1,"MMC_STATUS_FIFO_TX_HALF_FULL" }, { MMC_STATUS_FIFO_EMPTY ,-1,"MMC_STATUS_FIFO_EMPTY" }, { MMC_STATUS_FIFO_RX_LAST_DATA ,-1,"MMC_STATUS_FIFO_RX_LAST_DATA" }, { MMC_STATUS_FIFO_RX_FULL ,-1,"MMC_STATUS_FIFO_RX_FULL" }, { MMC_STATUS_FIFO_RX_HALF_FULL ,-1,"MMC_STATUS_FIFO_RX_HALF_FULL" }, { MMC_STATUS_FIFO_COUNT_MASK ,-1,"MMC_STATUS_FIFO_COUNT" },};/******* * Prototype static void decode_status( u16 status ) * Purpose: * 1. verify SD Command status / SD Data Status * FIFO status * Entry * Exit * Exceptions: *****************************************************/static void decode_status( u16 status[]){ int i; int cmd_len = sizeof( cmd_status_bit_names)/sizeof(struct status_bit_to_name); int data_len = sizeof(data_status_bit_names)/sizeof(struct status_bit_to_name); int fifo_len = sizeof(fifo_status_bit_names)/sizeof(struct status_bit_to_name); struct status_bit_to_name *b = cmd_status_bit_names; for ( i = 0 ; i <cmd_len ; i++, b++ ) { if ( status[0] & b->mask ) { if ( b->shift >= 0 ) printk("%s[%d] ",b->name,((status[0] & b->mask) >> b->shift)); else printk("%s ", b->name); } } b = data_status_bit_names; for ( i = 0 ; i <data_len ; i++, b++ ) { if ( status[1] & b->mask ) { if ( b->shift >= 0 ) printk("%s[%d] ",b->name,((status[1]& b->mask)>> b->shift)); else printk("%s ",b->name); } } b = fifo_status_bit_names; for ( i = 0 ; i <fifo_len ; i++, b++ ) { if ( status[2]& b->mask ) { if ( b->shift >= 0 ) printk("%s[%d] ",b->name,((status[2]&b->mask)>> b->shift)); else printk("%s ", b->name); } }}/******* * Prototype * static void mmc_s3c_reset_timeout( unsigned long nr ) * Purpose: * 1. timeout + reset * Entry * Exit * Exceptions: * I don't know how to implement but * 1. clock down or slow * 2. FIFO RESETw * 3. MMC Interrupt disable and enable *****************************************************/static void mmc_s3c_reset_timeout( unsigned long nr ){// struct s3c_mmc_data *sd = (struct s3c_mmc_data *) nr; /* Send 80 clocks to get things started *//*#ifdef CONFIG_MMC_DEBUG START_MMC_DEBUG(2) { u16 status = S3C_ASIC1_MMC_Status; printk(__FUNCTION__ ": enabling irq mask=%04x status=0x%04x (", S3C_ASIC1_MMC_InterruptMask, status); decode_status(status); printk(")\n"); } END_MMC_DEBUG;#endif*/// mod_timer( &g_s3c_mmc_data.irq_timer, jiffies + MMC_IRQ_TIMEOUT); }static void mmc_s3c_start_clock(void){ rSDICON |= CLOCK_OUT_EN;}/******* * Prototype * static int mmc_s3c_stop_clock(void) * * Purpose: * * Entry * Exit * Exceptions: * must add routine to check the status register fields. *****************************************************************/static int mmc_s3c_stop_clock(void){ //rSDIDCON &= ~CMD_START; rSDICON &= ~CLOCK_OUT_EN; return MMC_NO_ERROR;}static int mmc_s3c_set_clock( u32 rate ){ int retval; int prescaler = 0; /* Does not seem to work consistently if clock rate is above 10000000 */ if ( 10000000 < rate ) { rate = 10000000; } prescaler = (2*PCLK)/(rate) - 1; retval = mmc_s3c_stop_clock(); if ( retval ) return retval; rSDIPRE=prescaler; mmc_s3c_start_clock(); g_s3c_mmc_data.clock = rate ; return MMC_NO_ERROR;}/******* * Prototype * static int mmc_s3c_adjust_clock( u32 clock_rate ) * * Purpose: * * Entry * Exit * Exceptions: * BAUD RATE = PCLK/2/(PRESCALER +1) * rSDIPRE = PRESCALER( 8 bits) *******************************************************************/static int mmc_s3c_adjust_clock( u32 clock_rate ){ int retval; retval = mmc_s3c_set_clock( clock_rate ); mmc_s3c_start_clock(); return retval;}/******* * Prototype * staitc void sd_status_clear(int index ) * Purpose: * Entry: * 0 : cmd status reg. * 1 : data status reg. * 2 : fifo status reg. * Exit * Exceptions: * if you want to advance performance ,get rid of this func. *****************************************************************/static void sd_status_clear(int index){ switch ( index ) { case 0: rSDICSTA = MMC_CMD_STATUS_WRITEONE_CLEAR; break; case 1: rSDICSTA = MMC_CMD_STATUS_WRITEONE_CLEAR; rSDIDSTA = MMC_DATA_STATUS_WRITEONE_CLEAR; break; case 2: rSDICSTA = MMC_CMD_STATUS_WRITEONE_CLEAR; rSDIDSTA = MMC_DATA_STATUS_WRITEONE_CLEAR; //rSDIFSTA = MMC_FIFO_STATUS_WRITEONE_CLEAR; /* Read Only */ break; default: break; }}/******* * Prototype * staitc int chk_CMDend(int cmd, int be_resp) * Purpose: * 1. CMD0 use this function. * 2. Debug * After issuing one command , you can see response chk_CMDend * while making busy-waiting. * Entry * Exit * Exceptions: * not sure whether other command needs chk_CMDend func. *****************************************************************/static int chk_CMDend(int cmd, int be_resp){ int status; #define CONFIG_MMC_SPINLOOP_TIMEOUT 10000000 MMC_DEBUG(2, ""); if(!be_resp) { int count = 0; status = rSDICSTA; while ( !(status & MMC_STATUS_CMD_SENT_DONE) && (count++ < CONFIG_MMC_SPINLOOP_TIMEOUT)) { status = rSDICSTA; } MMC_DEBUG(2," STATUS=0x%08X",rSDICSTA); if (count >= CONFIG_MMC_SPINLOOP_TIMEOUT) { MMC_DEBUG(0, "cmd timed out status=%#x", status); return -EIO; } MMC_DEBUG(2," Ending Progressing"); sd_status_clear(0); return 0; } else // With response { int count = 0; status=rSDICSTA; while ( !(status&MMC_STATUS_CMD_SENT_DONE)&&!(status&MMC_STATUS_RESPONSE_RECV_DONE) && (count++ < CONFIG_MMC_SPINLOOP_TIMEOUT)) { printk("."); status = rSDICSTA; } if(cmd==MMC_SEND_OP_COND || cmd==MMC_SEND_CSD || cmd==MMC_READ_DAT_UNTIL_STOP) // CRC no check { if( (status&0xf00) != 0xa00 ) // Check error { rSDICSTA=status; // Clear error state if(((status&0x400)==0x400)) { MMC_DEBUG(2," TIMEOUT ERROR"); return -EIO; // Timeout error } else { MMC_DEBUG(2," RESPONSE OK "); } } rSDICSTA=status; // Clear cmd & rsp end state } else // CRC check { if( (status&0x1f00) != 0xa00 ) // Check error { MMC_DEBUG(2," ERROR CMD%d:rSDICSTA=0x%x, rSDIRSP0=0x%x",cmd, rSDICSTA, rSDIRSP0); rSDICSTA=status; // Clear error state if(((status&0x400)==0x400)) return -EIO; // Timeout error } rSDICSTA=status; } return 0; }}static void mmc_CMD0(void) { MMC_DEBUG(2,""); rSDICARG = 0x0; rSDICCON = START_TRANSMISSION_PRE; mmc_s3c_start_clock (); rSDICCON |= CMD_START; mdelay(1); chk_CMDend(0,0);}/******* * Prototype * static int mmc_s3c_reset( struct s3c_mmc_data *sd ) * * Purpose: * 1. mmc_cim_init_stack(struct mmc_dev *dev,int first ) issue "MMC_CIM_RESET" * 2. implement "CMD0" * Entry * Exit * Exceptions: * It is nessecary to get clock slow that obtains the sufficient response time. *****************************************************************************/static int mmc_s3c_reset( struct s3c_mmc_data *sd ){ int retval = 0 ; MMC_DEBUG(2,"");// retval = mmc_s3c_adjust_clock( MMC_CLOCK_SLOW ); g_s3c_mmc_data.type = RT_NO_RESPONSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -