static inline void wbsd_get_long_reply(struct wbsd_host* host, struct mmc_command* cmd) { int i; /* * Correct response type? */ if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) { cmd->error = MMC_ERR_INVALID; return; } for (i = 0;i < 4;i++) { cmd->resp[i] = wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24; cmd->resp[i] |= wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16; cmd->resp[i] |= wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8; cmd->resp[i] |= wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0; } }
static void wbsd_init_device(struct wbsd_host *host) { u8 setup, ier; /* * Reset chip (SD/MMC part) and fifo. */ setup = wbsd_read_index(host, WBSD_IDX_SETUP); setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); /* * Set DAT3 to input */ setup &= ~WBSD_DAT3_H; wbsd_write_index(host, WBSD_IDX_SETUP, setup); host->flags &= ~WBSD_FIGNORE_DETECT; /* * Read back default clock. */ host->clk = wbsd_read_index(host, WBSD_IDX_CLK); /* * Power down port. */ outb(WBSD_POWER_N, host->base + WBSD_CSR); /* * Set maximum timeout. */ wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); /* * Test for card presence */ if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT) host->flags |= WBSD_FCARD_PRESENT; else host->flags &= ~WBSD_FCARD_PRESENT; /* * Enable interesting interrupts. */ ier = 0; ier |= WBSD_EINT_CARD; ier |= WBSD_EINT_FIFO_THRE; ier |= WBSD_EINT_CRC; ier |= WBSD_EINT_TIMEOUT; ier |= WBSD_EINT_TC; outb(ier, host->base + WBSD_EIR); /* * Clear interrupts. */ inb(host->base + WBSD_ISR); }
static void wbsd_init_device(struct wbsd_host* host) { u8 setup, ier; /* * Reset chip (SD/MMC part) and fifo. */ setup = wbsd_read_index(host, WBSD_IDX_SETUP); setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); /* * Read back default clock. */ host->clk = wbsd_read_index(host, WBSD_IDX_CLK); /* * Power down port. */ outb(WBSD_POWER_N, host->base + WBSD_CSR); /* * Set maximum timeout. */ wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); /* * Enable interesting interrupts. */ ier = 0; ier |= WBSD_EINT_CARD; ier |= WBSD_EINT_FIFO_THRE; ier |= WBSD_EINT_CCRC; ier |= WBSD_EINT_TIMEOUT; ier |= WBSD_EINT_CRC; ier |= WBSD_EINT_TC; outb(ier, host->base + WBSD_EIR); /* * Clear interrupts. */ inb(host->base + WBSD_ISR); }
static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd) { int i; u8 status, isr; /* * Clear accumulated ISR. The interrupt routine * will fill this one with events that occur during * transfer. */ host->isr = 0; /* * Send the command (CRC calculated by host). */ outb(cmd->opcode, host->base + WBSD_CMDR); for (i = 3; i >= 0; i--) outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); cmd->error = 0; /* * Wait for the request to complete. */ do { status = wbsd_read_index(host, WBSD_IDX_STATUS); } while (status & WBSD_CARDTRAFFIC); /* * Do we expect a reply? */ if (cmd->flags & MMC_RSP_PRESENT) { /* * Read back status. */ isr = host->isr; /* Card removed? */ if (isr & WBSD_INT_CARD) cmd->error = -ENOMEDIUM; /* Timeout? */ else if (isr & WBSD_INT_TIMEOUT) cmd->error = -ETIMEDOUT; /* CRC? */ else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) cmd->error = -EILSEQ; /* All ok */ else { if (cmd->flags & MMC_RSP_136) wbsd_get_long_reply(host, cmd); else wbsd_get_short_reply(host, cmd); } } }
static void wbsd_reset(struct wbsd_host *host) { u8 setup; pr_err("%s: Resetting chip\n", mmc_hostname(host->mmc)); /* * Soft reset of chip (SD/MMC part). */ setup = wbsd_read_index(host, WBSD_IDX_SETUP); setup |= WBSD_SOFT_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); }
static void wbsd_reset(struct wbsd_host* host) { u8 setup; printk(KERN_ERR DRIVER_NAME ": Resetting chip\n"); /* * Soft reset of chip (SD/MMC part). */ setup = wbsd_read_index(host, WBSD_IDX_SETUP); setup |= WBSD_SOFT_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); }
static inline void wbsd_get_short_reply(struct wbsd_host *host, struct mmc_command *cmd) { /* * Correct response type? */ if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) { cmd->error = -EILSEQ; return; } cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24; cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16; cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8; cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0; cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24; }
static void wbsd_tasklet_block(unsigned long param) { struct wbsd_host* host = (struct wbsd_host*)param; struct mmc_data* data; spin_lock(&host->lock); if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) != WBSD_CRC_OK) { data = wbsd_get_data(host); if (!data) goto end; DBGF("CRC error\n"); data->error = MMC_ERR_BADCRC; tasklet_schedule(&host->finish_tasklet); } end: spin_unlock(&host->lock); }
static void wbsd_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) { struct wbsd_host* host = mmc_priv(mmc); u8 clk, setup, pwr; DBGF("clock %uHz busmode %u powermode %u Vdd %u\n", ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); spin_lock_bh(&host->lock); /* * Reset the chip on each power off. * Should clear out any weird states. */ if (ios->power_mode == MMC_POWER_OFF) wbsd_init_device(host); if (ios->clock >= 24000000) clk = WBSD_CLK_24M; else if (ios->clock >= 16000000) clk = WBSD_CLK_16M; else if (ios->clock >= 12000000) clk = WBSD_CLK_12M; else clk = WBSD_CLK_375K; /* * Only write to the clock register when * there is an actual change. */ if (clk != host->clk) { wbsd_write_index(host, WBSD_IDX_CLK, clk); host->clk = clk; } if (ios->power_mode != MMC_POWER_OFF) { /* * Power up card. */ pwr = inb(host->base + WBSD_CSR); pwr &= ~WBSD_POWER_N; outb(pwr, host->base + WBSD_CSR); /* * This behaviour is stolen from the * Windows driver. Don't know why, but * it is needed. */ setup = wbsd_read_index(host, WBSD_IDX_SETUP); if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) setup |= WBSD_DAT3_H; else setup &= ~WBSD_DAT3_H; wbsd_write_index(host, WBSD_IDX_SETUP, setup); mdelay(1); } spin_unlock_bh(&host->lock); }
static void wbsd_prepare_data(struct wbsd_host* host, struct mmc_data* data) { u16 blksize; u8 setup; unsigned long dmaflags; DBGF("blksz %04x blks %04x flags %08x\n", data->blksz, data->blocks, data->flags); DBGF("tsac %d ms nsac %d clk\n", data->timeout_ns / 1000000, data->timeout_clks); /* * Calculate size. */ host->size = data->blocks * data->blksz; /* * Check timeout values for overflow. * (Yes, some cards cause this value to overflow). */ if (data->timeout_ns > 127000000) wbsd_write_index(host, WBSD_IDX_TAAC, 127); else wbsd_write_index(host, WBSD_IDX_TAAC, data->timeout_ns/1000000); if (data->timeout_clks > 255) wbsd_write_index(host, WBSD_IDX_NSAC, 255); else wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); /* * Inform the chip of how large blocks will be * sent. It needs this to determine when to * calculate CRC. * * Space for CRC must be included in the size. */ blksize = data->blksz + 2; wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); /* * Clear the FIFO. This is needed even for DMA * transfers since the chip still uses the FIFO * internally. */ setup = wbsd_read_index(host, WBSD_IDX_SETUP); setup |= WBSD_FIFO_RESET; wbsd_write_index(host, WBSD_IDX_SETUP, setup); /* * DMA transfer? */ if (host->dma >= 0) { /* * The buffer for DMA is only 64 kB. */ BUG_ON(host->size > 0x10000); if (host->size > 0x10000) { data->error = MMC_ERR_INVALID; return; } /* * Transfer data from the SG list to * the DMA buffer. */ if (data->flags & MMC_DATA_WRITE) wbsd_sg_to_dma(host, data); /* * Initialise the ISA DMA controller. */ dmaflags = claim_dma_lock(); disable_dma(host->dma); clear_dma_ff(host->dma); if (data->flags & MMC_DATA_READ) set_dma_mode(host->dma, DMA_MODE_READ); else set_dma_mode(host->dma, DMA_MODE_WRITE); set_dma_addr(host->dma, host->dma_addr); set_dma_count(host->dma, host->size); enable_dma(host->dma); release_dma_lock(dmaflags); /* * Enable DMA on the host. */ wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_SINGLE | WBSD_DMA_ENABLE); } else { /* * This flag is used to keep printk * output to a minimum. */ host->firsterr = 1; /* * Initialise the SG list. */ wbsd_init_sg(host, data); /* * Turn off DMA. */ wbsd_write_index(host, WBSD_IDX_DMA, 0); /* * Set up FIFO threshold levels (and fill * buffer if doing a write). */ if (data->flags & MMC_DATA_READ) { wbsd_write_index(host, WBSD_IDX_FIFOEN, WBSD_FIFOEN_FULL | 8); } else { wbsd_write_index(host, WBSD_IDX_FIFOEN, WBSD_FIFOEN_EMPTY | 8); wbsd_fill_fifo(host); } } data->error = MMC_ERR_NONE; }
static void wbsd_send_command(struct wbsd_host* host, struct mmc_command* cmd) { int i; u8 status, eir, isr; DBGF("Sending cmd (%x)\n", cmd->opcode); /* * Disable interrupts as the interrupt routine * will destroy the contents of ISR. */ eir = inb(host->base + WBSD_EIR); outb(0, host->base + WBSD_EIR); /* * Send the command (CRC calculated by host). */ outb(cmd->opcode, host->base + WBSD_CMDR); for (i = 3;i >= 0;i--) outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); cmd->error = MMC_ERR_NONE; /* * Wait for the request to complete. */ do { status = wbsd_read_index(host, WBSD_IDX_STATUS); } while (status & WBSD_CARDTRAFFIC); /* * Do we expect a reply? */ if ((cmd->flags & MMC_RSP_MASK) != MMC_RSP_NONE) { /* * Read back status. */ isr = inb(host->base + WBSD_ISR); /* Card removed? */ if (isr & WBSD_INT_CARD) cmd->error = MMC_ERR_TIMEOUT; /* Timeout? */ else if (isr & WBSD_INT_TIMEOUT) cmd->error = MMC_ERR_TIMEOUT; /* CRC? */ else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) cmd->error = MMC_ERR_BADCRC; /* All ok */ else { if ((cmd->flags & MMC_RSP_MASK) == MMC_RSP_SHORT) wbsd_get_short_reply(host, cmd); else wbsd_get_long_reply(host, cmd); } } /* * Restore interrupt mask to previous value. */ outb(eir, host->base + WBSD_EIR); /* * Call the interrupt routine to jump start * interrupts. */ wbsd_irq(0, host, NULL); DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error); }