📄 s3c_mmc.c
字号:
/* * BRIEF MODULE DESCRIPTION * Low-level MMC functions for S3C24A0 and S3C2440 MMC controllers * Based on: omap_mmc.c and mmc driver for s3c2410 for ipaq * * Copyright 2002 Hewlett-Packard Company * * Copyright 2003 MontaVista Software Inc. * Author: MontaVista Software, Inc. * source@mvista.com * * Modified for the S3C24A0 by Naushad naushad@samsung.com: * Copyright (c) 2005 Samsung Electronics. * * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>/*#include <linux/irq.h>*/#include <linux/delay.h>#include <linux/timer.h>#include <linux/mmc/mmc_ll.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/arch/irq.h>#include <asm/arch/clocks.h>#include <linux/pm.h>#include <linux/delay.h>#include "s3c_mmc.h"#define S3C_SDIO 1#undef ASSERT#define ASSERT(expr) \if(!(expr)) { \ printk(" Assertion failed ! %s, %s,%s,line=%d\n",\ #expr,__FILE__,__FUNCTION__,__LINE__);\}#define MMC_DEBUG DEBUGextern struct s3c_mmc_lowlevel s3c24a0_mmc_lowlevel;extern struct s3c_mmc_lowlevel s3c2440_mmc_lowlevel;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; 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_lowlevel *lowlevel;/************************************************************************** * 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); } }}static struct s3c_mmc_data g_s3c_mmc_data;#define MMC_IRQ_TIMEOUT (3*HZ)static __inline__ void mmc_delay(void) { mdelay(100); }/*=>CEE LDM*/#include <linux/device.h>static int s3c_mmc_dpm_suspend(struct device * dev, u32 state, u32 level);static int s3c_mmc_dpm_resume(struct device * dev, u32 level);static struct device_driver s3c_mmc_driver_ldm = { .name = "elfin-mmc", .devclass = NULL, .probe = NULL, .suspend = s3c_mmc_dpm_suspend, .resume = s3c_mmc_dpm_resume, .remove = NULL,};static struct device s3c_mmc_device_ldm = { .name = "ELFIN MMC", .bus_id = "MMC", .driver = NULL, .power_state = DPM_POWER_ON,};static void s3c_mmc_ldm_register(void){ extern void apb_bus_register(struct device *device, struct device_driver *driver); apb_bus_register(&s3c_mmc_device_ldm, &s3c_mmc_driver_ldm);}static void s3c_mmc_ldm_unregister(void){ extern void apb_bus_unregister(struct device *device, struct device_driver *driver); apb_bus_unregister(&s3c_mmc_device_ldm, &s3c_mmc_driver_ldm);}/*CEE LDM=>*//************************************************************************** * Clock routines **************************************************************************/static void s3c_mmc_start_clock(void){ rSDICON |= CLOCK_OUT_EN;}static int s3c_mmc_stop_clock(void){ rSDICON &= ~CLOCK_OUT_EN; return MMC_NO_ERROR;}static int s3c_mmc_set_clock( u32 rate ){ int retval; int prescaler = 0; unsigned long pclk; /* Does not seem to work consistently if clock rate is above 10000000 */ if ( 10000000 < rate ) { rate = 10000000; } pclk = elfin_get_bus_clk(GET_PCLK); prescaler = (pclk/rate) - 1; retval = s3c_mmc_stop_clock(); if ( retval ) return retval; rSDIPRE=prescaler; s3c_mmc_start_clock(); g_s3c_mmc_data.clock = rate; return MMC_NO_ERROR; }static void s3c_mmc_set_transfer( u16 block_len, u16 nob,int cmd ){ MMC_DEBUG(2,": block_len=%d nob=%d", block_len, nob); rSDIDTIMER = SDIDTIMER_DEFAULT; if ( block_len > 0x1000) { MMC_DEBUG(2,"ERROR BLOCK SIZE"); } rSDIBSIZE = block_len; if ( nob > 0x1000) { MMC_DEBUG(2,"ERROR Num BLOCK "); } rSDIDCON = nob; /* Wide Bus Mode Disable */ switch (cmd) { case MMC_READ_SINGLE_BLOCK: case MMC_READ_MULTIPLE_BLOCK: rSDIDCON |=RECV_AF_CMD_NODIRECT| DATA_TRANS_BLOCK|BUSY_AF_CMD_NODIRECT|STANDARD_BUS_MODE; rSDIDCON |= (DATA_RECV_START | DATA_START); break; case MMC_WRITE_BLOCK: case MMC_WRITE_MULTIPLE_BLOCK: rSDIDCON |= TRAS_AF_RESPONSE_NODIRECT| DATA_TRANS_BLOCK|BUSY_AF_CMD_NODIRECT|STANDARD_BUS_MODE; rSDIDCON |= (DATA_TRANS_START | DATA_START); break; default: break; }} /******* * 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; } if(rSDIDSTA & MMC_STATUS_DATA_TRANSFER_DONE) rSDIDSTA = MMC_STATUS_DATA_TRANSFER_DONE;}/******* * Prototype * staitc int chk_CMDend(int cmd, int be_resp) * Purpose: * 1. CMD0 use this function. * 2. Debug * Aofter 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 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 */ }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -