📄 mmc_asic3.c
字号:
/* * Low-level MMC/SD functions for the ASIC3 MMC/SD Controller * * Use consistent with the GNU GPL is permitted, * provided that this copyright notice is * preserved in its entirety in all copies and derived works. * * Some parts of code based on iPAQ 3800 MMC driver code * * Author: Pawel Kolodziejski (aquadran) * * comments: * - initilise card procedure fail sometimes for my MMC card, * - add code for handle interrupt card detection (removal, insertion). * - add code for support PM. * - multi block mode not tested (MMC core driver doesn't support it) * - 3900 must disable bus host clock, and use asic2 MMC clock */#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/unaligned.h>#include <asm/arch/hardware.h>#include <asm/arch/h3900_asic.h>#include <linux/mmc/mmc_ll.h>struct response_info { int length; u16 cdc_flags;};static struct response_info rinfo[] = { { 0, SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL }, // R0 { 5, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1 }, // R1 { 5, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B }, // R1b { 16, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2 }, // R2 { 16, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2 }, // R2 { 5, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 }, // R3 { 5, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 }, // R4 { 5, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 }, // R5 { 5, SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 }, // R6};struct mmc_data { struct mmc_request *request; int flag;};static struct mmc_data g_data;#ifdef CONFIG_MMC_DEBUGstruct 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" }, { SD_APP_OP_COND, "SD_APP_OP_COND" }, { SD_APP_SET_BUS_WIDTH, "SD_APP_SET_BUS_WIDTH" },};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";}#endif /* CONFIG_MMC_DEBUG */static void mmc_asic3_stop_clock(void){ H3900_ASIC3_SD_CTRL_CardClockCtrl = 0; // disable clock}static void mmc_asic3_start_clock(void){ H3900_ASIC3_SD_CONFIG_ClockMode = CLOCK_MODE_DIV_CLOCK_DISABLE; // disable div rate clock H3900_ASIC3_SD_CTRL_CardClockCtrl = SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK; if (g_data.flag == 1) H3900_ASIC3_SD_CTRL_CardClockCtrl |= SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD; mdelay(1);}static int mmc_asic3_set_clock(u32 rate){ H3900_ASIC3_SD_CONFIG_ClockMode = CLOCK_MODE_DIV_CLOCK_DISABLE; // disable div rate clock H3900_ASIC3_SD_CTRL_CardClockCtrl = SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK; if (g_data.flag == 1) H3900_ASIC3_SD_CTRL_CardClockCtrl |= SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD; mdelay(1); return MMC_NO_ERROR;}static int mmc_asic3_reset_asic(void){ if (machine_is_h3900()) { H3800_ASIC2_CLOCK_Enable |= ASIC2_CLOCK_SD_2; // 24.576 Mhz H3900_ASIC3_CLOCK_CDEX |= CLOCK_CDEX_EX0; mdelay(1); /* Enable the host clock */ H3900_ASIC3_CLOCK_SEL |= CLOCK_SEL_SD_HCLK_SEL; H3900_ASIC3_CLOCK_SEL &= ~CLOCK_SEL_SD_BCLK_SEL; H3900_ASIC3_CLOCK_CDEX |= CLOCK_CDEX_SD_HOST; H3900_ASIC3_CLOCK_CDEX &= ~CLOCK_CDEX_SD_BUS; mdelay(1); } H3900_ASIC3_CLOCK_CDEX |= CLOCK_CDEX_EX1; mdelay(1); H3900_ASIC3_EXTCF_Select |= ASIC3_EXTCF_SD_MEM_ENABLE; // enable possibility SD/MMC card in CF slot ? H3900_ASIC3_SDHWCTRL_SDConf &= ~(ASIC3_SDHWCTRL_SUSPEND | ASIC3_SDHWCTRL_PCLR); // wake up mdelay(10); H3900_ASIC3_SD_CONFIG_Addr0 = 0x0800; // offset to host controller H3900_ASIC3_SD_CONFIG_Addr1 = 0x0000; H3900_ASIC3_SD_CONFIG_ClkStop = SD_CONFIG_CLKSTOP_ENABLE_ALL; // setup stop clock (i think) H3900_ASIC3_SD_CONFIG_SDHC_CardDetect = 2; // two cycles (possible 512ms) H3900_ASIC3_SD_CONFIG_Command = SD_CONFIG_COMMAND_MAE; // access to control registers enabled H3900_ASIC3_SD_CTRL_SoftwareReset = 0; mdelay(2); H3900_ASIC3_SD_CTRL_SoftwareReset = 1; mdelay(2); H3900_ASIC3_SDIO_CTRL_SoftwareReset = 0; mdelay(2); H3900_ASIC3_SDIO_CTRL_SoftwareReset = 1; mdelay(2); H3900_ASIC3_SD_CTRL_IntMaskCard = 0x0; // mask all interrupts, they are not used H3900_ASIC3_SD_CTRL_IntMaskBuffer = 0x0; // --- // --- H3900_ASIC3_SDHWCTRL_SDConf &= ~ASIC3_SDHWCTRL_PCLR; // clear all SDIO registers H3900_ASIC3_SD_CTRL_MemCardOptionSetup = MEM_CARD_OPTION_REQUIRED | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14) | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT | MEM_CARD_OPTION_DATA_XFR_WIDTH_1; H3900_ASIC3_SD_CTRL_ErrorStatus0 = 0; // clear detail error status register H3900_ASIC3_SD_CTRL_ErrorStatus1 = 0; // --- // --- H3900_ASIC3_SD_CTRL_CardStatus = 0; // clear card status H3900_ASIC3_SD_CTRL_BufferCtrl = 0; // clear buffer controller status H3900_ASIC3_SD_CTRL_StopInternal = 0; // clear stop transfer register H3900_ASIC3_SDIO_CTRL_ClocknWaitCtrl = 0x0100; // setup clock for SDIO H3900_ASIC3_SDHWCTRL_SDConf |= ASIC3_SDHWCTRL_SDPWR; // turn on power at controller(?) mdelay(10); H3900_ASIC3_SD_CONFIG_SDHC_Power3 = 0; // turn off auto power down after card remove H3900_ASIC3_SD_CONFIG_SDHC_Power1 = SD_CONFIG_POWER1_PC_33V; // turn on power to card, // turn off auto power up after card inserted mdelay(10); H3900_ASIC3_SD_CONFIG_ClockMode = CLOCK_MODE_DIV_CLOCK_ENABLE; // enable div clock for change rate H3900_ASIC3_SD_CTRL_CardClockCtrl = 0; // reset card clock ? H3900_ASIC3_SD_CTRL_CardClockCtrl = SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK | SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_64; mdelay(1); MMC_DEBUG(0, "Card status 0x%04x\n", H3900_ASIC3_SD_CTRL_CardStatus); MMC_DEBUG(0, "Buffer status 0x%04x\n", H3900_ASIC3_SD_CTRL_BufferCtrl); g_data.flag = 0; return MMC_NO_ERROR;}static void mmc_asic3_set_command(u16 cmd, u32 arg){ MMC_DEBUG(1, "cmd=%d arg=0x%08x", cmd, arg); H3900_ASIC3_SD_CTRL_Arg1 = arg >> 16; H3900_ASIC3_SD_CTRL_Arg0 = arg & 0xffff; H3900_ASIC3_SD_CTRL_Cmd = cmd;}static void mmc_asic3_set_transfer(u16 block_len, u16 nob){ MMC_DEBUG(1, "block_len=%d nob=%d", block_len, nob); H3900_ASIC3_SD_CTRL_MemCardXferDataLen = block_len; // size of block (default 512 bytes) H3900_ASIC3_SD_CTRL_TransferSectorCount = nob; // number blocks to transfer}static void mmc_asic3_transmit_data(struct mmc_request *request){ u8 *buf = request->buffer; u16 data; int i; MMC_DEBUG(1, "nob=%d block_len=%d", request->nob, request->block_len); if (request->nob <= 0) { MMC_DEBUG(1, "*** nob already at 0 ***"); return; } for (i = 0; i < request->block_len; i += 2, buf += 2) { data = ((u16) *buf) | (*(buf + 1) << 8); H3900_ASIC3_SD_CTRL_DataPort = data; // write two bytes to SD/MMC controller buffer } START_MMC_DEBUG(3) { u8 *b = request->buffer; for (i = 0 ; i < request->block_len ; i++) { printk(" %02x", *b++); if (((i + 1) % 16) == 0) printk("\n"); } } END_MMC_DEBUG; // Updated the request buffer to reflect the current state request->buffer = (u8 *)buf; request->nob--;}static void mmc_asic3_receive_data(struct mmc_request *request){ u8 *buf = request->buffer; u16 data; int i; MMC_DEBUG(1, "nob=%d block_len=%d buf=%p", request->nob, request->block_len, buf); if (request->nob <= 0) { MMC_DEBUG(1, "*** nob already at 0 ***"); return; } for (i = 0; i < request->block_len; i += 2) { data = H3900_ASIC3_SD_CTRL_DataPort; // read two bytes from SD/MMC controller buffer *buf++ = data & 0xff; *buf++ = data >> 8; } START_MMC_DEBUG(3) { u8 *b = request->buffer; for (i = 0; i < request->block_len ; i++) { printk(" %02x", *b++); if ( ((i + 1) % 16) == 0 ) printk("\n"); } } END_MMC_DEBUG; // Updated the request buffer to reflect the current state request->buffer = (u8 *)buf; request->nob--;}static void mmc_asic3_get_response(struct mmc_request *request){ int i, len = rinfo[request->rtype].length; u8 *buf = request->response; u16 data; request->result = MMC_NO_ERROR; // Mark this as having a request result of some kind if (len <= 0) { MMC_DEBUG(1, "no response"); return; } if (len > 5) { // R2 handle data = H3900_ASIC3_SD_CTRL_Response7; buf[0] = 0x3f; buf[1] = data & 0xff; data = H3900_ASIC3_SD_CTRL_Response6; buf[2] = data >> 8; buf[3] = data & 0xff; data = H3900_ASIC3_SD_CTRL_Response5; buf[4] = data >> 8; buf[5] = data & 0xff; data = H3900_ASIC3_SD_CTRL_Response4; buf[6] = data >> 8; buf[7] = data & 0xff; data = H3900_ASIC3_SD_CTRL_Response3; buf[8] = data >> 8; buf[9] = data & 0xff; data = H3900_ASIC3_SD_CTRL_Response2; buf[10] = data >> 8; buf[11] = data & 0xff; data = H3900_ASIC3_SD_CTRL_Response1; buf[12] = data >> 8;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -