static void s3cmci_send_command(struct s3cmci_host *host, struct mmc_command *cmd) { u32 ccon, imsk; imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT | S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT | S3C2410_SDIIMSK_RESPONSECRC; enable_imask(host, imsk); if (cmd->data) { host->complete_what = COMPLETION_XFERFINISH_RSPFIN; } else if (cmd->flags & MMC_RSP_PRESENT) { host->complete_what = COMPLETION_RSPFIN; } else { host->complete_what = COMPLETION_CMDSENT; } writel(cmd->arg, host->base + S3C2410_SDICMDARG); ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX; ccon|= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; if (cmd->flags & MMC_RSP_PRESENT) ccon |= S3C2410_SDICMDCON_WAITRSP; if (cmd->flags & MMC_RSP_136) ccon|= S3C2410_SDICMDCON_LONGRSP; writel(ccon, host->base + S3C2410_SDICMDCON); }
static 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_count(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 int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) { int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); host->pio_sgptr = 0; host->pio_words = 0; host->pio_count = 0; host->pio_active = rw ? XFER_WRITE : XFER_READ; if (rw) { do_pio_write(host); enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); } else { enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST); } return 0; }
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 int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) { u32 dcon, imsk, stoptries=3; /* write DCON register */ if (!data) { writel(0, host->base + S3C2410_SDIDCON); return 0; } while(readl(host->base + S3C2410_SDIDSTA) & (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { dbg(host, dbg_err, "mci_setup_data() transfer stillin progress.\n"); writel(0, host->base + S3C2410_SDIDCON); s3cmci_reset(host); if (0 == (stoptries--)) { #ifdef CONFIG_MMC_DEBUG dbg_dumpregs(host, "DRF"); #endif return -EINVAL; } } dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; if (host->dodma) { dcon |= S3C2410_SDIDCON_DMAEN; } if (host->bus_width == MMC_BUS_WIDTH_4) { dcon |= S3C2410_SDIDCON_WIDEBUS; } if (!(data->flags & MMC_DATA_STREAM)) { dcon |= S3C2410_SDIDCON_BLOCKMODE; } if (data->flags & MMC_DATA_WRITE) { dcon |= S3C2410_SDIDCON_TXAFTERRESP; dcon |= S3C2410_SDIDCON_XFER_TXSTART; } if (data->flags & MMC_DATA_READ) { dcon |= S3C2410_SDIDCON_RXAFTERCMD; dcon |= S3C2410_SDIDCON_XFER_RXSTART; } if (host->is2440) { dcon |= S3C2440_SDIDCON_DS_WORD; dcon |= S3C2440_SDIDCON_DATSTART; } writel(dcon, host->base + S3C2410_SDIDCON); /* write BSIZE register */ writel(data->blksz, host->base + S3C2410_SDIBSIZE); /* add to IMASK register */ imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; enable_imask(host, imsk); /* write TIMER register */ if (host->is2440) { writel(0x007FFFFF, host->base + S3C2410_SDITIMER); } else { writel(0x0000FFFF, host->base + S3C2410_SDITIMER); //FIX: set slow clock to prevent timeouts on read if (data->flags & MMC_DATA_READ) { writel(0xFF, host->base + S3C2410_SDIPRE); } } //debug_dump_registers(host, "Data setup:"); return 0; }
static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) { u32 dcon, imsk, stoptries = 3; /* write DCON register */ if (!data) { writel(0, host->base + S3C2410_SDIDCON); return 0; } if ((data->blksz & 3) != 0) { /* We cannot deal with unaligned blocks with more than * one block being transfered. */ if (data->blocks > 1) return -EINVAL; /* No support yet for non-word block transfers. */ return -EINVAL; } while (readl(host->base + S3C2410_SDIDSTA) & (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { dbg(host, dbg_err, "mci_setup_data() transfer stillin progress.\n"); writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); s3cmci_reset(host); if ((stoptries--) == 0) { dbg_dumpregs(host, "DRF"); return -EINVAL; } } dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; if (host->dodma) dcon |= S3C2410_SDIDCON_DMAEN; if (host->bus_width == MMC_BUS_WIDTH_4) dcon |= S3C2410_SDIDCON_WIDEBUS; if (!(data->flags & MMC_DATA_STREAM)) dcon |= S3C2410_SDIDCON_BLOCKMODE; if (data->flags & MMC_DATA_WRITE) { dcon |= S3C2410_SDIDCON_TXAFTERRESP; dcon |= S3C2410_SDIDCON_XFER_TXSTART; } if (data->flags & MMC_DATA_READ) { dcon |= S3C2410_SDIDCON_RXAFTERCMD; dcon |= S3C2410_SDIDCON_XFER_RXSTART; } if (host->is2440) { dcon |= S3C2440_SDIDCON_DS_WORD; dcon |= S3C2440_SDIDCON_DATSTART; } writel(dcon, host->base + S3C2410_SDIDCON); /* write BSIZE register */ writel(data->blksz, host->base + S3C2410_SDIBSIZE); /* add to IMASK register */ imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; enable_imask(host, imsk); /* write TIMER register */ if (host->is2440) { writel(0x007FFFFF, host->base + S3C2410_SDITIMER); } else { writel(0x0000FFFF, host->base + S3C2410_SDITIMER); /* FIX: set slow clock to prevent timeouts on read */ if (data->flags & MMC_DATA_READ) writel(0xFF, host->base + S3C2410_SDIPRE); } return 0; }