?? jz_mmc.c
字號:
/* * drivers/mmc/jz_mmc.c * * Low-level MMC/SD functions for the Ingenic JZ4730 MMC/SD on-chip controller * * Copyright 2006 Ingenic Semiconductor Inc. * * Copyright 2003 MontaVista Software Inc. * Author: MontaVista Software, Inc. * source@mvista.com * * 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. */#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 <linux/timer.h>#include <linux/pm.h>#include <linux/mmc/mmc_ll.h>#include <linux/pci.h>#include <asm/irq.h>#include <asm/unaligned.h>#include <asm/io.h>#include <asm/jzsoc.h>/* Device-specific data */typedef struct jz_mmc_data { struct mmc_request * request; int sd; int dma_rx_ch; int dma_tx_ch; int use_4bit; u32 events; u32 clock;} jz_mmc_data_t;static jz_mmc_data_t jz_mmc_data;/* MMC Events */#define MMC_EVENT_NONE 0x00 /* No events */#define MMC_EVENT_RX_DMA_DONE 0x01 /* Rx DMA done */#define MMC_EVENT_TX_DMA_DONE 0x02 /* Tx DMA done */#define MMC_EVENT_DATA_DONE 0x04 /* Data Transfer done */#define MMC_EVENT_PROG_DONE 0x08 /* Programming is done *//* Stop the MMC clock and wait while it happens */static inline int jz_mmc_stop_clock(void){ int timeout = 1000; DEBUG(2, "stop MMC clock\n"); REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP; while (timeout && (REG_MSC_STAT & MSC_STAT_CLK_EN)) { timeout--; if (timeout == 0) { DEBUG(3, "Timeout on stop clock waiting\n"); return MMC_ERROR_TIMEOUT; } udelay(1); } DEBUG(2, "clock off time is %d microsec\n", timeout); return MMC_NO_ERROR;}/* Start the MMC clock and operation */static inline int jz_mmc_start_clock(void){ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP; return MMC_NO_ERROR;}static inline u32 jz_mmc_calc_clkrt(int is_sd, u32 rate){ u32 clkrt; u32 clk_src = is_sd ? 24000000: 16000000; clkrt = 0; while (rate < clk_src) { clkrt ++; clk_src >>= 1; } return clkrt;}/* Select the MMC clock frequency */static int jz_mmc_set_clock(u32 rate){ int clkrt; jz_mmc_stop_clock(); __cpm_select_msc_clk(jz_mmc_data.sd); /* select clock source from CPM */ clkrt = jz_mmc_calc_clkrt(jz_mmc_data.sd, rate); DEBUG(2, "set clock to %u Hz is_sd=%d clkrt=%d\n", rate, jz_mmc_data.sd, clkrt); REG_MSC_CLKRT = clkrt; jz_mmc_data.clock = rate; return MMC_NO_ERROR;}/* Initialize the MMC controller to up the slot, assuming the card is in slot */void jz_mmc_slot_up(void){ DEBUG(2, "Init MMC h/w\n"); /* Init GPIO */ __gpio_as_msc(); /* Turn on power for the slot */ __msc_init_io(); __msc_enable_power(); /* Turn on core clock signal for the MMC/SC controller */ __cpm_select_msc_clk(0); /* select 19MHz clock to MSC module */ __msc_set_clkrt(7); /* lowest clock to MMC/SD card */ /* Stop the MMC clock before 1st command */ jz_mmc_stop_clock();}/* Shut down the slot */void jz_mmc_slot_down(void){ DEBUG(2, "down MMC h/w\n"); /* Turn off MMC clock */ jz_mmc_stop_clock(); /* Turn off power for the slot */ __msc_disable_power();}/* Complete the request processing */static inline void jz_mmc_request_complete(struct jz_mmc_data *mmc, enum mmc_result_t result){ struct mmc_request *req = mmc->request; if (!req) return; jz_mmc_stop_clock(); req->result = result; mmc_cmd_complete(req); mmc->request = NULL;}static inline voidjz_mmc_start_dma(int chan, unsigned long phyaddr,int count, int mode){ unsigned long flags; flags = claim_dma_lock(); disable_dma(chan); clear_dma_ff(chan); jz_set_dma_block_size(chan, 32); set_dma_mode(chan, mode); set_dma_addr(chan, phyaddr); set_dma_count(chan, count + 31); enable_dma(chan); release_dma_lock(flags);}static void jz_mmc_dma_rx_start(struct jz_mmc_data *mmc);/* Handle DMA data receive completion */static void jz_mmc_dma_rx_callback(int irq, void *data, struct pt_regs *regs){ struct jz_mmc_data *mmc = data; int channel = jz_mmc_data.dma_rx_ch; DEBUG(3, "DMA RX Callback: MMC_STAT 0x%08x " "I_REG 0x%08x I_MASK 0x%08x\n", REG_MSC_STAT, REG_MSC_IREG, REG_MSC_IMASK); disable_dma(channel); if (__dmac_channel_address_error_detected(channel)) { printk(KERN_DEBUG "%s: DMAC address error.\n", __FUNCTION__); __dmac_channel_clear_address_error(channel); } if (__dmac_channel_transmit_end_detected(channel)) { __dmac_channel_clear_transmit_end(channel); mmc->events &= ~MMC_EVENT_RX_DMA_DONE; if (mmc->events == 0) { jz_mmc_request_complete(mmc, MMC_NO_ERROR); } }}/* Prepare DMA to start data transfer from the MMC card */static void jz_mmc_dma_rx_start(struct jz_mmc_data *mmc){ struct mmc_request *request = mmc->request; int channel = mmc->dma_rx_ch; dma_addr_t dma_addr = PHYSADDR(request->buffer); u32 size = request->block_len * request->nob; DEBUG(3, "MMC DMA Rx start: chan %d buf 0x%08x phys 0x%08x " "blklen %d nob%d\n", channel, (u32)request->buffer, (u32)dma_addr, request->block_len, request->nob); dma_cache_wback_inv((unsigned int)request->buffer, size); jz_mmc_start_dma(channel, dma_addr, size, DMA_MODE_READ);}static void jz_mmc_dma_tx_start(struct jz_mmc_data *mmc);/* Handle transmit DMA competion */static void jz_mmc_dma_tx_callback(int irq, void *data, struct pt_regs *regs){ struct jz_mmc_data *mmc = data; int channel = jz_mmc_data.dma_tx_ch; DEBUG(3, "DMA TX Callback\n"); disable_dma(channel); if (__dmac_channel_address_error_detected(channel)) { __dmac_channel_clear_address_error(channel); } if (__dmac_channel_transmit_end_detected(channel)) { __dmac_channel_clear_transmit_end(channel); mmc->events &= ~MMC_EVENT_TX_DMA_DONE; if (mmc->events == 0) { jz_mmc_request_complete(mmc, MMC_NO_ERROR); } }}/* Prepare DMA to start data transfer to the MMC card */static void jz_mmc_dma_tx_start(struct jz_mmc_data *mmc){ struct mmc_request *request = mmc->request; int channel = mmc->dma_tx_ch; dma_addr_t dma_addr = PHYSADDR(request->buffer); u32 size = request->block_len * request->nob; DEBUG(3, "MMC DMA Tx start: chan %d buf 0x%08x phys 0x%08x " "blklen %d nob%d\n", channel, (u32)request->buffer, (u32)dma_addr, request->block_len, request->nob); dma_cache_wback_inv((unsigned int)request->buffer, size); jz_mmc_start_dma(channel, dma_addr, size, DMA_MODE_WRITE);}static int jz_mmc_check_status(struct mmc_request *request){ u32 status = REG_MSC_STAT; /* Checking for response or data timeout */ if (status & (MSC_STAT_TIME_OUT_RES | MSC_STAT_TIME_OUT_READ)) { printk("MMC/SD timeout, MMC_STAT 0x%x\n", status); return MMC_ERROR_TIMEOUT; } /* Checking for CRC error */ if (status & (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR | MSC_STAT_CRC_RES_ERR)) { printk("MMC/CD CRC error, MMC_STAT 0x%x\n", status); return MMC_ERROR_CRC; } return MMC_NO_ERROR;}/* Obtain response to the command and store it to response buffer */static void jz_mmc_get_response(struct mmc_request *request){ int i; u8 *buf; u32 data; DEBUG(2, "fetch response for request %d, cmd %d\n", request->rtype, request->cmd); buf = request->response; request->result = MMC_NO_ERROR; switch (request->rtype) { case RESPONSE_R1: case RESPONSE_R1B: case RESPONSE_R6: case RESPONSE_R3: case RESPONSE_R4: case RESPONSE_R5: { data = REG_MSC_RES; buf[0] = (data >> 8) & 0xff; buf[1] = data & 0xff; data = REG_MSC_RES; buf[2] = (data >> 8) & 0xff; buf[3] = data & 0xff; data = REG_MSC_RES; buf[4] = data & 0xff; DEBUG(3, "request %d, response [%02x %02x %02x %02x %02x]\n", request->rtype, buf[0], buf[1], buf[2], buf[3], buf[4]); break; } case RESPONSE_R2_CID: case RESPONSE_R2_CSD: { for (i = 0; i < 16; i += 2) { data = REG_MSC_RES; buf[i] = (data >> 8) & 0xff; buf[i+1] = data & 0xff; } DEBUG(3, "request %d, response [", request->rtype);#if CONFIG_MMC_DEBUG_VERBOSE > 2 if (g_mmc_debug >= 3) { int n; for (n = 0; n < 17; n++) printk("%02x ", buf[n]); printk("]\n"); }#endif break; } case RESPONSE_NONE: DEBUG(3, "No response\n"); break; default: DEBUG(3, "unhandled response type for request %d\n", request->rtype); break; }}/* Prepare MMC controller for card command execution */static int jz_mmc_exec_command(struct mmc_request *request){ struct jz_mmc_data *mmc = &jz_mmc_data; u32 cmdat = 0, timeout = 0x3fffff; int retval; /* stop MMC clock */ jz_mmc_stop_clock(); /* mask interrupts */ REG_MSC_IMASK = 0xffff; /* clear status */ REG_MSC_IREG = 0xffff; /* use 4-bit bus width when possible */ if (jz_mmc_data.use_4bit) cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; /* Set command type and events */ mmc->events = MMC_EVENT_NONE; switch (request->cmd) { /* MMC core extra command */ case MMC_CIM_RESET: cmdat |= MSC_CMDAT_INIT; /* Initialization sequence sent prior to command */ break; /* bc - broadcast - no response */ case MMC_GO_IDLE_STATE: case MMC_SET_DSR: break; /* bcr - broadcast with response */ case MMC_SEND_OP_COND: case MMC_ALL_SEND_CID: case MMC_GO_IRQ_STATE: break; /* adtc - addressed with data transfer */ case MMC_READ_DAT_UNTIL_STOP: case MMC_READ_SINGLE_BLOCK: case MMC_READ_MULTIPLE_BLOCK: cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ | MSC_CMDAT_DMA_EN; mmc->events = MMC_EVENT_RX_DMA_DONE | MMC_EVENT_DATA_DONE; break; case SEND_SCR: cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ; mmc->events = MMC_EVENT_DATA_DONE; break; case MMC_WRITE_DAT_UNTIL_STOP: case MMC_WRITE_BLOCK: case MMC_WRITE_MULTIPLE_BLOCK: case MMC_PROGRAM_CID: case MMC_PROGRAM_CSD: case MMC_SEND_WRITE_PROT: case MMC_GEN_CMD: case MMC_LOCK_UNLOCK: cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE | MSC_CMDAT_DMA_EN; mmc->events = MMC_EVENT_TX_DMA_DONE | MMC_EVENT_DATA_DONE | MMC_EVENT_PROG_DONE; break; case MMC_STOP_TRANSMISSION: mmc->events = MMC_EVENT_PROG_DONE; break; /* ac - no data transfer */ default: break; } /* Set response type */ switch (request->rtype) { case RESPONSE_NONE: break; case RESPONSE_R1B: cmdat |= MSC_CMDAT_BUSY; /*FALLTHRU*/ case RESPONSE_R1: cmdat |= MSC_CMDAT_RESPONSE_R1; break; case RESPONSE_R2_CID: case RESPONSE_R2_CSD: cmdat |= MSC_CMDAT_RESPONSE_R2; break; case RESPONSE_R3: cmdat |= MSC_CMDAT_RESPONSE_R3; break; case RESPONSE_R4: cmdat |= MSC_CMDAT_RESPONSE_R4; break; case RESPONSE_R5: cmdat |= MSC_CMDAT_RESPONSE_R5; break; case RESPONSE_R6: cmdat |= MSC_CMDAT_RESPONSE_R6;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -