?? mx2_mmc.c
字號(hào):
/* * BRIEF MODULE DESCRIPTION * Low-level MMC functions for the MX2 MMC controller * SD support is not present * * Based on: mx1_mmc.c * * Author: MontaVista Software, Inc. <source@mvista.com> * 2003 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. * */#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/io.h>#include <asm/arch/hardware.h>#include <linux/mmc/mmc_ll.h>#include <asm/arch/gpio.h>#include <linux/pm.h>#include <asm/arch/pll.h>#if 1 /*CEE LDM*/#include <linux/device.h>#include <linux/dpm.h>extern void mx21_ldm_bus_register(struct device *device, struct device_driver *driver);extern void mx21_ldm_bus_unregister(struct device *device, struct device_driver *driver);static int__ldm_suspend(struct device *dev, u32 state, u32 level);static int__ldm_resume(struct device *dev, u32 level);#ifdef CONFIG_DPMstatic struct constraints mx2mmc_constraints = { .count = 1, .param = {{DPM_MD_HCLK, 0, 0}}, /*to be initialized at module init time */ .asserted = 1,};#endifstatic struct device_driver __driver_ldm = { .name = "mx2_mmc", .devclass = NULL, .probe = NULL, .suspend = __ldm_suspend, .resume = __ldm_resume, .scale = NULL, .remove = NULL,};static struct device __device_ldm = { .name = "MX2_MMC", .bus_id = "mx2_mmc", .driver = &__driver_ldm, .power_state = DPM_POWER_ON,#ifdef CONFIG_DPM .constraints = &mx2mmc_constraints,#endif};#endif#include "mx2_mmcsd_def.h"/* MMC Port Selection */#define MMC_BASE SDHC_1enum mx2_request_type { RT_NO_RESPONSE, RT_RESPONSE_ONLY, RT_READ, RT_WRITE};struct mx2_mmc_data { u32 clock; /* Current clock frequency */ struct mmc_request *request; /* Current request */ enum mx2_request_type type;};static struct mx2_mmc_data g_mx2_mmc_data;static u16 card_state;static u8 bit_width_flag = 0; /* 1 bit bus width */static int rx_counter = 0;static int tx_counter = 0;static char mx2_mmc_inserted;void mmcsd_socket1_irq_handler(int irq, void *dev_id, struct pt_regs *regs);static __inline__ voidmmc_delay(void){ udelay(1);}/************************************************************************** * Clock routines **************************************************************************/u32mx2_mmc_softreset(void){ /* System Reset */ SDHC_STR_STP_CLK(MMC_BASE) = 0x8; SDHC_STR_STP_CLK(MMC_BASE) = 0x9; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; SDHC_STR_STP_CLK(MMC_BASE) = 0x1; /* Set MMC Response Time-Out Register */ SDHC_RESPONSE_TO(MMC_BASE) = 0xFF; /* Set Block length register */ SDHC_BLK_LEN(MMC_BASE) = 512; /* Set MMC Number of Blocks Register */ SDHC_NOB(MMC_BASE) = 1; return 0;}static intmx2_mmc_stop_clock(void){ /* Clear bit 1 of STR_STP_CLK to disable clock */ do { SDHC_STR_STP_CLK(MMC_BASE) &= ~MMCSD_CLOCK_MASK; SDHC_STR_STP_CLK(MMC_BASE) |= 0x01; } while (SDHC_STATUS(MMC_BASE) & 0x100); return 0;}u32mx2_clock_run(void){ return (SDHC_STATUS(MMC_BASE)) & 0x100;}static intmx2_mmc_start_clock(void){ u8 count = 0; do { if (count++ == 0) { /* do this every 256 loops */ SDHC_STR_STP_CLK(MMC_BASE) &= ~MMCSD_CLOCK_MASK; SDHC_STR_STP_CLK(MMC_BASE) |= 0x2; } } while (!mx2_clock_run()); return 0;}static intmx2_mmc_set_clock(u32 rate){ u8 clk_presc, clk_divider; u8 divisor; DEBUG(3, ": Set rate: %d \n", rate); if (rate == 0) { /* Disable MMC_SD_CLK */ mx2_mmc_stop_clock(); return MMC_NO_ERROR; } divisor = mx_module_get_clk(PERCLK2) / rate; for (clk_presc = 0; clk_presc <= 10; clk_presc++) { if ((clk_divider = divisor / (1 << clk_presc)) <= 8) break; /* clk_divider=divisor/2**clk_presc */ } mx2_mmc_stop_clock(); clk_divider = clk_divider & 0xf; clk_presc = clk_presc & 0xfff; if (rate == MMC_CLOCK_SLOW) SDHC_CLK_RATE(MMC_BASE) = (8 << 4) | 0xf; else SDHC_CLK_RATE(MMC_BASE) = (clk_presc << 4) | 2; mmc_delay(); /* Time out values */ mx2_mmc_start_clock(); SDHC_RESPONSE_TO(MMC_BASE) = 0xFFFF; SDHC_READ_TO(MMC_BASE) = 0x2DB4; g_mx2_mmc_data.clock = rate; return MMC_NO_ERROR;}static voidmx2_mmc_set_transfer(u16 block_len, u16 nob){ SDHC_BLK_LEN(MMC_BASE) = block_len; SDHC_NOB(MMC_BASE) = nob;}static voidmx2_mmc_rx(void){u16 *buf;u32 status; int i = 0; { buf = (u16 *)((u32)g_mx2_mmc_data.request->buffer + rx_counter); status = SDHC_STATUS(MMC_BASE) & 0xFFFF; while (1) { *buf++ = (u16) SDHC_BUFFER_ACCESS(MMC_BASE); if (SDHC_STATUS(MMC_BASE) & MX2STAT_CRC_READ_ERR) { DEBUG(3, ": CRC_ERR %04x \n", SDHC_STATUS(MMC_BASE)); } if (SDHC_STATUS(MMC_BASE) & MX2STAT_TIME_OUT_READ) { DEBUG(3, ": TIMEOUT %04x \n", SDHC_STATUS(MMC_BASE)); } status = SDHC_STATUS(MMC_BASE) & 0xFFFF; i++; if (bit_width_flag) { if (i == 32) break; } else { if (i == 8) break; } } } rx_counter += i * 2; if (rx_counter >= g_mx2_mmc_data.request->block_len) { DEBUG(3, ": RX Transfer finished\n"); g_mx2_mmc_data.request->nob--; }}static voidmx2_mmc_tx(void){u16 *buf;int i = 0; buf = (u16 *)((u32)g_mx2_mmc_data.request->buffer + tx_counter); if (tx_counter < g_mx2_mmc_data.request->block_len * g_mx2_mmc_data.request->nob) { while (1) { (u16) SDHC_BUFFER_ACCESS(MMC_BASE) = *buf++; i++; if (bit_width_flag) { if (i == 32) break; } else { if (i == 8) break; } } tx_counter += i * 2; } if (tx_counter >= g_mx2_mmc_data.request->block_len) { SDHC_INT_MASK(MMC_BASE) = MX2_WRITE_OP_DONE_MASK; }}static intmx2_mmc_exec_command(struct mmc_request *request){ u16 dat_control = 0; u16 int_mask = 0; SDHC_INT_MASK(MMC_BASE) = MX2_AUTO_CARD_DETECT_MASK & 0x7f; int_mask = 0x7b; mx2_mmc_stop_clock(); /* Clear status bits */ switch (request->cmd) { case MMC_READ_SINGLE_BLOCK: mx2_mmc_set_transfer(request->block_len, request->nob); break; case MMC_WRITE_BLOCK: mx2_mmc_set_transfer(request->block_len, request->nob); break; } dat_control = 0; /* Set response type */ switch (request->rtype) { case RESPONSE_NONE: break; case RESPONSE_R1: case RESPONSE_R1B: dat_control |= MMCSDB_R1; break; case RESPONSE_R2_CID: case RESPONSE_R2_CSD: dat_control |= MMCSDB_R2; break; case RESPONSE_R3: dat_control |= MMCSDB_R3; break; case RESPONSE_R4: dat_control |= MMCSDB_R4; break; case RESPONSE_R5: dat_control |= MMCSDB_R5; break; } if (bit_width_flag) { dat_control |= MMCSD_BUS_4BIT; } /* Set command type */ switch (request->cmd) { case MMC_GO_IDLE_STATE: dat_control |= MMCSDB_INIT; break; case MMC_READ_SINGLE_BLOCK: case MMC_READ_MULTIPLE_BLOCK: dat_control |= MMCSDB_DATEN; break; case MMC_WRITE_BLOCK: case MMC_WRITE_MULTIPLE_BLOCK: dat_control |= MMCSDB_DATEN | MMCSDB_WRRD; break; case MMC_SELECT_CARD: if (request->arg != 0) dat_control |= MMCSDB_BSY | MMCSDB_R1; else dat_control |= MMCSDB_BSY; break; case MMC_LOCK_UNLOCK: dat_control |= MMCSDB_DATEN | MMCSDB_WRRD; break; } /* Send command */ DEBUG(3, ": Send cmd : %d Arg: %x\n", request->cmd, request->arg); SDHC_CMD(MMC_BASE) = request->cmd; /* Set argument */ SDHC_ARGH(MMC_BASE) = (request->arg) >> 16; SDHC_ARGL(MMC_BASE) = (request->arg) & 0xffff; SDHC_CMD_DAT_CONT(MMC_BASE) = dat_control; if (request->cmd == MMC_READ_SINGLE_BLOCK || request->cmd == MMC_READ_MULTIPLE_BLOCK) { g_mx2_mmc_data.type = RT_READ; rx_counter = 0; } else if (request->cmd == MMC_WRITE_BLOCK || request->cmd == MMC_WRITE_MULTIPLE_BLOCK) { g_mx2_mmc_data.type = RT_WRITE; tx_counter = 0; } else if (request->rtype == RESPONSE_NONE) { g_mx2_mmc_data.type = RT_NO_RESPONSE; } else { g_mx2_mmc_data.type = RT_RESPONSE_ONLY; } SDHC_INT_MASK(MMC_BASE) = int_mask; mx2_mmc_start_clock(); return MMC_NO_ERROR;}static voidmx2_mmc_send_command(struct mmc_request *request){ int retval; g_mx2_mmc_data.request = request; request->result = MMC_NO_RESPONSE; /* Flag to indicate don't have a result yet */ if (request->cmd == MMC_CIM_RESET) { /* Reset MX2 MMC hardware */ mx2_mmc_softreset(); retval = mx2_mmc_set_clock(MMC_CLOCK_SLOW); if (retval) { /* If any error occured -> exit with error code */ request->result = retval; mmc_cmd_complete(request); return; } request->result = MMC_NO_ERROR; request->rtype = RESPONSE_NONE; mmc_cmd_complete(request); return; } retval = mx2_mmc_exec_command(request); if (retval) { /* If any error occured -> exit with error code */ request->result = retval; mmc_cmd_complete(request); }}/**************************************************************************//* TODO: Fix MMC core for correct response processing */static voidmx2_mmc_get_response(struct mmc_request *request){
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -