/* * Handle transmitted data */ static void at91_mci_handle_transmitted(struct at91mci_host *host) { struct mmc_command *cmd; struct mmc_data *data; pr_debug("Handling the transmit\n"); /* Disable the transfer */ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); /* Now wait for cmd ready */ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); cmd = host->cmd; if (!cmd) return; data = cmd->data; if (!data) return; if (cmd->data->blocks > 1) { pr_debug("multiple write : wait for BLKE...\n"); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); } else at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); data->bytes_xfered = host->total_length; }
/* * Handle after a dma read */ static void at91_mci_post_dma_read(struct at91mci_host *host) { struct mmc_command *cmd; struct mmc_data *data; pr_debug("post dma read\n"); cmd = host->cmd; if (!cmd) { pr_debug("no command\n"); return; } data = cmd->data; if (!data) { pr_debug("no data\n"); return; } while (host->in_use_index < host->transfer_index) { struct scatterlist *sg; pr_debug("finishing index %d\n", host->in_use_index); sg = &data->sg[host->in_use_index++]; pr_debug("Unmapping page %08X\n", sg->dma_address); dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); data->bytes_xfered += sg->length; if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ unsigned int *buffer; int index; /* Swap the contents of the buffer */ buffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; pr_debug("buffer = %p, length = %d\n", buffer, sg->length); for (index = 0; index < (sg->length / 4); index++) buffer[index] = swab32(buffer[index]); kunmap_atomic(buffer, KM_BIO_SRC_IRQ); } flush_dcache_page(sg_page(sg)); } /* Is there another transfer to trigger? */ if (host->transfer_index < data->sg_len) at91_mci_pre_dma_read(host); else { at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); } pr_debug("post dma read done\n"); }
static void at91_mci_post_dma_read(struct at91mci_host *host) { struct mmc_command *cmd; struct mmc_data *data; unsigned int len, i, size; unsigned *dmabuf = host->buffer; pr_debug("post dma read\n"); cmd = host->cmd; if (!cmd) { pr_debug("no command\n"); return; } data = cmd->data; if (!data) { pr_debug("no data\n"); return; } size = data->blksz * data->blocks; len = data->sg_len; at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); for (i = 0; i < len; i++) { struct scatterlist *sg; int amount; unsigned int *sgbuffer; sg = &data->sg[i]; sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; amount = min(size, sg->length); size -= amount; if (cpu_is_at91rm9200()) { int index; for (index = 0; index < (amount / 4); index++) sgbuffer[index] = swab32(*dmabuf++); } else { char *tmpv = (char *)dmabuf; memcpy(sgbuffer, tmpv, amount); tmpv += amount; dmabuf = (unsigned *)tmpv; } flush_kernel_dcache_page(sg_page(sg)); kunmap_atomic(sgbuffer); data->bytes_xfered += amount; if (size == 0) break; } pr_debug("post dma read done\n"); }
/* * Enable the controller */ static void at91_mci_enable(struct at91mci_host *host) { at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a); /* use Slot A or B (only one at same time) */ at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); }
static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { int clkdiv; struct at91mci_host *host = mmc_priv(mmc); unsigned long at91_master_clock = clk_get_rate(host->mci_clk); host->bus_mode = ios->bus_mode; if (ios->clock == 0) { at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS); clkdiv = 0; } else { at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); if ((at91_master_clock % (ios->clock * 2)) == 0) clkdiv = ((at91_master_clock / ios->clock) / 2) - 1; else clkdiv = (at91_master_clock / ios->clock) / 2; pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv, at91_master_clock / (2 * (clkdiv + 1))); } if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { pr_debug("MMC: Setting controller bus width to 4\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS); } else { pr_debug("MMC: Setting controller bus width to 1\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); if (gpio_is_valid(host->board->vcc_pin)) { switch (ios->power_mode) { case MMC_POWER_OFF: gpio_set_value(host->board->vcc_pin, 0); break; case MMC_POWER_UP: gpio_set_value(host->board->vcc_pin, 1); break; case MMC_POWER_ON: break; default: WARN_ON(1); } } }
static irqreturn_t at91_mmc_det_irq(int irq, void *_host) { struct at91mci_host *host = _host; int present; /* entering this ISR means that we have configured det_pin: * we can use its value in board structure */ present = !gpio_get_value(host->board->det_pin); /* * we expect this irq on both insert and remove, * and use a short delay to debounce. */ if (present != host->present) { host->present = present; pr_debug("%s: card %s\n", mmc_hostname(host->mmc), present ? "insert" : "remove"); if (!present) { pr_debug("****** Resetting SD-card bus width ******\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } /* 0.5s needed because of early card detect switch firing */ mmc_detect_change(host->mmc, msecs_to_jiffies(500)); } return IRQ_HANDLED; }
static void at91_mci_enable(struct at91mci_host *host) { unsigned int mr; at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); mr = AT91_MCI_PDCMODE | 0x34a; if (at91mci_is_mci1rev2xx()) mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; at91_mci_write(host, AT91_MCI_MR, mr); at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); }
/* * Enable the controller */ static void at91_mci_enable(struct at91mci_host *host) { unsigned int mr; at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); mr = AT91_MCI_PDCMODE | 0x34a; if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; at91_mci_write(host, AT91_MCI_MR, mr); /* use Slot A or B (only one at same time) */ at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); }
static void at91_reset_host(struct at91mci_host *host) { unsigned long flags; u32 mr; u32 sdcr; u32 dtor; u32 imr; local_irq_save(flags); imr = at91_mci_read(host, AT91_MCI_IMR); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; sdcr = at91_mci_read(host, AT91_MCI_SDCR); dtor = at91_mci_read(host, AT91_MCI_DTOR); at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); at91_mci_write(host, AT91_MCI_MR, mr); at91_mci_write(host, AT91_MCI_SDCR, sdcr); at91_mci_write(host, AT91_MCI_DTOR, dtor); at91_mci_write(host, AT91_MCI_IER, imr); at91_mci_read(host, AT91_MCI_SR); local_irq_restore(flags); }
/* * Reset the controller and restore most of the state */ static void at91_reset_host(struct at91mci_host *host) { unsigned long flags; u32 mr; u32 sdcr; u32 dtor; u32 imr; local_irq_save(flags); imr = at91_mci_read(host, AT91_MCI_IMR); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); /* save current state */ mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; sdcr = at91_mci_read(host, AT91_MCI_SDCR); dtor = at91_mci_read(host, AT91_MCI_DTOR); /* reset the controller */ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); /* restore state */ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); at91_mci_write(host, AT91_MCI_MR, mr); at91_mci_write(host, AT91_MCI_SDCR, sdcr); at91_mci_write(host, AT91_MCI_DTOR, dtor); at91_mci_write(host, AT91_MCI_IER, imr); /* make sure sdio interrupts will fire */ at91_mci_read(host, AT91_MCI_SR); local_irq_restore(flags); }
static int at91_mci_handle_cmdrdy(struct at91mci_host *host) { if (!host->cmd) return 1; else if (!host->cmd->data) { if (host->flags & FL_SENT_STOP) { at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); } else return 1; } else if (host->cmd->data->flags & MMC_DATA_WRITE) { at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE); at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); } return 0; }
/*Handle after command sent ready*/ static int at91_mci_handle_cmdrdy(struct at91mci_host *host) { if (!host->cmd) return 1; else if (!host->cmd->data) { if (host->flags & FL_SENT_STOP) { /*After multi block write, we must wait for NOTBUSY*/ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); } else return 1; } else if (host->cmd->data->flags & MMC_DATA_WRITE) { /*After sendding multi-block-write command, start DMA transfer*/ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE); at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); } /* command not completed, have to wait */ return 0; }
static void at91_mci_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct at91mci_host *host = mmc_priv(mmc); pr_debug("%s: sdio_irq %c : %s\n", mmc_hostname(host->mmc), host->board->slot_b ? 'B':'A', enable ? "enable" : "disable"); at91_mci_write(host, enable ? AT91_MCI_IER : AT91_MCI_IDR, host->board->slot_b ? AT91_MCI_SDIOIRQB : AT91_MCI_SDIOIRQA); }
/* * Handle a command that has been completed */ static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status) { struct mmc_command *cmd = host->cmd; struct mmc_data *data = cmd->data; at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); if (host->buffer) { dma_unmap_single(NULL, host->physical_address, host->total_length, DMA_TO_DEVICE); kfree(host->buffer); host->buffer = NULL; } pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n", status, at91_mci_read(host, AT91_MCI_SR), cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); if (status & AT91_MCI_ERRORS) { if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) { cmd->error = 0; } else { if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) { if (data) { if (status & AT91_MCI_DTOE) data->error = -ETIMEDOUT; else if (status & AT91_MCI_DCRCE) data->error = -EILSEQ; } } else { if (status & AT91_MCI_RTOE) cmd->error = -ETIMEDOUT; else if (status & AT91_MCI_RCRCE) cmd->error = -EILSEQ; else cmd->error = -EIO; } pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n", cmd->error, data ? data->error : 0, cmd->opcode, cmd->retries); } } else cmd->error = 0; at91_mci_process_next(host); }
/* * Wait for a command to complete */ static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) { unsigned int ier; ier = at91_mci_send_command(host, cmd); pr_debug("setting ier to %08X\n", ier); /* Stop on errors or the required value */ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); }
/* * Handle a command that has been completed */ static void at91mci_completed_command(struct at91mci_host *host) { struct mmc_command *cmd = host->cmd; unsigned int status; at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); if (host->buffer) { dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address); host->buffer = NULL; } status = at91_mci_read(host, AT91_MCI_SR); pr_debug("Status = %08X [%08X %08X %08X %08X]\n", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { if ((status & AT91_MCI_RCRCE) && ((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) { cmd->error = MMC_ERR_NONE; } else { if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) cmd->error = MMC_ERR_TIMEOUT; else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) cmd->error = MMC_ERR_BADCRC; else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE)) cmd->error = MMC_ERR_FIFO; else cmd->error = MMC_ERR_FAILED; pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", cmd->error, cmd->opcode, cmd->retries); } } else cmd->error = MMC_ERR_NONE; at91mci_process_next(host); }
/* * Handle a command that has been completed */ static void at91_mci_completed_command(struct at91mci_host *host) { struct mmc_command *cmd = host->cmd; unsigned int status; at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); if (host->buffer) { dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address); host->buffer = NULL; } status = at91_mci_read(host, AT91_MCI_SR); pr_debug("Status = %08X [%08X %08X %08X %08X]\n", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); if (status & AT91_MCI_ERRORS) { if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) { cmd->error = 0; } else { if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) cmd->error = -ETIMEDOUT; else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) cmd->error = -EILSEQ; else cmd->error = -EIO; pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", cmd->error, cmd->opcode, cmd->retries); } } else cmd->error = 0; at91_mci_process_next(host); }
static irqreturn_t at91_mmc_det_irq(int irq, void *_host) { struct at91mci_host *host = _host; int present; present = !gpio_get_value(host->board->det_pin); if (present != host->present) { host->present = present; pr_debug("%s: card %s\n", mmc_hostname(host->mmc), present ? "insert" : "remove"); if (!present) { pr_debug("****** Resetting SD-card bus width ******\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } mmc_detect_change(host->mmc, msecs_to_jiffies(500)); } return IRQ_HANDLED; }
static irqreturn_t at91_mmc_det_irq(int irq, void *_host) { struct at91mci_host *host = _host; int present = !at91_get_gpio_value(irq); /* * we expect this irq on both insert and remove, * and use a short delay to debounce. */ if (present != host->present) { host->present = present; pr_debug("%s: card %s\n", mmc_hostname(host->mmc), present ? "insert" : "remove"); if (!present) { pr_debug("****** Resetting SD-card bus width ******\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } mmc_detect_change(host->mmc, msecs_to_jiffies(100)); } return IRQ_HANDLED; }
/* * Handle an interrupt */ static irqreturn_t at91_mci_irq(int irq, void *devid) { struct at91mci_host *host = devid; int completed = 0; unsigned int int_status, int_mask; int_status = at91_mci_read(host, AT91_MCI_SR); int_mask = at91_mci_read(host, AT91_MCI_IMR); pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, int_status & int_mask); int_status = int_status & int_mask; if (int_status & AT91_MCI_ERRORS) { completed = 1; if (int_status & AT91_MCI_UNRE) pr_debug("MMC: Underrun error\n"); if (int_status & AT91_MCI_OVRE) pr_debug("MMC: Overrun error\n"); if (int_status & AT91_MCI_DTOE) pr_debug("MMC: Data timeout\n"); if (int_status & AT91_MCI_DCRCE) pr_debug("MMC: CRC error in data\n"); if (int_status & AT91_MCI_RTOE) pr_debug("MMC: Response timeout\n"); if (int_status & AT91_MCI_RENDE) pr_debug("MMC: Response end bit error\n"); if (int_status & AT91_MCI_RCRCE) pr_debug("MMC: Response CRC error\n"); if (int_status & AT91_MCI_RDIRE) pr_debug("MMC: Response direction error\n"); if (int_status & AT91_MCI_RINDE) pr_debug("MMC: Response index error\n"); } else { /* Only continue processing if no errors */ if (int_status & AT91_MCI_TXBUFE) { pr_debug("TX buffer empty\n"); at91_mci_handle_transmitted(host); } if (int_status & AT91_MCI_ENDRX) { pr_debug("ENDRX\n"); at91_mci_post_dma_read(host); } if (int_status & AT91_MCI_RXBUFF) { pr_debug("RX buffer full\n"); at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX); completed = 1; } if (int_status & AT91_MCI_ENDTX) pr_debug("Transmit has ended\n"); if (int_status & AT91_MCI_NOTBUSY) { pr_debug("Card is ready\n"); completed = 1; } if (int_status & AT91_MCI_DTIP) pr_debug("Data transfer in progress\n"); if (int_status & AT91_MCI_BLKE) { pr_debug("Block transfer has ended\n"); completed = 1; } if (int_status & AT91_MCI_TXRDY) pr_debug("Ready to transmit\n"); if (int_status & AT91_MCI_RXRDY) pr_debug("Ready to receive\n"); if (int_status & AT91_MCI_CMDRDY) { pr_debug("Command ready\n"); completed = at91_mci_handle_cmdrdy(host); } } if (completed) { pr_debug("Completed command\n"); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91_mci_completed_command(host); } else at91_mci_write(host, AT91_MCI_IDR, int_status); return IRQ_HANDLED; }
/* * Send a command */ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) { unsigned int cmdr, mr; unsigned int block_length; struct mmc_data *data = cmd->data; unsigned int blocks; unsigned int ier = 0; host->cmd = cmd; /* Needed for leaving busy state before CMD1 */ if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { pr_debug("Clearing timeout\n"); at91_mci_write(host, AT91_MCI_ARGR, 0); at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { /* spin */ pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); } } cmdr = cmd->opcode; if (mmc_resp_type(cmd) == MMC_RSP_NONE) cmdr |= AT91_MCI_RSPTYP_NONE; else { /* if a response is expected then allow maximum response latancy */ cmdr |= AT91_MCI_MAXLAT; /* set 136 bit response for R2, 48 bit response otherwise */ if (mmc_resp_type(cmd) == MMC_RSP_R2) cmdr |= AT91_MCI_RSPTYP_136; else cmdr |= AT91_MCI_RSPTYP_48; } if (data) { if ( data->blksz & 0x3 ) { pr_debug("Unsupported block size\n"); cmd->error = -EINVAL; mmc_request_done(host->mmc, host->request); return; } block_length = data->blksz; blocks = data->blocks; /* always set data start - also set direction flag for read */ if (data->flags & MMC_DATA_READ) cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); else if (data->flags & MMC_DATA_WRITE) cmdr |= AT91_MCI_TRCMD_START; if (data->flags & MMC_DATA_STREAM) cmdr |= AT91_MCI_TRTYP_STREAM; if (data->blocks > 1) cmdr |= AT91_MCI_TRTYP_MULTIPLE; } else { block_length = 0; blocks = 0; } if (host->flags & FL_SENT_STOP) cmdr |= AT91_MCI_TRCMD_STOP; if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) cmdr |= AT91_MCI_OPDCMD; /* * Set the arguments and send the command */ pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); if (!data) { at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); at91_mci_write(host, ATMEL_PDC_RPR, 0); at91_mci_write(host, ATMEL_PDC_RCR, 0); at91_mci_write(host, ATMEL_PDC_RNPR, 0); at91_mci_write(host, ATMEL_PDC_RNCR, 0); at91_mci_write(host, ATMEL_PDC_TPR, 0); at91_mci_write(host, ATMEL_PDC_TCR, 0); at91_mci_write(host, ATMEL_PDC_TNPR, 0); at91_mci_write(host, ATMEL_PDC_TNCR, 0); ier = AT91_MCI_CMDRDY; } else { /* zero block length and PDC mode */ mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); /* * Disable the PDC controller */ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); if (cmdr & AT91_MCI_TRCMD_START) { data->bytes_xfered = 0; host->transfer_index = 0; host->in_use_index = 0; if (cmdr & AT91_MCI_TRDIR) { /* * Handle a read */ host->buffer = NULL; host->total_length = 0; at91_mci_pre_dma_read(host); ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; } else { /* * Handle a write */ host->total_length = block_length * blocks; host->buffer = dma_alloc_coherent(NULL, host->total_length, &host->physical_address, GFP_KERNEL); at91_mci_sg_to_dma(host, data); pr_debug("Transmitting %d bytes\n", host->total_length); at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); ier = AT91_MCI_CMDRDY; } } } /* * Send the command and then enable the PDC - not the other way round as * the data sheet says */ at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); at91_mci_write(host, AT91_MCI_CMDR, cmdr); if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); } /* Enable selected interrupts */ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); }
/* * Disable the controller */ static void at91_mci_disable(struct at91mci_host *host) { at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); }
static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) { unsigned int cmdr, mr; unsigned int block_length; struct mmc_data *data = cmd->data; unsigned int blocks; unsigned int ier = 0; host->cmd = cmd; if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { pr_debug("Clearing timeout\n"); at91_mci_write(host, AT91_MCI_ARGR, 0); at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); } } cmdr = cmd->opcode; if (mmc_resp_type(cmd) == MMC_RSP_NONE) cmdr |= AT91_MCI_RSPTYP_NONE; else { cmdr |= AT91_MCI_MAXLAT; if (mmc_resp_type(cmd) == MMC_RSP_R2) cmdr |= AT91_MCI_RSPTYP_136; else cmdr |= AT91_MCI_RSPTYP_48; } if (data) { if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) { if (data->blksz & 0x3) { pr_debug("Unsupported block size\n"); cmd->error = -EINVAL; mmc_request_done(host->mmc, host->request); return; } if (data->flags & MMC_DATA_STREAM) { pr_debug("Stream commands not supported\n"); cmd->error = -EINVAL; mmc_request_done(host->mmc, host->request); return; } } block_length = data->blksz; blocks = data->blocks; if (data->flags & MMC_DATA_READ) cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); else if (data->flags & MMC_DATA_WRITE) cmdr |= AT91_MCI_TRCMD_START; if (cmd->opcode == SD_IO_RW_EXTENDED) { cmdr |= AT91_MCI_TRTYP_SDIO_BLOCK; } else { if (data->flags & MMC_DATA_STREAM) cmdr |= AT91_MCI_TRTYP_STREAM; if (data->blocks > 1) cmdr |= AT91_MCI_TRTYP_MULTIPLE; } } else { block_length = 0; blocks = 0; } if (host->flags & FL_SENT_STOP) cmdr |= AT91_MCI_TRCMD_STOP; if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) cmdr |= AT91_MCI_OPDCMD; pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); if (!data) { at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); at91_mci_write(host, ATMEL_PDC_RPR, 0); at91_mci_write(host, ATMEL_PDC_RCR, 0); at91_mci_write(host, ATMEL_PDC_RNPR, 0); at91_mci_write(host, ATMEL_PDC_RNCR, 0); at91_mci_write(host, ATMEL_PDC_TPR, 0); at91_mci_write(host, ATMEL_PDC_TCR, 0); at91_mci_write(host, ATMEL_PDC_TNPR, 0); at91_mci_write(host, ATMEL_PDC_TNCR, 0); ier = AT91_MCI_CMDRDY; } else { mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff; mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0; mr |= (block_length << 16); mr |= AT91_MCI_PDCMODE; at91_mci_write(host, AT91_MCI_MR, mr); if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261())) at91_mci_write(host, AT91_MCI_BLKR, AT91_MCI_BLKR_BCNT(blocks) | AT91_MCI_BLKR_BLKLEN(block_length)); at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); if (cmdr & AT91_MCI_TRCMD_START) { data->bytes_xfered = 0; host->transfer_index = 0; host->in_use_index = 0; if (cmdr & AT91_MCI_TRDIR) { host->total_length = 0; at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address); at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? (blocks * block_length) : (blocks * block_length) / 4); at91_mci_write(host, ATMEL_PDC_RNPR, 0); at91_mci_write(host, ATMEL_PDC_RNCR, 0); ier = AT91_MCI_ENDRX ; } else { host->total_length = block_length * blocks; if (at91mci_is_mci1rev2xx()) if (host->total_length < 12) host->total_length = 12; at91_mci_sg_to_dma(host, data); pr_debug("Transmitting %d bytes\n", host->total_length); at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ? host->total_length : host->total_length / 4); ier = AT91_MCI_CMDRDY; } } } at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); at91_mci_write(host, AT91_MCI_CMDR, cmdr); if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); } at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); }
/* * Prepare a dma read */ static void at91_mci_pre_dma_read(struct at91mci_host *host) { int i; struct scatterlist *sg; struct mmc_command *cmd; struct mmc_data *data; pr_debug("pre dma read\n"); cmd = host->cmd; if (!cmd) { pr_debug("no command\n"); return; } data = cmd->data; if (!data) { pr_debug("no data\n"); return; } for (i = 0; i < 2; i++) { /* nothing left to transfer */ if (host->transfer_index >= data->sg_len) { pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index); break; } /* Check to see if this needs filling */ if (i == 0) { if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) { pr_debug("Transfer active in current\n"); continue; } } else { if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) { pr_debug("Transfer active in next\n"); continue; } } /* Setup the next transfer */ pr_debug("Using transfer index %d\n", host->transfer_index); sg = &data->sg[host->transfer_index++]; pr_debug("sg = %p\n", sg); sg->dma_address = dma_map_page(NULL, sg_page(sg), sg->offset, sg->length, DMA_FROM_DEVICE); pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); if (i == 0) { at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4); } else { at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4); } } pr_debug("pre dma read done\n"); }