?? s3cmci.c
字號:
/* * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver * * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/dma-mapping.h>#include <linux/clk.h>#include <linux/mmc/host.h>#include <linux/mmc/protocol.h>#include <linux/platform_device.h>#include <asm/dma.h>#include <asm/dma-mapping.h>#include <asm/io.h>#include <asm/arch/regs-sdi.h>#include <asm/arch/regs-gpio.h>#include "mmc_debug.h"#include "s3cmci.h"#define DRIVER_NAME "s3c-mci"enum dbg_channels { dbg_err = (1 << 0), dbg_debug = (1 << 1), dbg_info = (1 << 2), dbg_irq = (1 << 3), dbg_sg = (1 << 4), dbg_dma = (1 << 5), dbg_pio = (1 << 6), dbg_fail = (1 << 7), dbg_conf = (1 << 8),};static const int dbgmap_err = dbg_err | dbg_fail;static const int dbgmap_info = dbg_info | dbg_conf;static const int dbgmap_debug = dbg_debug;#define dbg(host, channels, args...) \ if (dbgmap_err & channels) \ dev_err(&host->pdev->dev, args); \ else if (dbgmap_info & channels) \ dev_info(&host->pdev->dev, args);\ else if (dbgmap_debug & channels) \ dev_dbg(&host->pdev->dev, args);#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)static struct s3c2410_dma_client s3cmci_dma_client = { .name = "s3c-mci",};static void finalize_request(struct s3cmci_host *host);static void s3cmci_send_request(struct mmc_host *mmc);static void s3cmci_reset(struct s3cmci_host *host);#ifdef CONFIG_MMC_DEBUGstatic inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix){ u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize; u32 datcon, datcnt, datsta, fsta, imask; con = readl(host->base + S3C2410_SDICON); pre = readl(host->base + S3C2410_SDIPRE); cmdarg = readl(host->base + S3C2410_SDICMDARG); cmdcon = readl(host->base + S3C2410_SDICMDCON); cmdsta = readl(host->base + S3C2410_SDICMDSTAT); r0 = readl(host->base + S3C2410_SDIRSP0); r1 = readl(host->base + S3C2410_SDIRSP1); r2 = readl(host->base + S3C2410_SDIRSP2); r3 = readl(host->base + S3C2410_SDIRSP3); timer = readl(host->base + S3C2410_SDITIMER); bsize = readl(host->base + S3C2410_SDIBSIZE); datcon = readl(host->base + S3C2410_SDIDCON); datcnt = readl(host->base + S3C2410_SDIDCNT); datsta = readl(host->base + S3C2410_SDIDSTA); fsta = readl(host->base + S3C2410_SDIFSTA); imask = readl(host->base + host->sdiimsk); dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n", prefix, con, pre, timer); dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n", prefix, cmdcon, cmdarg, cmdsta); dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]" " DSTA:[%08x] DCNT:[%08x]\n", prefix, datcon, fsta, datsta, datcnt); dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]" " R2:[%08x] R3:[%08x]\n", prefix, r0, r1, r2, r3);}static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd, int stop){ snprintf(host->dbgmsg_cmd, 300, "#%u%s op:%s(%i) arg:0x%08x flags:0x08%x retries:%u", host->ccnt, (stop?" (STOP)":""), mmc_cmd2str(cmd->opcode), cmd->opcode, cmd->arg, cmd->flags, cmd->retries); if (cmd->data) { snprintf(host->dbgmsg_dat, 300, "#%u bsize:%u blocks:%u bytes:%u", host->dcnt, cmd->data->blksz, cmd->data->blocks, cmd->data->blocks * cmd->data->blksz); } else { host->dbgmsg_dat[0] = '\0'; }}static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd, int fail){ unsigned int dbglvl = fail?dbg_fail:dbg_debug; if (!cmd) return; if (cmd->error == MMC_ERR_NONE) { dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n", host->dbgmsg_cmd, cmd->resp[0]); } else { dbg(host, dbglvl, "CMD[%s] %s Status:%s\n", mmc_err2str(cmd->error), host->dbgmsg_cmd, host->status); } if (!cmd->data) return; if (cmd->data->error == MMC_ERR_NONE) { dbg(host, dbglvl, "DAT[%s] %s\n", mmc_err2str(cmd->data->error), host->dbgmsg_dat); } else { dbg(host, dbglvl, "DAT[%s] %s DCNT:0x%08x\n", mmc_err2str(cmd->data->error), host->dbgmsg_dat, readl(host->base + S3C2410_SDIDCNT)); }}#endifstatic inline u32 enable_imask(struct s3cmci_host *host, u32 imask){ u32 newmask; newmask = readl(host->base + host->sdiimsk); newmask|= imask; writel(newmask, host->base + host->sdiimsk); return newmask;}static inline u32 disable_imask(struct s3cmci_host *host, u32 imask){ u32 newmask; newmask = readl(host->base + host->sdiimsk); newmask&= ~imask; writel(newmask, host->base + host->sdiimsk); return newmask;}static inline void clear_imask(struct s3cmci_host *host){ writel(0, host->base + host->sdiimsk);}static inline int get_data_buffer(struct s3cmci_host *host, volatile u32 *words, volatile u32 **pointer){ struct scatterlist *sg; if (host->pio_active == XFER_NONE) return -EINVAL; if ((!host->mrq) || (!host->mrq->data)) return -EINVAL; if (host->pio_sgptr >= host->mrq->data->sg_len) { dbg(host, dbg_debug, "no more buffers (%i/%i)\n", host->pio_sgptr, host->mrq->data->sg_len); return -EBUSY; } sg = &host->mrq->data->sg[host->pio_sgptr]; *words = sg->length >> 2; *pointer= page_address(sg->page) + sg->offset; host->pio_sgptr++; dbg(host, dbg_sg, "new buffer (%i/%i)\n", host->pio_sgptr, host->mrq->data->sg_len); return 0;}#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)static inline void do_pio_read(struct s3cmci_host *host){ int res; u32 fifo; void __iomem *from_ptr; //write real prescaler to host, it might be set slow to fix writel(host->prescaler, host->base + S3C2410_SDIPRE); from_ptr = host->base + host->sdidata; while ((fifo = FIFO_FILL(host))) { if (!host->pio_words) { res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); if (res) { host->pio_active = XFER_NONE; host->complete_what = COMPLETION_FINALIZE; dbg(host, dbg_pio, "pio_read(): " "complete (no more data).\n"); return; } dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n", host->pio_words, host->pio_ptr); } dbg(host, dbg_pio, "pio_read(): fifo:[%02i] " "buffer:[%03i] dcnt:[%08X]\n", fifo, host->pio_words, readl(host->base + S3C2410_SDIDCNT)); if (fifo > host->pio_words) fifo = host->pio_words; host->pio_words-= fifo; host->pio_count+= fifo; while(fifo--) { *(host->pio_ptr++) = readl(from_ptr); } } if (!host->pio_words) { res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); if (res) { dbg(host, dbg_pio, "pio_read(): " "complete (no more buffers).\n"); host->pio_active = XFER_NONE; host->complete_what = COMPLETION_FINALIZE; return; } } enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);}static inline void do_pio_write(struct s3cmci_host *host){ int res; u32 fifo; void __iomem *to_ptr; to_ptr = host->base + host->sdidata; while ((fifo = FIFO_FREE(host))) { if (!host->pio_words) { res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); if (res) { dbg(host, dbg_pio, "pio_write(): " "complete (no more data).\n"); host->pio_active = XFER_NONE; return; } dbg(host, dbg_pio, "pio_write(): " "new source: [%i]@[%p]\n", host->pio_words, host->pio_ptr); } if (fifo > host->pio_words) fifo = host->pio_words; host->pio_words-= fifo; host->pio_count+= fifo; while(fifo--) { writel(*(host->pio_ptr++), to_ptr); } } enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);}static void pio_tasklet(unsigned long data){ struct s3cmci_host *host = (struct s3cmci_host *) data; if (host->pio_active == XFER_WRITE) do_pio_write(host); if (host->pio_active == XFER_READ) do_pio_read(host); if (host->complete_what == COMPLETION_FINALIZE) { clear_imask(host); if (host->pio_active != XFER_NONE) { dbg(host, dbg_err, "unfinished %s " "- pio_count:[%u] pio_words:[%u]\n", (host->pio_active == XFER_READ)?"read":"write", host->pio_count, host->pio_words); host->mrq->data->error = MMC_ERR_DMA; } disable_irq(host->irq); finalize_request(host); }}/* * ISR for SDI Interface IRQ * Communication between driver and ISR works as follows: * host->mrq points to current request * host->complete_what tells the ISR when the request is considered done * COMPLETION_CMDSENT when the command was sent * COMPLETION_RSPFIN when a response was received * COMPLETION_XFERFINISH when the data transfer is finished * COMPLETION_XFERFINISH_RSPFIN both of the above. * host->complete_request is the completion-object the driver waits for * * 1) Driver sets up host->mrq and host->complete_what * 2) Driver prepares the transfer * 3) Driver enables interrupts * 4) Driver starts transfer * 5) Driver waits for host->complete_rquest * 6) ISR checks for request status (errors and success) * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error * 7) ISR completes host->complete_request * 8) ISR disables interrupts * 9) Driver wakes up and takes care of the request * * Note: "->error"-fields are expected to be set to 0 before the request * was issued by mmc.c - therefore they are only set, when an error * contition comes up */static irqreturn_t s3cmci_irq(int irq, void *dev_id){ struct s3cmci_host *host; struct mmc_command *cmd; u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk; u32 mci_cclear, mci_dclear; unsigned long iflags; host = (struct s3cmci_host *)dev_id; spin_lock_irqsave(&host->complete_lock, iflags); mci_csta = readl(host->base + S3C2410_SDICMDSTAT); mci_dsta = readl(host->base + S3C2410_SDIDSTA); mci_dcnt = readl(host->base + S3C2410_SDIDCNT); mci_fsta = readl(host->base + S3C2410_SDIFSTA); mci_imsk = readl(host->base + host->sdiimsk); mci_cclear = 0; mci_dclear = 0; if ((host->complete_what == COMPLETION_NONE) || (host->complete_what == COMPLETION_FINALIZE)) { host->status = "nothing to complete"; clear_imask(host); goto irq_out; } if (!host->mrq) { host->status = "no active mrq"; clear_imask(host); goto irq_out; } cmd = host->cmd_is_stop?host->mrq->stop:host->mrq->cmd; if (!cmd) { host->status = "no active cmd"; clear_imask(host); goto irq_out; } if (!host->dodma) { if ((host->pio_active == XFER_WRITE) && (mci_fsta & S3C2410_SDIFSTA_TFDET)) { disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); tasklet_schedule(&host->pio_tasklet); host->status = "pio tx"; } if ((host->pio_active == XFER_READ) && (mci_fsta & S3C2410_SDIFSTA_RFDET)) { disable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST); tasklet_schedule(&host->pio_tasklet); host->status = "pio rx"; } } if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) { cmd->error = MMC_ERR_TIMEOUT; host->status = "error: command timeout"; goto fail_transfer; } if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) { if (host->complete_what == COMPLETION_CMDSENT) { host->status = "ok: command sent"; goto close_transfer; } mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT; } if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) { if (host->complete_what == COMPLETION_RSPFIN) { host->status = "ok: command response received"; goto close_transfer; } if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) host->complete_what = COMPLETION_XFERFINISH; mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN; } /* errors handled after this point are only relevant when a data transfer is in progress */ if (!cmd->data) goto clear_status_bits; /* Check for FIFO failure */ if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) { cmd->data->error = MMC_ERR_FIFO; host->status = "error: fifo failure"; goto fail_transfer; } if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) { cmd->data->error = MMC_ERR_BADCRC; host->status = "error: bad data crc (outgoing)"; goto fail_transfer; } if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) { cmd->data->error = MMC_ERR_BADCRC; host->status = "error: bad data crc (incoming)"; goto fail_transfer; } if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) { cmd->data->error = MMC_ERR_TIMEOUT; host->status = "error: data timeout"; goto fail_transfer; } if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) { if (host->complete_what == COMPLETION_XFERFINISH) { host->status = "ok: data transfer completed"; goto close_transfer; } if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) { host->complete_what = COMPLETION_RSPFIN; } mci_dclear |= S3C2410_SDIDSTA_XFERFINISH; }clear_status_bits: writel(mci_cclear, host->base + S3C2410_SDICMDSTAT); writel(mci_dclear, host->base + S3C2410_SDIDSTA); goto irq_out;fail_transfer: host->pio_active = XFER_NONE;close_transfer: host->complete_what = COMPLETION_FINALIZE; clear_imask(host); tasklet_schedule(&host->pio_tasklet); goto irq_out;irq_out: dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x " "fsta:0x%08x dcnt:0x%08x status:%s.\n", mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status); spin_unlock_irqrestore(&host->complete_lock, iflags); return IRQ_HANDLED;}/* * ISR for the CardDetect Pin*/static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id){ struct s3cmci_host *host = (struct s3cmci_host *)dev_id; dbg(host, dbg_irq, "card detect\n"); mmc_detect_change(host->mmc, 500); return IRQ_HANDLED;}void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id, int size, enum s3c2410_dma_buffresult result){ unsigned long iflags; u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt; struct s3cmci_host *host = (struct s3cmci_host *)buf_id; mci_csta = readl(host->base + S3C2410_SDICMDSTAT); mci_dsta = readl(host->base + S3C2410_SDIDSTA); mci_fsta = readl(host->base + S3C2410_SDIFSTA); mci_dcnt = readl(host->base + S3C2410_SDIDCNT); if ((!host->mrq) || (!host->mrq) || (!host->mrq->data)) return; if (!host->dmatogo) return; spin_lock_irqsave(&host->complete_lock, iflags); if (result != S3C2410_RES_OK) { dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x " "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n", mci_csta, mci_dsta, mci_fsta, mci_dcnt, result, host->dmatogo); goto fail_request; } host->dmatogo--; if (host->dmatogo) { dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] " "DCNT:[%08x] toGo:%u\n", size, mci_dsta, mci_dcnt, host->dmatogo); goto out; } dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n", size, mci_dsta, mci_dcnt); host->complete_what = COMPLETION_FINALIZE;out: tasklet_schedule(&host->pio_tasklet); spin_unlock_irqrestore(&host->complete_lock, iflags); return;fail_request: host->mrq->data->error = MMC_ERR_DMA; host->complete_what = COMPLETION_FINALIZE; writel(0, host->base + host->sdiimsk); goto out;}static void finalize_request(struct s3cmci_host *host){ struct mmc_request *mrq = host->mrq; struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd; int debug_as_failure = 0; u32 mci_con; if (host->complete_what != COMPLETION_FINALIZE) return; if (!mrq) return; if (cmd->data && (cmd->error == MMC_ERR_NONE) && (cmd->data->error == MMC_ERR_NONE)) { if (host->dodma && (!host->dma_complete)) { dbg(host, dbg_dma, "DMA Missing!\n"); return; } } // Read response cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0); cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1); cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2); cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3); // reset clock speed, as it could still be set low for writel(host->prescaler, host->base + S3C2410_SDIPRE); if (cmd->error) debug_as_failure = 1; if (cmd->data && cmd->data->error) debug_as_failure = 1;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -