omap24xx_mmc.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,027 行 · 第 1/2 页
C
1,027 行
/* * drivers/media/mmc/omap.c * * Driver for OMAP24xx MMC controller. * * Author: Texas Instruments * * Copyright (C) 2004 Texas Instruments. * * 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. * * History: * * 2004/10/07 Madhusudhan Chikksture - Modified to integrate MMC hotplug requirements on * 2420 platforms */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/dma-mapping.h>#include <asm/irq.h>#include <asm/dma.h>#include <asm/hardware.h>#include <asm/mach-types.h>#include <asm/arch/bus.h>#include <linux/mmc/host.h>#include <linux/mmc/protocol.h>#include <linux/mmc/card.h>#include <asm/arch/mux.h>#include <asm/arch/gpio.h>#include <linux/ioport.h>#include <linux/blkdev.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <linux/mmc/mmc.h>#include <asm/arch/dma.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/fb.h>#include "omap24xx_mmc.h"/* global variables *//* Time out Value for the mmc wait for command */#define MMC_WAIT_TIMEOUT 10000#ifdef CONFIG_MMC_DEBUG#define DBG(x...) printk(x)#else#define DBG(x...) do { } while (0)#endif#ifdef CONFIG_MMC_HOTPLUG/************************ Hotplug specific function prototypes and macros ****/#define CONTROL_PADCONF_sdrc_a14 0x0030static int card_present;static unsigned short card_status;static void start_state_machine(void);static void set_hp_handler(void *);DECLARE_WORK(set_hp_work, &set_hp_handler, NULL);static void stop_state_machine(void);static void stop_hp_handler(void *);DECLARE_WORK(stop_hp_work, &stop_hp_handler, NULL);static struct mmc_omap_host *saved_host;static void run_sbin_hotplug(int insert);void mmc_cd_register(struct hotplug_structure *ptr);int mmc_hotplug_insert(struct mmc_host *host);int mmc_hotplug_remove(struct mmc_host *host);#define GPIO_0 0#define DEBOUNCE_ENABLE 1#define DEBOUNCE_TIME 10#define INSERT 1#define REMOVE 0/************************************************************************/static __inline__ u8 omap_mmc_pin_out(u32 offset, u8 val){ return writeb(val, OMAP24XX_VA_SYSTEM_CONTROL_BASE + offset);}#endifstatic voidmmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd){ u32 cmdreg; u32 resptype; u32 cmdtype; udelay(500); DBG("MMC%d: CMD%d, argument 0x%08x", host->id, cmd->opcode, cmd->arg); if (cmd->flags & MMC_RSP_SHORT) DBG(", 32-bit response"); if (cmd->flags & MMC_RSP_LONG) DBG(", 128-bit response"); if (cmd->flags & MMC_RSP_CRC) DBG(", CRC"); if (cmd->flags & MMC_RSP_BUSY) DBG(", busy notification"); DBG("\n"); host->cmd = cmd; resptype = 0; cmdtype = 0; /* Protocol layer does not provide response type, * but our hardware needs to know exact type, not just size! */ switch (cmd->flags & MMC_RSP_MASK) { case MMC_RSP_NONE: /* resp 0 */ break; case MMC_RSP_SHORT: /* resp 1, resp 1b */ /* OR resp 3!! (assume this if bus is set opendrain) */ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) resptype = 3; else resptype = 1; break; case MMC_RSP_LONG: /* resp 2 */ resptype = 2; break; } /* Protocol layer does not provide command type, but our hardware * needs it! * any data transfer means adtc type (but that information is not * in command structure, so we flagged it into host struct.) * However, telling bc, bcr and ac apart based on response is * not foolproof: * CMD0 = bc = resp0 CMD15 = ac = resp0 * CMD2 = bcr = resp2 CMD10 = ac = resp2 * * Resolve to best guess with some exception testing: * resp0 -> bc, except CMD15 = ac * rest are ac, except if opendrain */ if (host->datadir) { cmdtype = OMAP_MMC_CMDTYPE_ADTC; } else if (resptype == 0 && cmd->opcode != 15) { cmdtype = OMAP_MMC_CMDTYPE_BC; } else if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) { cmdtype = OMAP_MMC_CMDTYPE_BCR; } else { cmdtype = OMAP_MMC_CMDTYPE_AC; } cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) cmdreg |= 1 << 6; if (cmd->flags & MMC_RSP_BUSY) cmdreg |= 1 << 11; if (host->datadir == OMAP_MMC_DATADIR_READ) cmdreg |= 1 << 15; OMAP_MMC_WRITE(host->base, CTO, 200); OMAP_MMC_WRITE(host->base, ARGL, cmd->arg & 0xffff); OMAP_MMC_WRITE(host->base, ARGH, cmd->arg >> 16); OMAP_MMC_WRITE(host->base, IE, OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | OMAP_MMC_STAT_END_OF_DATA); OMAP_MMC_WRITE(host->base, CMD, cmdreg);}static voidmmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data){ host->data = NULL; host->datadir = OMAP_MMC_DATADIR_NONE; if (data->error == MMC_ERR_NONE) data->bytes_xfered += data->blocks * (1 << data->blksz_bits); if (!data->stop) { host->mrq = NULL; mmc_request_done(host->mmc, data->mrq); return; } mmc_omap_start_command(host, data->stop);}static voidmmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd){ host->cmd = NULL; switch (cmd->flags & MMC_RSP_MASK) { case MMC_RSP_NONE: /* resp 0 */ break; case MMC_RSP_SHORT: /* response types 1, 1b, 3, 4, 5, 6 */ cmd->resp[0] = OMAP_MMC_READ(host->base, RSP6) | (OMAP_MMC_READ(host->base, RSP7) << 16); DBG("MMC%d: Response %08x\n", host->id, cmd->resp[0]); break; case MMC_RSP_LONG: /* response type 2 */ cmd->resp[3] = OMAP_MMC_READ(host->base, RSP0) | (OMAP_MMC_READ(host->base, RSP1) << 16); cmd->resp[2] = OMAP_MMC_READ(host->base, RSP2) | (OMAP_MMC_READ(host->base, RSP3) << 16); cmd->resp[1] = OMAP_MMC_READ(host->base, RSP4) | (OMAP_MMC_READ(host->base, RSP5) << 16); cmd->resp[0] = OMAP_MMC_READ(host->base, RSP6) | (OMAP_MMC_READ(host->base, RSP7) << 16); DBG("MMC%d: Response %08x %08x %08x %08x\n", host->id, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); break; } if (host->data == NULL || cmd->error != MMC_ERR_NONE) { DBG("MMC%d: End request, err %x\n", host->id, cmd->error); host->mrq = NULL; mmc_request_done(host->mmc, cmd->mrq); }}static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs){ struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; u16 status; int end_command; int end_transfer; int ii; if (host->cmd == NULL && host->data == NULL) { status = OMAP_MMC_READ(host->base, STAT); printk(KERN_INFO "MMC%d: Spurious interrupt 0x%04x\n", host->id, status); if (status != 0) { OMAP_MMC_WRITE(host->base, STAT, status); OMAP_MMC_WRITE(host->base, IE, 0); } return IRQ_HANDLED; } end_command = 0; end_transfer = 0; while ((status = OMAP_MMC_READ(host->base, STAT)) != 0) { OMAP_MMC_WRITE(host->base, STAT, status); // Reset status bits DBG("\tMMC IRQ %04x\n", status); if ((status & OMAP_MMC_STAT_A_FULL) || ((status & OMAP_MMC_STAT_END_OF_DATA) && (host->bytesleft > 0))) { // Buffer almost full ii = host->bytesleft / 2; if (ii > 32) ii = 32; host->bytesleft -= ii * 2; while (ii-- > 0) *host->buffer++ = OMAP_MMC_READ(host->base, DATA); } if (status & OMAP_MMC_STAT_A_EMPTY) { // Buffer almost empty ii = host->bytesleft / 2; if (ii > 32) ii = 32; host->bytesleft -= ii * 2; while (ii-- > 0) OMAP_MMC_WRITE(host->base, DATA, *host->buffer++); } if (status & OMAP_MMC_STAT_END_OF_DATA) { // Block sent/received end_transfer = 1; } if (status & OMAP_MMC_STAT_DATA_TOUT) { // Data timeout printk(KERN_DEBUG "MMC%d: Data timeout\n", host->id); if (host->data) { host->data->error |= MMC_ERR_TIMEOUT; end_transfer = 1; } } if (status & OMAP_MMC_STAT_DATA_CRC) { // Data CRC error if (host->data) { host->data->error |= MMC_ERR_BADCRC; printk(KERN_DEBUG "MMC%d: Data CRC error, bytes left %d\n", host->id, host->bytesleft); end_transfer = 1; } else { printk(KERN_DEBUG "MMC%d: Data CRC error\n", host->id); } } if (status & OMAP_MMC_STAT_CMD_TOUT) { // Command timeout if (host->cmd) { /* Timeouts are normal in case of MMC_SEND_STATUS */ if (host->cmd->opcode != MMC_ALL_SEND_CID /* && !mmc_omap_cover_is_open(host) */ ) printk(KERN_ERR "MMC%d: Command timeout, CMD%d\n", host->id, host->cmd->opcode); host->cmd->error |= MMC_ERR_TIMEOUT; end_command = 1; } } if (status & OMAP_MMC_STAT_CMD_CRC) { /* Command CRC error */ printk(KERN_ERR "MMC%d: Command CRC error\n", host->id); if (host->cmd) { host->cmd->error |= MMC_ERR_BADCRC; end_command = 1; } } if (status & OMAP_MMC_STAT_OCR_BUSY) { // OCR Busy if (host->cmd && host->cmd->opcode != MMC_SEND_OP_COND && host->cmd->opcode != MMC_SET_RELATIVE_ADDR) { printk(KERN_DEBUG "MMC%d: OCR busy error, CMD%d\n", host->id, host->cmd->opcode); } } if (status & OMAP_MMC_STAT_CARD_ERR) { // Card status error printk(KERN_DEBUG "MMC%d: Card status error\n", host->id); if (host->cmd) { host->cmd->error |= MMC_ERR_FAILED; end_command = 1; } if (host->data) { host->data->error |= MMC_ERR_FAILED; end_transfer = 1; } } /* * NOTE: On 1610 the END_OF_CMD may come too early when * starting a write */ if ((status & OMAP_MMC_STAT_END_OF_CMD) && (!(status & OMAP_MMC_STAT_A_EMPTY))) { // End of command phase end_command = 1; } } if (end_command) { mmc_omap_cmd_done(host, host->cmd); } if (end_transfer) { mmc_omap_xfer_done(host, host->data); } return IRQ_HANDLED;}#ifdef CONFIG_MMC_HOTPLUG/* Interrupt service routine for handling card insertion and removal */static irqreturn_t mmc_omap_irq_cd(int irq, void *dev_id, struct pt_regs *regs){ saved_host = (struct mmc_omap_host *)dev_id; card_status = omap_get_gpio_datain(GPIO_0); if (card_status == 1) { card_present = REMOVE; stop_state_machine(); } else { card_present = INSERT; start_state_machine(); } return IRQ_HANDLED;}#endif/* * DMA call back function */static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data){ struct mmc_omap_host *host = (struct mmc_omap_host *)data; int dma_ch; /* FIXME: We ignore the possible errors for now. */ if (host->dma_ch < 0) { printk(KERN_ERR "MMC%d: DMA callback while DMA not enabled?\n", host->id); return; } dma_ch = host->dma_ch; host->dma_ch = -1; omap_free_dma(dma_ch); complete(&host->dma_completion);}static int mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req){ int sync_dev, dma_ch, r; const char *dev_name; /* If for some reason the DMA transfer is still active, * we wait for it to complete. This shouldn't normally happen. */ if (host->dma_ch != -1) { printk(" MMC : waiting for DMA channel \n"); wait_for_completion(&host->dma_completion); } init_completion(&host->dma_completion); if (!(req->data->flags & MMC_DATA_WRITE)) { sync_dev = MMC_DMA_RX_REQUEST + 1; dev_name = "MMC1 read"; } else { sync_dev = MMC_DMA_TX_REQUEST + 1; dev_name = "MMC1 write"; } r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb, host, &dma_ch); if (r != 0) { printk("MMC%d: omap_request_dma() failed with %d\n", host->id, r); return r; } if (!(req->data->flags & MMC_DATA_WRITE)) { /* 16 frames/block, 32 bytes/frame */ omap_set_dma_src_params(dma_ch, 0x00, (OMAP_MMC_BASE + OMAP_MMC_REG_DATA), 0, 0); omap_set_dma_dest_params(dma_ch, 0x01, virt_to_phys(req->data->req->buffer), 0, 0); OMAP_MMC_WRITE(host->base, BUF, 0x8f0f); r = 0; } else { omap_set_dma_dest_params(dma_ch, 0x00, (OMAP_MMC_BASE + OMAP_MMC_REG_DATA), 0, 0); omap_set_dma_src_params(dma_ch, 0x01, virt_to_phys(req->data->req->buffer), 0, 0); OMAP_MMC_WRITE(host->base, BUF, 0x0f8f); r = 0; } omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, 16, 16 * req->data->blocks, 0x02, sync_dev, r); host->dma_ch = dma_ch; omap_start_dma(dma_ch); return 0;}static void mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req){ int timeout; host->data = req->data;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?