/* * File : at91_mci.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006, RT-Thread Development Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Change Logs: * Date Author Notes * 2011-07-25 weety first version */ #include #include #include #include #include "at91_mci.h" #define USE_SLOT_B //#define RT_MCI_DBG #ifdef RT_MCI_DBG #define mci_dbg(fmt, ...) rt_kprintf(fmt, ##__VA_ARGS__) #else #define mci_dbg(fmt, ...) #endif #define MMU_NOCACHE_ADDR(a) ((rt_uint32_t)a | (1UL<<31)) extern void mmu_clean_dcache(rt_uint32_t buffer, rt_uint32_t size); extern void mmu_invalidate_dcache(rt_uint32_t buffer, rt_uint32_t size); #define AT91C_MCI_ERRORS (AT91C_MCI_RINDE | AT91C_MCI_RDIRE | AT91C_MCI_RCRCE \ | AT91C_MCI_RENDE | AT91C_MCI_RTOE | AT91C_MCI_DCRCE \ | AT91C_MCI_DTOE | AT91C_MCI_OVRE | AT91C_MCI_UNRE) #define at91_mci_read(reg) readl(AT91C_BASE_MCI + (reg)) #define at91_mci_write(reg, val) writel((val), AT91C_BASE_MCI + (reg)) #define REQ_ST_INIT (1U << 0) #define REQ_ST_CMD (1U << 1) #define REQ_ST_STOP (1U << 2) struct at91_mci { struct rt_mmcsd_host *host; struct rt_mmcsd_req *req; struct rt_mmcsd_cmd *cmd; struct rt_timer timer; //struct rt_semaphore sem_ack; rt_uint32_t *buf; rt_uint32_t current_status; }; /* * Reset the controller and restore most of the state */ static void at91_reset_host() { rt_uint32_t mr; rt_uint32_t sdcr; rt_uint32_t dtor; rt_uint32_t imr; rt_uint32_t level; level = rt_hw_interrupt_disable(); imr = at91_mci_read(AT91C_MCI_IMR); at91_mci_write(AT91C_MCI_IDR, 0xffffffff); /* save current state */ mr = at91_mci_read(AT91C_MCI_MR) & 0x7fff; sdcr = at91_mci_read(AT91C_MCI_SDCR); dtor = at91_mci_read(AT91C_MCI_DTOR); /* reset the controller */ at91_mci_write(AT91C_MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_SWRST); /* restore state */ at91_mci_write(AT91C_MCI_CR, AT91C_MCI_MCIEN); at91_mci_write(AT91C_MCI_MR, mr); at91_mci_write(AT91C_MCI_SDCR, sdcr); at91_mci_write(AT91C_MCI_DTOR, dtor); at91_mci_write(AT91C_MCI_IER, imr); /* make sure sdio interrupts will fire */ at91_mci_read(AT91C_MCI_SR); rt_hw_interrupt_enable(level); } /* * Enable the controller */ static void at91_mci_enable() { rt_uint32_t mr; at91_mci_write(AT91C_MCI_CR, AT91C_MCI_MCIEN); at91_mci_write(AT91C_MCI_IDR, 0xffffffff); at91_mci_write(AT91C_MCI_DTOR, AT91C_MCI_DTOMUL_1M | AT91C_MCI_DTOCYC); mr = AT91C_MCI_PDCMODE | 0x34a; mr |= AT91C_MCI_RDPROOF | AT91C_MCI_WRPROOF; at91_mci_write(AT91C_MCI_MR, mr); /* use Slot A or B (only one at same time) */ at91_mci_write(AT91C_MCI_SDCR, 1); /* use slot b */ } /* * Disable the controller */ static void at91_mci_disable() { at91_mci_write(AT91C_MCI_CR, AT91C_MCI_MCIDIS | AT91C_MCI_SWRST); } static void at91_timeout_timer(void *data) { struct at91_mci *mci; mci = (struct at91_mci *)data; if (mci->req) { rt_kprintf("Timeout waiting end of packet\n"); if (mci->current_status == REQ_ST_CMD) { if (mci->req->cmd && mci->req->data) { mci->req->data->err = -RT_ETIMEOUT; } else { if (mci->req->cmd) mci->req->cmd->err = -RT_ETIMEOUT; } } else if (mci->current_status == REQ_ST_STOP) { mci->req->stop->err = -RT_ETIMEOUT; } at91_reset_host(); mmcsd_req_complete(mci->host); } } /* * Prepare a dma read */ static void at91_mci_init_dma_read(struct at91_mci *mci) { rt_uint8_t i; struct rt_mmcsd_cmd *cmd; struct rt_mmcsd_data *data; rt_uint32_t length; mci_dbg("pre dma read\n"); cmd = mci->cmd; if (!cmd) { mci_dbg("no command\n"); return; } data = cmd->data; if (!data) { mci_dbg("no data\n"); return; } for (i = 0; i < 1; i++) { /* Check to see if this needs filling */ if (i == 0) { if (at91_mci_read(AT91C_PDC_RCR) != 0) { mci_dbg("Transfer active in current\n"); continue; } } else { if (at91_mci_read(AT91C_PDC_RNCR) != 0) { mci_dbg("Transfer active in next\n"); continue; } } length = data->blksize * data->blks; mci_dbg("dma address = %08X, length = %d\n", data->buf, length); if (i == 0) { at91_mci_write(AT91C_PDC_RPR, (rt_uint32_t)(data->buf)); at91_mci_write(AT91C_PDC_RCR, (data->blksize & 0x3) ? length : length / 4); } else { at91_mci_write(AT91C_PDC_RNPR, (rt_uint32_t)(data->buf)); at91_mci_write(AT91C_PDC_RNCR, (data->blksize & 0x3) ? length : length / 4); } } mci_dbg("pre dma read done\n"); } /* * Send a command */ static void at91_mci_send_command(struct at91_mci *mci, struct rt_mmcsd_cmd *cmd) { rt_uint32_t cmdr, mr; rt_uint32_t block_length; struct rt_mmcsd_data *data = cmd->data; struct rt_mmcsd_host *host = mci->host; rt_uint32_t blocks; rt_uint32_t ier = 0; rt_uint32_t length; mci->cmd = cmd; /* Needed for leaving busy state before CMD1 */ if ((at91_mci_read(AT91C_MCI_SR) & AT91C_MCI_RTOE) && (cmd->cmd_code == 1)) { mci_dbg("Clearing timeout\n"); at91_mci_write(AT91C_MCI_ARGR, 0); at91_mci_write(AT91C_MCI_CMDR, AT91C_MCI_OPDCMD); while (!(at91_mci_read(AT91C_MCI_SR) & AT91C_MCI_CMDRDY)) { /* spin */ mci_dbg("Clearing: SR = %08X\n", at91_mci_read(AT91C_MCI_SR)); } } cmdr = cmd->cmd_code; if (resp_type(cmd) == RESP_NONE) cmdr |= AT91C_MCI_RSPTYP_NONE; else { /* if a response is expected then allow maximum response latancy */ cmdr |= AT91C_MCI_MAXLAT; /* set 136 bit response for R2, 48 bit response otherwise */ if (resp_type(cmd) == RESP_R2) cmdr |= AT91C_MCI_RSPTYP_136; else cmdr |= AT91C_MCI_RSPTYP_48; } if (data) { block_length = data->blksize; blocks = data->blks; /* always set data start - also set direction flag for read */ if (data->flags & DATA_DIR_READ) cmdr |= (AT91C_MCI_TRDIR | AT91C_MCI_TRCMD_START); else if (data->flags & DATA_DIR_WRITE) cmdr |= AT91C_MCI_TRCMD_START; if (data->flags & DATA_STREAM) cmdr |= AT91C_MCI_TRTYP_STREAM; if (data->blks > 1) cmdr |= AT91C_MCI_TRTYP_MULTIPLE; } else { block_length = 0; blocks = 0; } /*if (cmd->cmd_code == GO_IDLE_STATE) { cmdr |= AT91C_MCI_SPCMD_INIT; }*/ if (cmd->cmd_code == STOP_TRANSMISSION) cmdr |= AT91C_MCI_TRCMD_STOP; if (host->io_cfg.bus_mode == MMCSD_BUSMODE_OPENDRAIN) cmdr |= AT91C_MCI_OPDCMD; /* * Set the arguments and send the command */ mci_dbg("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", cmd->cmd_code, cmdr, cmd->arg, blocks, block_length, at91_mci_read(AT91C_MCI_MR)); if (!data) { at91_mci_write(AT91C_PDC_PTCR, AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS); at91_mci_write(AT91C_PDC_RPR, 0); at91_mci_write(AT91C_PDC_RCR, 0); at91_mci_write(AT91C_PDC_RNPR, 0); at91_mci_write(AT91C_PDC_RNCR, 0); at91_mci_write(AT91C_PDC_TPR, 0); at91_mci_write(AT91C_PDC_TCR, 0); at91_mci_write(AT91C_PDC_TNPR, 0); at91_mci_write(AT91C_PDC_TNCR, 0); ier = AT91C_MCI_CMDRDY; } else { /* zero block length and PDC mode */ mr = at91_mci_read(AT91C_MCI_MR) & 0x5fff; mr |= (data->blksize & 0x3) ? AT91C_MCI_PDCFBYTE : 0; mr |= (block_length << 16); mr |= AT91C_MCI_PDCMODE; at91_mci_write(AT91C_MCI_MR, mr); at91_mci_write(AT91C_MCI_BLKR, AT91C_MCI_BLKR_BCNT(blocks) | AT91C_MCI_BLKR_BLKLEN(block_length)); /* * Disable the PDC controller */ at91_mci_write(AT91C_PDC_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); if (cmdr & AT91C_MCI_TRCMD_START) { if (cmdr & AT91C_MCI_TRDIR) { /* * Handle a read */ mmu_invalidate_dcache((rt_uint32_t)data->buf, data->blksize*data->blks); at91_mci_init_dma_read(mci); ier = AT91C_MCI_ENDRX /* | AT91C_MCI_RXBUFF */; } else { /* * Handle a write */ length = block_length * blocks; /* * at91mci MCI1 rev2xx Data Write Operation and * number of bytes erratum */ if (length < 12) { length = 12; mci->buf = rt_malloc(length); if (!mci->buf) { rt_kprintf("rt alloc tx buffer failed\n"); cmd->err = -RT_ENOMEM; mmcsd_req_complete(mci->host); return; } rt_memset(mci->buf, 0, 12); rt_memcpy(mci->buf, data->buf, length); mmu_clean_dcache((rt_uint32_t)mci->buf, length); at91_mci_write(AT91C_PDC_TPR, (rt_uint32_t)(mci->buf)); at91_mci_write(AT91C_PDC_TCR, (data->blksize & 0x3) ? length : length / 4); } else { mmu_clean_dcache((rt_uint32_t)data->buf, data->blksize*data->blks); at91_mci_write(AT91C_PDC_TPR, (rt_uint32_t)(data->buf)); at91_mci_write(AT91C_PDC_TCR, (data->blksize & 0x3) ? length : length / 4); } mci_dbg("Transmitting %d bytes\n", length); ier = AT91C_MCI_CMDRDY; } } } /* * Send the command and then enable the PDC - not the other way round as * the data sheet says */ at91_mci_write(AT91C_MCI_ARGR, cmd->arg); at91_mci_write(AT91C_MCI_CMDR, cmdr); if (cmdr & AT91C_MCI_TRCMD_START) { if (cmdr & AT91C_MCI_TRDIR) at91_mci_write(AT91C_PDC_PTCR, AT91C_PDC_RXTEN); } /* Enable selected interrupts */ at91_mci_write(AT91C_MCI_IER, AT91C_MCI_ERRORS | ier); } /* * Process the next step in the request */ static void at91_mci_process_next(struct at91_mci *mci) { if (mci->current_status == REQ_ST_INIT) { mci->current_status = REQ_ST_CMD; at91_mci_send_command(mci, mci->req->cmd); } else if ((mci->current_status == REQ_ST_CMD) && mci->req->stop) { mci->current_status = REQ_ST_STOP; at91_mci_send_command(mci, mci->req->stop); } else { rt_timer_stop(&mci->timer); /* the mci controller hangs after some transfers, * and the workaround is to reset it after each transfer. */ at91_reset_host(); mmcsd_req_complete(mci->host); } } /* * Handle an MMC request */ static void at91_mci_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) { rt_uint32_t timeout = RT_TICK_PER_SECOND; struct at91_mci *mci = host->private_data; mci->req = req; mci->current_status = REQ_ST_INIT; rt_timer_control(&mci->timer, RT_TIMER_CTRL_SET_TIME, (void*)&timeout); rt_timer_start(&mci->timer); at91_mci_process_next(mci); } /* * Handle transmitted data */ static void at91_mci_handle_transmitted(struct at91_mci *mci) { struct rt_mmcsd_cmd *cmd; struct rt_mmcsd_data *data; mci_dbg("Handling the transmit\n"); /* Disable the transfer */ at91_mci_write(AT91C_PDC_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); /* Now wait for cmd ready */ at91_mci_write(AT91C_MCI_IDR, AT91C_MCI_TXBUFE); cmd = mci->cmd; if (!cmd) return; data = cmd->data; if (!data) return; if (data->blks > 1) { mci_dbg("multiple write : wait for BLKE...\n"); at91_mci_write(AT91C_MCI_IER, AT91C_MCI_BLKE); } else at91_mci_write(AT91C_MCI_IER, AT91C_MCI_NOTBUSY); } /* * Handle after a dma read */ static void at91_mci_post_dma_read(struct at91_mci *mci) { struct rt_mmcsd_cmd *cmd; struct rt_mmcsd_data *data; mci_dbg("post dma read\n"); cmd = mci->cmd; if (!cmd) { mci_dbg("no command\n"); return; } data = cmd->data; if (!data) { mci_dbg("no data\n"); return; } at91_mci_write(AT91C_MCI_IDR, AT91C_MCI_ENDRX); at91_mci_write(AT91C_MCI_IER, AT91C_MCI_RXBUFF); mci_dbg("post dma read done\n"); } /*Handle after command sent ready*/ static int at91_mci_handle_cmdrdy(struct at91_mci *mci) { if (!mci->cmd) return 1; else if (!mci->cmd->data) { if (mci->current_status == REQ_ST_STOP) { /*After multi block write, we must wait for NOTBUSY*/ at91_mci_write(AT91C_MCI_IER, AT91C_MCI_NOTBUSY); } else return 1; } else if (mci->cmd->data->flags & DATA_DIR_WRITE) { /*After sendding multi-block-write command, start DMA transfer*/ at91_mci_write(AT91C_MCI_IER, AT91C_MCI_TXBUFE | AT91C_MCI_BLKE); at91_mci_write(AT91C_PDC_PTCR, AT91C_PDC_TXTEN); } /* command not completed, have to wait */ return 0; } /* * Handle a command that has been completed */ static void at91_mci_completed_command(struct at91_mci *mci, rt_uint32_t status) { struct rt_mmcsd_cmd *cmd = mci->cmd; struct rt_mmcsd_data *data = cmd->data; at91_mci_write(AT91C_MCI_IDR, 0xffffffff & ~(AT91C_MCI_SDIOIRQA | AT91C_MCI_SDIOIRQB)); cmd->resp[0] = at91_mci_read(AT91C_MCI_RSPR(0)); cmd->resp[1] = at91_mci_read(AT91C_MCI_RSPR(1)); cmd->resp[2] = at91_mci_read(AT91C_MCI_RSPR(2)); cmd->resp[3] = at91_mci_read(AT91C_MCI_RSPR(3)); if (mci->buf) { //rt_memcpy(data->buf, mci->buf, data->blksize*data->blks); rt_free(mci->buf); mci->buf = RT_NULL; } mci_dbg("Status = %08X/%08x [%08X %08X %08X %08X]\n", status, at91_mci_read(AT91C_MCI_SR), cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); if (status & AT91C_MCI_ERRORS) { if ((status & AT91C_MCI_RCRCE) && (resp_type(cmd) & (RESP_R3|RESP_R4))) { cmd->err = 0; } else { if (status & (AT91C_MCI_DTOE | AT91C_MCI_DCRCE)) { if (data) { if (status & AT91C_MCI_DTOE) data->err = -RT_ETIMEOUT; else if (status & AT91C_MCI_DCRCE) data->err = -RT_ERROR; } } else { if (status & AT91C_MCI_RTOE) cmd->err = -RT_ETIMEOUT; else if (status & AT91C_MCI_RCRCE) cmd->err = -RT_ERROR; else cmd->err = -RT_ERROR; } rt_kprintf("error detected and set to %d/%d (cmd = %d)\n", cmd->err, data ? data->err : 0, cmd->cmd_code); } } else cmd->err = 0; at91_mci_process_next(mci); } /* * Handle an interrupt */ static void at91_mci_irq(int irq, void *param) { struct at91_mci *mci = (struct at91_mci *)param; rt_int32_t completed = 0; rt_uint32_t int_status, int_mask; int_status = at91_mci_read(AT91C_MCI_SR); int_mask = at91_mci_read(AT91C_MCI_IMR); mci_dbg("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, int_status & int_mask); int_status = int_status & int_mask; if (int_status & AT91C_MCI_ERRORS) { completed = 1; if (int_status & AT91C_MCI_UNRE) mci_dbg("MMC: Underrun error\n"); if (int_status & AT91C_MCI_OVRE) mci_dbg("MMC: Overrun error\n"); if (int_status & AT91C_MCI_DTOE) mci_dbg("MMC: Data timeout\n"); if (int_status & AT91C_MCI_DCRCE) mci_dbg("MMC: CRC error in data\n"); if (int_status & AT91C_MCI_RTOE) mci_dbg("MMC: Response timeout\n"); if (int_status & AT91C_MCI_RENDE) mci_dbg("MMC: Response end bit error\n"); if (int_status & AT91C_MCI_RCRCE) mci_dbg("MMC: Response CRC error\n"); if (int_status & AT91C_MCI_RDIRE) mci_dbg("MMC: Response direction error\n"); if (int_status & AT91C_MCI_RINDE) mci_dbg("MMC: Response index error\n"); } else { /* Only continue processing if no errors */ if (int_status & AT91C_MCI_TXBUFE) { mci_dbg("TX buffer empty\n"); at91_mci_handle_transmitted(mci); } if (int_status & AT91C_MCI_ENDRX) { mci_dbg("ENDRX\n"); at91_mci_post_dma_read(mci); } if (int_status & AT91C_MCI_RXBUFF) { mci_dbg("RX buffer full\n"); at91_mci_write(AT91C_PDC_PTCR, AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS); at91_mci_write(AT91C_MCI_IDR, AT91C_MCI_RXBUFF | AT91C_MCI_ENDRX); completed = 1; } if (int_status & AT91C_MCI_ENDTX) mci_dbg("Transmit has ended\n"); if (int_status & AT91C_MCI_NOTBUSY) { mci_dbg("Card is ready\n"); //at91_mci_update_bytes_xfered(host); completed = 1; } if (int_status & AT91C_MCI_DTIP) mci_dbg("Data transfer in progress\n"); if (int_status & AT91C_MCI_BLKE) { mci_dbg("Block transfer has ended\n"); if (mci->req->data && mci->req->data->blks > 1) { /* multi block write : complete multi write * command and send stop */ completed = 1; } else { at91_mci_write(AT91C_MCI_IER, AT91C_MCI_NOTBUSY); } } /*if (int_status & AT91C_MCI_SDIOIRQA) rt_mmcsd_signal_sdio_irq(host->mmc);*/ if (int_status & AT91C_MCI_SDIOIRQB) sdio_irq_wakeup(mci->host); if (int_status & AT91C_MCI_TXRDY) mci_dbg("Ready to transmit\n"); if (int_status & AT91C_MCI_RXRDY) mci_dbg("Ready to receive\n"); if (int_status & AT91C_MCI_CMDRDY) { mci_dbg("Command ready\n"); completed = at91_mci_handle_cmdrdy(mci); } } if (completed) { mci_dbg("Completed command\n"); at91_mci_write(AT91C_MCI_IDR, 0xffffffff & ~(AT91C_MCI_SDIOIRQA | AT91C_MCI_SDIOIRQB)); at91_mci_completed_command(mci, int_status); } else at91_mci_write(AT91C_MCI_IDR, int_status & ~(AT91C_MCI_SDIOIRQA | AT91C_MCI_SDIOIRQB)); } /* * Set the IOCFG */ static void at91_mci_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg) { rt_uint32_t clkdiv; //struct at91_mci *mci = host->private_data; rt_uint32_t at91_master_clock = clk_get_rate(clk_get("mck")); if (io_cfg->clock == 0) { /* Disable the MCI controller */ at91_mci_write(AT91C_MCI_CR, AT91C_MCI_MCIDIS); clkdiv = 0; } else { /* Enable the MCI controller */ at91_mci_write(AT91C_MCI_CR, AT91C_MCI_MCIEN); if ((at91_master_clock % (io_cfg->clock * 2)) == 0) clkdiv = ((at91_master_clock / io_cfg->clock) / 2) - 1; else clkdiv = (at91_master_clock / io_cfg->clock) / 2; mci_dbg("clkdiv = %d. mcck = %ld\n", clkdiv, at91_master_clock / (2 * (clkdiv + 1))); } if (io_cfg->bus_width == MMCSD_BUS_WIDTH_4) { mci_dbg("MMC: Setting controller bus width to 4\n"); at91_mci_write(AT91C_MCI_SDCR, at91_mci_read(AT91C_MCI_SDCR) | AT91C_MCI_SDCBUS); } else { mci_dbg("MMC: Setting controller bus width to 1\n"); at91_mci_write(AT91C_MCI_SDCR, at91_mci_read(AT91C_MCI_SDCR) & ~AT91C_MCI_SDCBUS); } /* Set the clock divider */ at91_mci_write(AT91C_MCI_MR, (at91_mci_read(AT91C_MCI_MR) & ~AT91C_MCI_CLKDIV) | clkdiv); /* maybe switch power to the card */ switch (io_cfg->power_mode) { case MMCSD_POWER_OFF: break; case MMCSD_POWER_UP: break; case MMCSD_POWER_ON: /*at91_mci_write(AT91C_MCI_ARGR, 0); at91_mci_write(AT91C_MCI_CMDR, 0|AT91C_MCI_SPCMD_INIT|AT91C_MCI_OPDCMD); mci_dbg("MCI_SR=0x%08x\n", at91_mci_read(AT91C_MCI_SR)); while (!(at91_mci_read(AT91C_MCI_SR) & AT91C_MCI_CMDRDY)) { } mci_dbg("at91 mci power on\n");*/ break; default: rt_kprintf("unknown power_mode %d\n", io_cfg->power_mode); break; } } static void at91_mci_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t enable) { at91_mci_write(enable ? AT91C_MCI_IER : AT91C_MCI_IDR, AT91C_MCI_SDIOIRQB); } static const struct rt_mmcsd_host_ops ops = { at91_mci_request, at91_mci_set_iocfg, RT_NULL, at91_mci_enable_sdio_irq, }; void at91_mci_detect(int irq, void *param) { rt_kprintf("mmcsd gpio detected\n"); } static void mci_gpio_init() { #ifdef USE_SLOT_B AT91C_BASE_PIOA->PIO_PUER = (1 << 0)|(1 << 1)|(1 << 3)|(1 << 4)|(1 << 5); AT91C_BASE_PIOA->PIO_PUDR = (1 << 8); AT91C_BASE_PIOA->PIO_BSR = (1 << 0)|(1 << 1)|(1 << 3)|(1 << 4)|(1 << 5); AT91C_BASE_PIOA->PIO_ASR = (1 << 8); AT91C_BASE_PIOA->PIO_PDR = (1 << 0)|(1 << 1)|(1 << 3)|(1 << 4)|(1 << 5)|(1 << 8); AT91C_BASE_PIOA->PIO_IDR = (1 << 6)|(1 << 7); AT91C_BASE_PIOA->PIO_PUER = (1 << 6)|(1 << 7); AT91C_BASE_PIOA->PIO_ODR = (1 << 6)|(1 << 7); AT91C_BASE_PIOA->PIO_PER = (1 << 6)|(1 << 7); #else AT91C_BASE_PIOA->PIO_PUER = (1 << 6)|(1 << 7)|(1 << 9)|(1 << 10)|(1 << 11); AT91C_BASE_PIOA->PIO_ASR = (1 << 6)|(1 << 7)|(1 << 9)|(1 << 10)|(1 << 11)|(1 << 8); AT91C_BASE_PIOA->PIO_PDR = (1 << 6)|(1 << 7)|(1 << 9)|(1 << 10)|(1 << 11)|(1 << 8); #endif } int at91_mci_init(void) { struct rt_mmcsd_host *host; struct at91_mci *mci; host = mmcsd_alloc_host(); if (!host) { return -RT_ERROR; } mci = rt_malloc(sizeof(struct at91_mci)); if (!mci) { rt_kprintf("alloc mci failed\n"); goto err; } rt_memset(mci, 0, sizeof(struct at91_mci)); host->ops = &ops; host->freq_min = 375000; host->freq_max = 25000000; host->valid_ocr = VDD_32_33 | VDD_33_34; host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | \ MMCSD_SUP_HIGHSPEED | MMCSD_SUP_SDIO_IRQ; host->max_seg_size = 65535; host->max_dma_segs = 2; host->max_blk_size = 512; host->max_blk_count = 4096; mci->host = host; mci_gpio_init(); AT91C_BASE_PMC->AT91C_PMC_PCER = 1 << AT91C_ID_MCI; //enable MCI clock at91_mci_disable(); at91_mci_enable(); /* instal interrupt */ rt_hw_interrupt_install(AT91SAM9260_ID_MCI, at91_mci_irq, (void *)mci, "MMC"); rt_hw_interrupt_umask(AT91SAM9260_ID_MCI); rt_hw_interrupt_install(gpio_to_irq(AT91C_PIN_PA7), at91_mci_detect, RT_NULL, "MMC_DETECT"); rt_hw_interrupt_umask(gpio_to_irq(AT91C_PIN_PA7)); rt_timer_init(&mci->timer, "mci_timer", at91_timeout_timer, mci, RT_TICK_PER_SECOND, RT_TIMER_FLAG_PERIODIC); //rt_timer_start(&mci->timer); //rt_sem_init(&mci->sem_ack, "sd_ack", 0, RT_IPC_FLAG_FIFO); host->private_data = mci; mmcsd_change(host); return 0; err: mmcsd_free_host(host); return -RT_ENOMEM; } INIT_DEVICE_EXPORT(at91_mci_init); #include "finsh.h" FINSH_FUNCTION_EXPORT(at91_mci_init, at91sam9260 sd init); void mci_dump(void) { rt_uint32_t i; rt_kprintf("PIOA_PSR=0x%08x\n", readl(AT91C_PIOA+PIO_PSR)); rt_kprintf("PIOA_ABSR=0x%08x\n", readl(AT91C_PIOA+PIO_ABSR)); rt_kprintf("PIOA_PUSR=0x%08x\n", readl(AT91C_PIOA+PIO_PUSR)); for (i = 0; i <= 0x4c; i += 4) { rt_kprintf("0x%08x:0x%08x\n", AT91SAM9260_BASE_MCI+i, at91_mci_read(i)); } } FINSH_FUNCTION_EXPORT(mci_dump, dump register for mci);