📄 mmc_core.c
字号:
/* * Core MMC driver functions * * 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 */#include <linux/config.h>#include <linux/module.h>#include <linux/version.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/list.h>#include <linux/sysctl.h>#include <linux/pm.h>#include "mmc_core.h"#define STATE_CMD_ACTIVE (1<<0)#define STATE_CMD_DONE (1<<1)#define STATE_INSERT (1<<2)#define STATE_EJECT (1<<3)static struct mmc_dev g_mmc_dev;static struct proc_dir_entry *proc_mmc_dir;#ifdef CONFIG_MMC_DEBUGint g_mmc_debug = CONFIG_MMC_DEBUG_VERBOSE;EXPORT_SYMBOL(g_mmc_debug);#endif/************************************************************************** * Debugging functions **************************************************************************/static char * mmc_result_strings[] = { "NO_RESPONSE", "NO_ERROR", "ERROR_OUT_OF_RANGE", "ERROR_ADDRESS", "ERROR_BLOCK_LEN", "ERROR_ERASE_SEQ", "ERROR_ERASE_PARAM", "ERROR_WP_VIOLATION", "ERROR_CARD_IS_LOCKED", "ERROR_LOCK_UNLOCK_FAILED", "ERROR_COM_CRC", "ERROR_ILLEGAL_COMMAND", "ERROR_CARD_ECC_FAILED", "ERROR_CC", "ERROR_GENERAL", "ERROR_UNDERRUN", "ERROR_OVERRUN", "ERROR_CID_CSD_OVERWRITE", "ERROR_STATE_MISMATCH", "ERROR_HEADER_MISMATCH", "ERROR_TIMEOUT", "ERROR_CRC", "ERROR_DRIVER_FAILURE",};char * mmc_result_to_string( int i ){ return mmc_result_strings[i+1];}static char * card_state_strings[] = { "empty", "idle", "ready", "ident", "stby", "tran", "data", "rcv", "prg", "dis",};static inline char * card_state_to_string( int i ){ return card_state_strings[i+1];}/************************************************************************** * Utility functions **************************************************************************/#define PARSE_U32(_buf,_index) \ (((u32)_buf[_index]) << 24) | (((u32)_buf[_index+1]) << 16) | \ (((u32)_buf[_index+2]) << 8) | ((u32)_buf[_index+3]);#define PARSE_U16(_buf,_index) \ (((u16)_buf[_index]) << 8) | ((u16)_buf[_index+1]);int mmc_unpack_csd( struct mmc_request *request, struct mmc_csd *csd ){ u8 *buf = request->response; if ( request->result ) return request->result; csd->csd_structure = (buf[1] & 0xc0) >> 6; csd->spec_vers = (buf[1] & 0x3c) >> 2; csd->taac = buf[2]; csd->nsac = buf[3]; csd->tran_speed = buf[4]; csd->ccc = (((u16)buf[5]) << 4) | ((buf[6] & 0xf0) >> 4); csd->read_bl_len = buf[6] & 0x0f; csd->read_bl_partial = (buf[7] & 0x80) ? 1 : 0; csd->write_blk_misalign = (buf[7] & 0x40) ? 1 : 0; csd->read_blk_misalign = (buf[7] & 0x20) ? 1 : 0; csd->dsr_imp = (buf[7] & 0x10) ? 1 : 0; csd->c_size = ((((u16)buf[7]) & 0x03) << 10) | (((u16)buf[8]) << 2) | (((u16)buf[9]) & 0xc0) >> 6; csd->vdd_r_curr_min = (buf[9] & 0x38) >> 3; csd->vdd_r_curr_max = buf[9] & 0x07; csd->vdd_w_curr_min = (buf[10] & 0xe0) >> 5; csd->vdd_w_curr_max = (buf[10] & 0x1c) >> 2; csd->c_size_mult = ((buf[10] & 0x03) << 1) | ((buf[11] & 0x80) >> 7); switch ( csd->csd_structure ) { case CSD_STRUCT_VER_1_0: case CSD_STRUCT_VER_1_1: csd->erase.v22.sector_size = (buf[11] & 0x7c) >> 2; csd->erase.v22.erase_grp_size = ((buf[11] & 0x03) << 3) | ((buf[12] & 0xe0) >> 5); break; case CSD_STRUCT_VER_1_2: default: csd->erase.v31.erase_grp_size = (buf[11] & 0x7c) >> 2; csd->erase.v31.erase_grp_mult = ((buf[11] & 0x03) << 3) | ((buf[12] & 0xe0) >> 5); break; } csd->wp_grp_size = buf[12] & 0x1f; csd->wp_grp_enable = (buf[13] & 0x80) ? 1 : 0; csd->default_ecc = (buf[13] & 0x60) >> 5; csd->r2w_factor = (buf[13] & 0x1c) >> 2; csd->write_bl_len = ((buf[13] & 0x03) << 2) | ((buf[14] & 0xc0) >> 6); csd->write_bl_partial = (buf[14] & 0x20) ? 1 : 0; csd->file_format_grp = (buf[15] & 0x80) ? 1 : 0; csd->copy = (buf[15] & 0x40) ? 1 : 0; csd->perm_write_protect = (buf[15] & 0x20) ? 1 : 0; csd->tmp_write_protect = (buf[15] & 0x10) ? 1 : 0; csd->file_format = (buf[15] & 0x0c) >> 2; csd->ecc = buf[15] & 0x03; MMC_DEBUG(2,"csd_structure=%d spec_vers=%d taac=%02x nsac=%02x tran_speed=%02x\n" " ccc=%04x read_bl_len=%d read_bl_partial=%d write_blk_misalign=%d\n" " read_blk_misalign=%d dsr_imp=%d c_size=%d vdd_r_curr_min=%d\n" " vdd_r_curr_max=%d vdd_w_curr_min=%d vdd_w_curr_max=%d c_size_mult=%d\n" " wp_grp_size=%d wp_grp_enable=%d default_ecc=%d r2w_factor=%d\n" " write_bl_len=%d write_bl_partial=%d file_format_grp=%d copy=%d\n" " perm_write_protect=%d tmp_write_protect=%d file_format=%d ecc=%d", csd->csd_structure, csd->spec_vers, csd->taac, csd->nsac, csd->tran_speed, csd->ccc, csd->read_bl_len, csd->read_bl_partial, csd->write_blk_misalign, csd->read_blk_misalign, csd->dsr_imp, csd->c_size, csd->vdd_r_curr_min, csd->vdd_r_curr_max, csd->vdd_w_curr_min, csd->vdd_w_curr_max, csd->c_size_mult, csd->wp_grp_size, csd->wp_grp_enable, csd->default_ecc, csd->r2w_factor, csd->write_bl_len, csd->write_bl_partial, csd->file_format_grp, csd->copy, csd->perm_write_protect, csd->tmp_write_protect, csd->file_format, csd->ecc); switch (csd->csd_structure) { case CSD_STRUCT_VER_1_0: case CSD_STRUCT_VER_1_1: MMC_DEBUG(2,"V22 sector_size=%d erase_grp_size=%d", csd->erase.v22.sector_size, csd->erase.v22.erase_grp_size); break; case CSD_STRUCT_VER_1_2: default: MMC_DEBUG(2,"V31 erase_grp_size=%d erase_grp_mult=%d", csd->erase.v31.erase_grp_size, csd->erase.v31.erase_grp_mult); break; } if ( buf[0] != 0x3f ) return MMC_ERROR_HEADER_MISMATCH; return 0;}int mmc_unpack_r1( struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state ){ u8 *buf = request->response; if ( request->result ) return request->result; r1->cmd = buf[0]; r1->status = PARSE_U32(buf,1); MMC_DEBUG(2,"cmd=%d status=%08x", r1->cmd, r1->status); /* SD card return in upper status the cardid */ if (R1_STATUS(r1->status) && request->cmd!=MMC_SET_RELATIVE_ADDR) { if ( r1->status & R1_OUT_OF_RANGE ) return MMC_ERROR_OUT_OF_RANGE; if ( r1->status & R1_ADDRESS_ERROR ) return MMC_ERROR_ADDRESS; if ( r1->status & R1_BLOCK_LEN_ERROR ) return MMC_ERROR_BLOCK_LEN; if ( r1->status & R1_ERASE_SEQ_ERROR ) return MMC_ERROR_ERASE_SEQ; if ( r1->status & R1_ERASE_PARAM ) return MMC_ERROR_ERASE_PARAM; if ( r1->status & R1_WP_VIOLATION ) return MMC_ERROR_WP_VIOLATION; if ( r1->status & R1_CARD_IS_LOCKED ) return MMC_ERROR_CARD_IS_LOCKED; if ( r1->status & R1_LOCK_UNLOCK_FAILED ) return MMC_ERROR_LOCK_UNLOCK_FAILED; if ( r1->status & R1_COM_CRC_ERROR ) return MMC_ERROR_COM_CRC; if ( r1->status & R1_ILLEGAL_COMMAND ) return MMC_ERROR_ILLEGAL_COMMAND; if ( r1->status & R1_CARD_ECC_FAILED ) return MMC_ERROR_CARD_ECC_FAILED; if ( r1->status & R1_CC_ERROR ) return MMC_ERROR_CC; if ( r1->status & R1_ERROR ) return MMC_ERROR_GENERAL; if ( r1->status & R1_UNDERRUN ) return MMC_ERROR_UNDERRUN; if ( r1->status & R1_OVERRUN ) return MMC_ERROR_OVERRUN; if ( r1->status & R1_CID_CSD_OVERWRITE ) return MMC_ERROR_CID_CSD_OVERWRITE; } if ( buf[0] != request->cmd ) return MMC_ERROR_HEADER_MISMATCH; /* This should be last - it's the least dangerous error */ if ( R1_CURRENT_STATE(r1->status) != state ) return MMC_ERROR_STATE_MISMATCH; return 0;}int mmc_unpack_cid_mmc( struct mmc_request *request, struct mmc_cid *cid ){ u8 *buf = request->response; int i; if ( request->result ) return request->result; cid->mid = buf[1]; cid->oid = PARSE_U16(buf,2); for ( i = 0 ; i < 6 ; i++ ) cid->pnm[i] = buf[4+i]; cid->pnm[6] = 0; cid->prv = buf[10]; cid->psn = PARSE_U32(buf,11); cid->mdt = buf[15]; MMC_DEBUG(2,"mid=%d oid=%d pnm=%s prv=%d.%d psn=%08x mdt=%d/%d", cid->mid, cid->oid, cid->pnm, (cid->prv>>4), (cid->prv&0xf), cid->psn, (cid->mdt>>4), (cid->mdt&0xf)+1997); if ( buf[0] != 0x3f ) return MMC_ERROR_HEADER_MISMATCH; return 0;}int mmc_unpack_cid_sd( struct mmc_request *request, struct mmc_cid *cid ){ u8 *buf = request->response; int i; if ( request->result ) return request->result; /* * Packed CID Format * * Field|size| slice | byte | Description * * MID | 8 | [127:120] | [ 0: 0] | Manufacture ID [$02 = Toshiba ] * OID | 16 | [119:104] | [ 1: 2] | Manufacture ID [$544D "TM" = Toshiba ] * PNM | 40 | [103: 64] | [ 3: 7] | Product Name Manufacture [SD044 | SD128 | SD256 for toshiba sdcard ] * PRV | 8 | [ 63: 56] | [ 8: 8] | Product Revision * PSN | 32 | [ 55: 24] | [ 9:12] | Product Serial Number * - | 4 | [ 23: 20] | [13:13] | All zeros * MDT | 12 | [ 19: 8] | [13:14] | Manufacture Date [11:8] Month / [19:12] Year + 2000 * CRC | 7 | [ 7: 1] | [15:15] | CRC * - | 1 | [ 0: 0] | [15:15] | 1 */ cid->mid = buf[1]; cid->oid = PARSE_U16(buf,2); for ( i = 0 ; i < 5 ; i++ ) cid->pnm[i] = buf[4+i]; cid->pnm[5] = 0; cid->prv = buf[9]; cid->psn = PARSE_U32(buf,10); cid->mdt = (buf[14]<<4)|buf[15]; MMC_DEBUG(2,"mid=%d oid=%d pnm=%s prv=%d.%d psn=%08x mdt=%d/%d", cid->mid, cid->oid, cid->pnm, (cid->prv>>4), (cid->prv&0xf), cid->psn, (cid->mdt>>4), (cid->mdt&0xf)+2000); if ( buf[0] != 0x3f ) return MMC_ERROR_HEADER_MISMATCH; if ( !(buf[16]&1) ) return MMC_ERROR_CARD_ECC_FAILED; return 0;}int mmc_unpack_r3( struct mmc_request *request, struct mmc_response_r3 *r3 ){ u8 *buf = request->response; if ( request->result ) return request->result; r3->ocr = PARSE_U32(buf,1); MMC_DEBUG(2,"ocr=%08x", r3->ocr); if ( buf[0] != 0x3f ) return MMC_ERROR_HEADER_MISMATCH; return 0;}/**************************************************************************/#define KBPS 1#define MBPS 1000static u32 ts_exp[] = { 100*KBPS, 1*MBPS, 10*MBPS, 100*MBPS, 0, 0, 0, 0 };static u32 ts_mul[] = { 0, 1000, 1200, 1300, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, 5500, 6000, 7000, 8000 };u32 mmc_tran_speed( u8 ts ){ u32 rate = ts_exp[(ts & 0x7)] * ts_mul[(ts & 0x78) >> 3]; if ( rate <= 0 ) { MMC_DEBUG(0, ": error - unrecognized speed 0x%02x", ts); return 1; } return rate;}/**************************************************************************/void mmc_send_cmd( struct mmc_dev *dev, int cmd, u32 arg, u16 nob, u16 block_len, enum mmc_rsp_t rtype ){ dev->request.cmd = cmd; dev->request.arg = arg; dev->request.rtype = rtype; dev->request.nob = nob; dev->request.block_len = block_len; dev->request.buffer = NULL; if ( nob && dev->io_request ) dev->request.buffer = dev->io_request->buffer; dev->state |= STATE_CMD_ACTIVE; dev->sdrive->send_cmd(&dev->request);}void mmc_finish_io_request( struct mmc_dev *dev, int result ){ struct mmc_io_request *t = dev->io_request; struct mmc_slot *slot = dev->slot + t->id; dev->io_request = NULL; // Remove the old request (the media driver may requeue) if ( slot->media_driver ) slot->media_driver->io_request_done( t, result );}/* Only call this when there is no pending request - it unloads the media driver */int mmc_check_eject( struct mmc_dev *dev ){ unsigned long flags; int state; int i; MMC_DEBUG(2,"dev state=%x", dev->state); local_irq_save(flags); state = dev->state; dev->state = state & ~STATE_EJECT; local_irq_restore(flags); if ( !(state & STATE_EJECT) ) return 0; for ( i = 0 ; i < dev->num_slots ; i++ ) { struct mmc_slot *slot = dev->slot + i; if ( slot->flags & MMC_SLOT_FLAG_EJECT ) { slot->state = CARD_STATE_EMPTY; if ( slot->media_driver ) { slot->media_driver->unload( slot ); slot->media_driver = NULL; } /* Clear eject & SD card here in case the next * card is an MMC. Cannot clear all as suspend * resume will queue eject & insert together. */ slot->flags &= ~(MMC_SLOT_FLAG_EJECT | MMC_SLOT_FLAG_SDCARD); run_sbin_mmc_hotplug(dev,i,0); } } return 1;}int mmc_check_insert( struct mmc_dev *dev ){ unsigned long flags; int state; int i; int card_count = 0; MMC_DEBUG(2,"dev state=%x", dev->state); local_irq_save(flags); state = dev->state; dev->state = state & ~STATE_INSERT; local_irq_restore(flags); if ( !(state & STATE_INSERT) ) return 0; for ( i = 0 ; i < dev->num_slots ; i++ ) { struct mmc_slot *slot = dev->slot + i; if ( slot->flags & MMC_SLOT_FLAG_INSERT ) { if (!dev->sdrive->is_empty(i)) { slot->state = CARD_STATE_IDLE; card_count++; } slot->flags &= ~MMC_SLOT_FLAG_INSERT; } } return card_count;}/****************************************************************** * * Hotplug callback card insertion/removal * ******************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -