/****************************************************************************************************//** * @file sdio.c * * @status EXPERIMENTAL * * @brief LPC18xx_43xx SD/MMC/SDIO controller driver * * @version V1.0 * @date 02. November 2011 * * Software that is described herein is for illustrative purposes only * which provides customers with programming information regarding the * products. This software is supplied "AS IS" without any warranties. * NXP Semiconductors assumes no responsibility or liability for the * use of the software, conveys no license or title under any patent, * copyright, or mask work right to the product. NXP Semiconductors * reserves the right to make changes in the software without * notification. NXP Semiconductors also make no representation or * warranty that such application will be suitable for the specified * use without further testing or modification. * Permission to use, copy, modify, and distribute this software and its * documentation is hereby granted, under NXP Semiconductors’ * relevant copyright in the software, without fee, provided that it * is used in conjunction with NXP Semiconductors microcontrollers. This * copyright, permission, and disclaimer notice must appear in all copies of * this code. *******************************************************************************************************/ #include #include "LPC43xx.h" /* LPC18xx definitions */ #include "system_LPC43xx.h" #include "lpc_sdmmc.h" #include "sdio.h" /* Global instance of the current card */ static MCI_CARD_INFO_T g_card_info; #ifdef USE_DMADESC_DBUFF /* Array of DMA descriptors used for double buffered mode. Must be enough to transfer up to 64KBytes in a single DMA session. */ static LPC_SDMMC_DMA_Type mci_dma_dd[1 + (0x10000 / (2 * MCI_DMADES1_MAXTR))]; #else /* Array of DMA descriptors used for chained mode. Must be enough to transfer up to 64KBytes in a single DMA session. */ static LPC_SDMMC_DMA_Type mci_dma_dd[1 + (0x10000 / MCI_DMADES1_MAXTR)]; #endif /* This must be defined outside the driver and is the rate of the base clock going into the SDIO IP */ uint32_t sdio_clk_rate; extern void timer_wait_us(void *t, int us); extern void timer_wait_ms(void *t, int ms); int sdio_execute_command(MCI_CARD_INFO_T* pdev, uint32_t cmd, uint32_t arg, uint32_t wait_status); /*********************************************************************** * MCI driver private functions **********************************************************************/ /***************************************************************************** ** Function name: sdio_enable_clock ** ** Descriptions: Enables the SDIO controller clock ** ** parameters: None ** ** Returned value: None ** *****************************************************************************/ static void sdio_enable_clock(void) { /* Enable SD MMC clock */ LPC_CCU1->CLK_M4_SDIO_CFG = 1; } /***************************************************************************** ** Function name: sdio_disable_clock ** ** Descriptions: Disables the SDIO controller clock ** ** parameters: None ** ** Returned value: None ** *****************************************************************************/ static void sdio_disable_clock(void) { /* Disable SD MMC clock */ LPC_CCU1->CLK_M4_SDIO_CFG = 0; } /***************************************************************************** ** Function name: sdio_disable_clock ** ** Descriptions: Return clock running status ** ** parameters: None ** ** Returned value: !0 if the clock is enabled, otherwise 0 ** *****************************************************************************/ int sdio_is_clock_enabled(void) { return (LPC_CCU1->CLK_M4_SDIO_CFG & 0x1); } /********************************************************************** ** Function name: wait_for_program_finish ** ** Description: Wait for card program to finish ** ** Parameters: pdev : None ** ** Returned value: None **********************************************************************/ static void wait_for_program_finish(void) { while (sdio_get_state() == SDMMC_PRG_ST); while (sdio_get_state() != SDMMC_TRAN_ST); } /***************************************************************************** ** Function name: sdio_dma_setup ** ** Descriptions: Setup DMA descriptors ** ** parameters: addr : Address of buffer (source or destination) ** size: size of buffer in bytes (64K max) ** ** Returned value: None ** *****************************************************************************/ void sdio_dma_setup(uint32_t addr, uint32_t size) { int i = 0; uint32_t ctrl, maxs; #ifdef USE_DMADESC_DBUFF uint32_t maxs2; #endif /* Reset DMA */ LPC_SDMMC->CTRL |= MCI_CTRL_DMA_RESET | MCI_CTRL_FIFO_RESET; while (LPC_SDMMC->CTRL & MCI_CTRL_DMA_RESET); #ifdef USE_DMADESC_DBUFF /* Build a descriptor list using the double buffered DMA method */ while (size > 0) { /* Limit size of the transfer to maximum buffer size * 2*/ maxs = size; if (maxs > (MCI_DMADES1_MAXTR * 2)) maxs = (MCI_DMADES1_MAXTR * 2); size -= maxs; /* Setup buffer sizes */ if (maxs > MCI_DMADES1_MAXTR) { maxs2 = maxs - MCI_DMADES1_MAXTR; maxs = MCI_DMADES1_MAXTR; } else maxs2 = 0; /* Set buffer sizes */ mci_dma_dd[i].des1 = MCI_DMADES1_BS1(maxs) | MCI_DMADES1_BS2(maxs2); /* Setup buffer address (double buffered) */ mci_dma_dd[i].des2 = addr + (i * (MCI_DMADES1_MAXTR * 2)); mci_dma_dd[i].des3 = mci_dma_dd[i].des2 + maxs; /* Setup basic control */ ctrl = MCI_DMADES0_OWN; // | MCI_DMADES0_DIC; if (i == 0) ctrl |= MCI_DMADES0_FS; /* First DMA buffer */ /* No more data? Then this is the last descriptor */ if (!size) ctrl |= MCI_DMADES0_LD; else ctrl |= MCI_DMADES0_DIC; mci_dma_dd[i].des0 = ctrl; i++; } mci_dma_dd[i].des0 = MCI_DMADES0_OWN | MCI_DMADES0_LD; mci_dma_dd[i].des1 = 0; mci_dma_dd[i].des2 = 0; mci_dma_dd[i].des3 = 0; #else /* Build a descriptor list using the chained DMA method */ i = 0; while (size > 0) { /* Limit size of the transfer to maximum buffer size */ maxs = size; if (maxs > MCI_DMADES1_MAXTR) maxs = MCI_DMADES1_MAXTR; size -= maxs; /* Set buffer size */ mci_dma_dd[i].des1 = MCI_DMADES1_BS1(maxs); /* Setup buffer address (chained) */ mci_dma_dd[i].des2 = addr + (i * MCI_DMADES1_MAXTR); /* Setup basic control */ ctrl = MCI_DMADES0_OWN | MCI_DMADES0_CH; if (i == 0) ctrl |= MCI_DMADES0_FS; /* First DMA buffer */ /* No more data? Then this is the last descriptor */ if (!size) ctrl |= MCI_DMADES0_LD; else ctrl |= MCI_DMADES0_DIC; /* Another descriptor is needed */ mci_dma_dd[i].des3 = (uint32_t) &mci_dma_dd[i + 1]; mci_dma_dd[i].des0 = ctrl; i++; } #endif /* Set DMA derscriptor base address */ LPC_SDMMC->DBADDR = (uint32_t) &mci_dma_dd[0]; } /***************************************************************************** ** Function name: prv_card_acquired ** ** Descriptions: Checks whether card is acquired properly or not ** ** parameters: pdev: Pointer to card info structure ** ** Returned value: !0 if card has been acquired, otherwise 0 ** *****************************************************************************/ static int prv_card_acquired(MCI_CARD_INFO_T* pdev) { return (pdev->cid[0] != 0); } /***************************************************************************** ** Function name: prv_get_bits ** ** Descriptions: Helper function to get a bit field withing multi-word ** buffer. Used to get fields with-in CSD & EXT-CSD ** structures. ** ** parameters: start: Start position of the bit field ** end : Start position of the bit field ** data : Pointer to buffer ** ** Returned value: The bit field value of the selected range ** *****************************************************************************/ static uint32_t prv_get_bits(int start, int end, uint32_t* data) { uint32_t v; uint32_t i = end >> 5; uint32_t j = start & 0x1f; if (i == (start >> 5)) v = (data[i] >> j); else v = ((data[i] << (32 - j)) | (data[start >> 5] >> j)); return (v & ((1 << (end - start + 1)) - 1)); } /***************************************************************************** ** Function name: prv_clear_all ** ** Descriptions: Clears the FIFOs, response and data, and the interrupt ** status ** ** parameters: None ** ** Returned value: None ** *****************************************************************************/ static void prv_clear_all(void) { /* reset all blocks */ LPC_SDMMC->CTRL |= MCI_CTRL_FIFO_RESET; /* wait till resets clear */ while (LPC_SDMMC->CTRL & MCI_CTRL_FIFO_RESET); /* Clear interrupt status */ LPC_SDMMC->RINTSTS = 0xFFFFFFFF; } /***************************************************************************** ** Function name: prv_send_cmd ** ** Descriptions: Function to send command to Card interface unit (CIU) ** ** parameters: cmd : Command with all flags set. ** arg : Argument for the command ** ** Returned value: TRUE on times-out, otherwise FALSE ** *****************************************************************************/ static int prv_send_cmd(uint32_t cmd, uint32_t arg) { volatile int tmo = 50; volatile int delay; /* set command arg reg*/ LPC_SDMMC->CMDARG = arg; LPC_SDMMC->CMD = MCI_CMD_START | cmd; /* poll untill command is accepted by the CIU */ while (--tmo && (LPC_SDMMC->CMD & MCI_CMD_START)) { if (tmo & 1) delay = 50; else delay = 18000; while (--delay > 1); } return (tmo < 1) ? 1 : 0; } /***************************************************************************** ** Function name: prv_set_clock ** ** Descriptions: Function to set speed of the clock going to card ** ** parameters: speed : Clock speed ** ** Returned value: TRUE on times-out, otherwise FALSE ** *****************************************************************************/ static void prv_set_clock(uint32_t speed) { /* compute SD/MMC clock dividers */ uint32_t div; div = ((sdio_clk_rate / speed) + 2) >> 1; if ((div == LPC_SDMMC->CLKDIV) && LPC_SDMMC->CLKENA) return; /* requested speed is already set */ /* disable clock */ LPC_SDMMC->CLKENA = 0; /* User divider 0 */ LPC_SDMMC->CLKSRC = MCI_CLKSRC_CLKDIV0; /* inform CIU */ prv_send_cmd(MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0); /* set divider 0 to desired value */ LPC_SDMMC->CLKDIV = MCI_CLOCK_DIVIDER(0, div); /* inform CIU */ prv_send_cmd(MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0); /* enable clock */ LPC_SDMMC->CLKENA = MCI_CLKEN_ENABLE(0); /* inform CIU */ prv_send_cmd(MCI_CMD_UPD_CLK | MCI_CMD_PRV_DAT_WAIT, 0); } /***************************************************************************** ** Function name: prv_pull_response ** ** Descriptions: Function to retrieve command response ** ** parameters: pdev: Pointer to card info structure ** length : the length of the expected response, in bits ** ** Returned value: None ** *****************************************************************************/ static void prv_pull_response(MCI_CARD_INFO_T* pdev, int length) { /* on this chip response is not a fifo so read all 4 regs */ pdev->response[0] = LPC_SDMMC->RESP0; pdev->response[1] = LPC_SDMMC->RESP1; pdev->response[2] = LPC_SDMMC->RESP2; pdev->response[3] = LPC_SDMMC->RESP3; } /***************************************************************************** ** Function name: prv_wait_for_completion ** ** Descriptions: Polls for command completion ** ** parameters: pdev : Pointer to card info structure ** bit : Status bits to poll for command completion. ** ** Returned value: 0 on success, or failure condition (-1) ** *****************************************************************************/ static uint32_t prv_wait_for_completion(MCI_CARD_INFO_T* pdev, uint32_t bits) { uint32_t status = 0; int tmo_count = US_TIMEOUT; /* also check error conditions */ bits |= MCI_INT_EBE | MCI_INT_SBE | MCI_INT_HLE | MCI_INT_RTO | MCI_INT_RCRC | MCI_INT_RESP_ERR; if (bits & MCI_INT_DATA_OVER) bits |= MCI_INT_FRUN | MCI_INT_HTO | MCI_INT_DTO | MCI_INT_DCRC; if (pdev->wait_func == 0) { /* do busy polling when wait_func is not set*/ do { timer_wait_us(0, 1); status = LPC_SDMMC->RINTSTS; if (--tmo_count < 1) { break; } } while ((status & bits) == 0); /* set time out flag for driver timeout also */ status |= ((tmo_count < 1) ? MCI_INT_RTO : 0); } else { /* call wait function set by application */ status = pdev->wait_func(pdev, bits); } return status; } /***************************************************************************** ** Function name: prv_process_csd ** ** Descriptions: Function to process the CSD & EXT-CSD of the card ** ** parameters: pdev: Pointer to card info structure ** ** Returned value: None ** *****************************************************************************/ static void prv_process_csd(MCI_CARD_INFO_T* pdev) { int status = 0; int c_size = 0; int c_size_mult = 0; int mult = 0; /* compute block length based on CSD response */ pdev->block_len = 1 << prv_get_bits(80, 83, pdev->csd); if ((pdev->card_type & CARD_TYPE_HC) && (pdev->card_type & CARD_TYPE_SD)) { /* See section 5.3.3 CSD Register (CSD Version 2.0) of SD2.0 spec an explanation for the calculation of these values */ c_size = prv_get_bits(48, 63, (uint32_t*)pdev->csd) + 1; pdev->blocknr = c_size << 10; /* 512 byte blocks */ } else { /* See section 5.3 of the 4.1 revision of the MMC specs for an explanation for the calculation of these values */ c_size = prv_get_bits(62, 73, (uint32_t*)pdev->csd); c_size_mult = prv_get_bits(47, 49, (uint32_t*)pdev->csd); //csd_c_size_mult (); mult = 1 << (c_size_mult + 2); pdev->blocknr = (c_size + 1) * mult; /* adjust blocknr to 512/block */ if (pdev->block_len > MMC_SECTOR_SIZE) pdev->blocknr = pdev->blocknr * (pdev->block_len >> 9); /* get extended CSD for newer MMC cards CSD spec >= 4.0*/ if (((pdev->card_type & CARD_TYPE_SD) == 0) && (prv_get_bits(122, 125, (uint32_t*)pdev->csd) >= 4)) { /* put card in trans state */ status = sdio_execute_command(pdev, CMD_SELECT_CARD, pdev->rca << 16, 0); /* set block size and byte count */ LPC_SDMMC->BLKSIZ = MMC_SECTOR_SIZE; LPC_SDMMC->BYTCNT = MMC_SECTOR_SIZE; /* send EXT_CSD command */ sdio_dma_setup((uint32_t) pdev->ext_csd, MMC_SECTOR_SIZE); status = sdio_execute_command(pdev, CMD_SEND_EXT_CSD, 0, 0 | MCI_INT_DATA_OVER); if ((status & MCI_INT_ERROR) == 0) { /* check EXT_CSD_VER is greater than 1.1 */ if ((pdev->ext_csd[48] & 0xFF) > 1) pdev->blocknr = pdev->ext_csd[53]; /* bytes 212:215 represent sec count */ /* switch to 52MHz clock if card type is set to 1 or else set to 26MHz */ if ((pdev->ext_csd[49] & 0xFF) == 1) { /* for type 1 MMC cards high speed is 52MHz */ pdev->speed = MMC_HIGH_BUS_MAX_CLOCK; } else { /* for type 0 MMC cards high speed is 26MHz */ pdev->speed = MMC_LOW_BUS_MAX_CLOCK; } } } } pdev->device_size = pdev->blocknr << 9; /* blocknr * 512 */ } /***************************************************************************** ** Function name: prv_set_trans_state ** ** Descriptions: Puts current selected card in trans state ** ** parameters: pdev: Pointer to card info structure ** ** Returned value: 0 on success, or error code (-1) ** *****************************************************************************/ static int prv_set_trans_state(MCI_CARD_INFO_T* pdev) { uint32_t status; /* get current state of the card */ status = sdio_execute_command(pdev, CMD_SEND_STATUS, pdev->rca << 16, 0); if (status & MCI_INT_RTO) { /* unable to get the card state. So return immediatly. */ return -1; } /* check card state in response */ status = R1_CURRENT_STATE(pdev->response[0]); switch (status) { case SDMMC_STBY_ST: /* put card in 'Trans' state */ status = sdio_execute_command(pdev, CMD_SELECT_CARD, pdev->rca << 16, 0); if (status != 0) { /* unable to put the card in Trans state. So return immediatly. */ return -1; } break; case SDMMC_TRAN_ST: /*do nothing */ break; default: /* card shouldn't be in other states so return */ return -1; } #if SDIO_BUS_WIDTH > 1 if (pdev->card_type & CARD_TYPE_SD) { sdio_execute_command(pdev, CMD_SD_SET_WIDTH, 2, 0); /* SD, 4 bit width */ /* if positive response */ LPC_SDMMC->CTYPE = MCI_CTYPE_4BIT(0); } #if SDIO_BUS_WIDTH > 4 #error 8-bit mode not supported yet! #endif #endif /* set block length */ LPC_SDMMC->BLKSIZ = MMC_SECTOR_SIZE; status = sdio_execute_command(pdev, CMD_SET_BLOCKLEN, MMC_SECTOR_SIZE, 0); return 0; } /*********************************************************************** * MCI driver public functions **********************************************************************/ /***************************************************************************** ** Function name: sdio_execute_command ** ** Descriptions: Function to execute a command ** ** parameters: pdev: Pointer to card info structure ** cmd : Command with all flags set. ** arg : Argument for the command ** wait_status : Status bits to poll for command completion. ** ** Returned value: 0 on success, or error code (-1) ** *****************************************************************************/ int sdio_execute_command(MCI_CARD_INFO_T* pdev, uint32_t cmd, uint32_t arg, uint32_t wait_status) { /* if APP command there are 2 stages */ int step = (cmd & CMD_BIT_APP) ? 2 : 1; int status = 0; uint32_t cmd_reg = 0; if (!wait_status) wait_status = (cmd & CMD_MASK_RESP) ? MCI_INT_CMD_DONE : MCI_INT_DATA_OVER; /* Clear the interrupts & FIFOs*/ if (cmd & CMD_BIT_DATA) prv_clear_all(); while (step) { prv_set_clock((cmd & CMD_BIT_LS) ? SD_MMC_ENUM_CLOCK : pdev->speed); /* Clear the interrupts */ LPC_SDMMC->RINTSTS = 0xFFFFFFFF; switch (step) { case 1: /* Execute command */ cmd_reg = ((cmd & CMD_MASK_CMD) >> CMD_SHIFT_CMD) | ((cmd & CMD_BIT_INIT) ? MCI_CMD_INIT : 0) | ((cmd & CMD_BIT_DATA) ? (MCI_CMD_DAT_EXP | MCI_CMD_PRV_DAT_WAIT) : 0) | (((cmd & CMD_MASK_RESP) == CMD_RESP_R2) ? MCI_CMD_RESP_LONG : 0) | ((cmd & CMD_MASK_RESP) ? MCI_CMD_RESP_EXP : 0) | ((cmd & CMD_BIT_WRITE) ? MCI_CMD_DAT_WR : 0) | ((cmd & CMD_BIT_STREAM) ? MCI_CMD_STRM_MODE : 0) | ((cmd & CMD_BIT_BUSY) ? MCI_CMD_STOP : 0) | ((cmd & CMD_BIT_AUTO_STOP) ? MCI_CMD_SEND_STOP : 0) | MCI_CMD_START ; /* wait for previos data finsh for select/deselect commands */ if (((cmd & CMD_MASK_CMD) >> CMD_SHIFT_CMD) == MMC_SELECT_CARD) { cmd_reg |= MCI_CMD_PRV_DAT_WAIT; } /* wait for command to be accepted by CIU */ if (prv_send_cmd(cmd_reg, arg) == 0) --step; break; case 0: return 0; case 2: /* APP prefix */ cmd_reg = MMC_APP_CMD | MCI_CMD_RESP_EXP /* Response is status */ | ((cmd & CMD_BIT_INIT) ? MCI_CMD_INIT : 0) | MCI_CMD_START ; if (prv_send_cmd(cmd_reg, pdev->rca << 16) == 0) --step; break; } /* wait for command response*/ status = prv_wait_for_completion(pdev, wait_status); /* We return an error if there is a timeout, even if we've fetched a response */ if (status & MCI_INT_ERROR) return status; if (status & MCI_INT_CMD_DONE) { switch (cmd & CMD_MASK_RESP) { case 0: break; case CMD_RESP_R1: case CMD_RESP_R3: prv_pull_response(pdev, 48); break; case CMD_RESP_R2: prv_pull_response(pdev, 136); break; } } } return 0; } /***************************************************************************** ** Function name: sdio_acquire ** ** Descriptions: Function to enumerate the SD/MMC/SDHC/MMC+ cards ** ** parameters: None ** ** Returned value: 1 if a card is acquired, otherwise 0 ** *****************************************************************************/ int sdio_acquire(void) { MCI_CARD_INFO_T* pdev = &g_card_info; int status; int tries = 0; uint32_t ocr = OCR_VOLTAGE_RANGE_MSK; uint32_t r; int state = 0; uint32_t command = 0; /* preserve wait_func and irq callback*/ MCI_CMD_WAIT_FUNC_T temp = pdev->wait_func; MCI_IRQ_CB_FUNC_T tempirq = pdev->irq_callback; /* clear card struct */ memset(pdev, 0, sizeof(MCI_CARD_INFO_T)); /* restore wait_func */ pdev->wait_func = temp; pdev->irq_callback = tempirq; /* clear card type */ LPC_SDMMC->CTYPE = 0; /* set high speed for the card as 20MHz */ pdev->speed = MMC_MAX_CLOCK; status = sdio_execute_command(pdev, CMD_IDLE, 0, MCI_INT_CMD_DONE); while (state < 100) { switch (state) { case 0: /* Setup for SD */ /* check if it is SDHC card */ status = sdio_execute_command(pdev, CMD_SD_SEND_IF_COND, SD_SEND_IF_ARG, 0); if (!(status & MCI_INT_RTO)) { /* check response has same echo pattern */ if ((pdev->response[0] & SD_SEND_IF_ECHO_MSK) == SD_SEND_IF_RESP) /* it is SD 2.0 card so indicate we are SDHC capable*/ ocr |= OCR_HC_CCS; } ++state; command = CMD_SD_OP_COND; tries = INIT_OP_RETRIES; /* assume SD card */ pdev->card_type |= CARD_TYPE_SD; /* for SD cards high speed is 25MHz */ pdev->speed = SD_MAX_CLOCK; break; case 10: /* Setup for MMC */ /* start fresh for MMC crds */ pdev->card_type &= ~CARD_TYPE_SD; status = sdio_execute_command(pdev, CMD_IDLE, 0, MCI_INT_CMD_DONE); command = CMD_MMC_OP_COND; tries = INIT_OP_RETRIES; ocr |= OCR_HC_CCS; ++state; /* for MMC cards high speed is 20MHz */ pdev->speed = MMC_MAX_CLOCK; break; case 1: case 11: status = sdio_execute_command(pdev, command, 0, 0); if (status & MCI_INT_RTO) state += 9; /* Mode unavailable */ else ++state; break; case 2: /* Initial OCR check */ case 12: ocr = pdev->response[0] | (ocr & OCR_HC_CCS); if (ocr & OCR_ALL_READY) ++state; else state += 2; break; case 3: /* Initial wait for OCR clear */ case 13: while ((ocr & OCR_ALL_READY) && --tries > 0) { timer_wait_ms(0, MS_ACQUIRE_DELAY); status = sdio_execute_command(pdev, command, 0, 0); ocr = pdev->response[0] | (ocr & OCR_HC_CCS); } if (ocr & OCR_ALL_READY) state += 7; else ++state; break; case 14: /* for MMC cards set high capacity bit */ ocr |= OCR_HC_CCS; case 4: /* Assign OCR */ tries = SET_OP_RETRIES; ocr &= OCR_VOLTAGE_RANGE_MSK | OCR_HC_CCS; /* Mask for the bits we care about */ do { timer_wait_ms(0, MS_ACQUIRE_DELAY); status = sdio_execute_command(pdev, command, ocr, 0); r = pdev->response[0]; } while (!(r & OCR_ALL_READY) && --tries > 0); if (r & OCR_ALL_READY) { /* is it high capacity card */ pdev->card_type |= (r & OCR_HC_CCS); ++state; } else state += 6; break; case 5: /* CID polling */ case 15: status = sdio_execute_command(pdev, CMD_ALL_SEND_CID, 0, 0); memcpy(pdev->cid, pdev->response, 16); ++state; break; case 6: /* RCA send, for SD get RCA */ status = sdio_execute_command(pdev, CMD_SD_SEND_RCA, 0, 0); pdev->rca = (pdev->response[0]) >> 16; ++state; break; case 16: /* RCA assignment for MMC set to 1 */ pdev->rca = 1; status = sdio_execute_command(pdev, CMD_MMC_SET_RCA, pdev->rca << 16, 0); ++state; break; case 7: case 17: status = sdio_execute_command(pdev, CMD_SEND_CSD, pdev->rca << 16, 0); memcpy(pdev->csd, pdev->response, 16); state = 100; break; default: state += 100; /* break from while loop */ break; } } /* Compute card size, block size and no. of blocks based on CSD response recived. */ if (prv_card_acquired(pdev)) prv_process_csd(pdev); return prv_card_acquired(&g_card_info); } /***************************************************************************** ** Function name: sdio_init ** ** Descriptions: Initializes the MCI card controller ** ** parameters: waitfunc : Pointer to wait function to be used during for poll ** command status. ** irqfunc : Pointer to IRQ status callback ** ** Returned value: None ** *****************************************************************************/ void sdio_init(MCI_CMD_WAIT_FUNC_T waitfunc, MCI_IRQ_CB_FUNC_T irqfunc) { volatile uint32_t i; /* enable SD/MMC clock */ sdio_enable_clock(); /* Software reset */ LPC_SDMMC->BMOD = MCI_BMOD_SWR; /* reset all blocks */ LPC_SDMMC->CTRL = MCI_CTRL_RESET | MCI_CTRL_FIFO_RESET | MCI_CTRL_DMA_RESET; while (LPC_SDMMC->CTRL & (MCI_CTRL_RESET | MCI_CTRL_FIFO_RESET | MCI_CTRL_DMA_RESET)); /* Internal DMA setup for control register */ #ifdef SDIO_USE_POLLING LPC_SDMMC->CTRL = MCI_CTRL_USE_INT_DMAC; #else LPC_SDMMC->CTRL = MCI_CTRL_USE_INT_DMAC | MCI_CTRL_INT_ENABLE; #endif /* Clear the interrupts for the host controller */ LPC_SDMMC->RINTSTS = 0xFFFFFFFF; /* Put in max timeout */ LPC_SDMMC->TMOUT = 0xFFFFFFFF; /* FIFO threshold settings for DMA, DMA burst of 4, FIFO watermark at 16 */ LPC_SDMMC->FIFOTH = (0x1 << 28) | (0xF << 16) | (0x10 << 0); /* Enable DMA, burst size of 4, fixed burst */ LPC_SDMMC->BMOD = MCI_BMOD_DE | MCI_BMOD_PBL4 | MCI_BMOD_DSL(4); /* disable clock to CIU (needs latch) */ LPC_SDMMC->CLKENA = 0; LPC_SDMMC->CLKSRC = 0; /* clear mmc structure*/ memset(&g_card_info, 0, sizeof(MCI_CARD_INFO_T)); /* set the wait_func if passed */ g_card_info.wait_func = waitfunc; g_card_info.irq_callback = irqfunc; /* If not in polling mode, enable SDIO IRQ */ #ifndef SDIO_USE_POLLING NVIC_EnableIRQ(SDIO_IRQn); #endif } /***************************************************************************** ** Function name: sdio_card_detect ** ** Descriptions: Detect if an SD card is inserted ** ** parameters: None ** ** Returned value: 1 if a card is detected, otherwise 0 ** *****************************************************************************/ int sdio_card_detect(void) { /* No card = high state in regsiter */ if (LPC_SDMMC->CDETECT & 1) return 0; return 1; } /***************************************************************************** ** Function name: sdio_card_wp_on ** ** Descriptions: Detect if write protect is enabled ** ** parameters: None ** ** Returned value: Returns 1 if card is write protected, otherwise 0 ** *****************************************************************************/ int sdio_card_wp_on(void) { if (LPC_SDMMC->WRTPRT & 1) return 1; return 0; } /***************************************************************************** ** Function name: sdio_power_on ** ** Descriptions: Enable slot power ** ** Returned value: None ** *****************************************************************************/ void sdio_power_on(void) { sdio_power_onoff(0); } /***************************************************************************** ** Function name: sdio_power_off ** ** Descriptions: Enable slot power ** ** Returned value: None ** *****************************************************************************/ void sdio_power_off(void) { sdio_power_onoff(1); } /***************************************************************************** ** Function name: sdio_power_onoff ** ** Descriptions: Enable or disable slot power ** ** parameters: enable: !0 to enable, or 0 to disable ** ** Returned value: None ** *****************************************************************************/ void sdio_power_onoff(int enable) { if (enable) LPC_SDMMC->PWREN = 1; else LPC_SDMMC->PWREN = 0; } /***************************************************************************** ** Function name: sdio_get_state ** ** Descriptions: Get card's current state (idle, transfer, program, etc.) ** ** parameters: None ** ** Returned value: Current transfer state (0 - ** *****************************************************************************/ /* Get card's current state (idle, transfer, program, etc.) */ int sdio_get_state(void) { uint32_t status; /* get current state of the card */ status = sdio_execute_command(&g_card_info, CMD_SEND_STATUS, g_card_info.rca << 16, 0); if (status & MCI_INT_RTO) { /* unable to get the card state. So return immediatly. */ return -1; } /* check card state in response */ return (int) R1_CURRENT_STATE(g_card_info.response[0]); } /***************************************************************************** ** Function name: sdio_deinit ** ** Descriptions: Close the MCI ** ** parameters: None ** ** Returned value: None ** *****************************************************************************/ void sdio_deinit(void) { /* Place card in idle state */ if (prv_card_acquired(&g_card_info) == 0) sdio_execute_command(&g_card_info, CMD_IDLE, 0, MCI_INT_CMD_DONE); /* clear mmc structure*/ memset(&g_card_info, 0, sizeof(MCI_CARD_INFO_T)); sdio_disable_clock(); } /***************************************************************************** ** Function name: sdio_read_blocks ** ** Descriptions: Performs the read of data from the SD/MMC card ** ** parameters: buffer: Pointer to data buffer to copy to ** start_block: Start block number ** end_block: End block number ** ** Returned value: Bytes read, or 0 on error ** *****************************************************************************/ int sdio_read_blocks(void* buffer, int start_block, int end_block) { MCI_CARD_INFO_T* pdev = &g_card_info; int cbRead = (end_block - start_block + 1) << 9; /*(end_block - start_block) * 512 */ int status = 0; int index; /* if card is not acquired return immediately */ if ((prv_card_acquired(pdev) == 0) || (end_block < start_block) /* check block index in range */ || (start_block < 0) || (end_block > pdev->blocknr) ) { return 0; } /* put card in trans state */ if (prv_set_trans_state(pdev) != 0) return 0; /* set number of bytes to read */ LPC_SDMMC->BYTCNT = cbRead; /* if high capacity card use block indexing */ if (pdev->card_type & CARD_TYPE_HC) index = start_block; else index = start_block << 9; sdio_dma_setup((uint32_t) buffer, cbRead); /* check how many blocks to read */ if (end_block == start_block) { status = sdio_execute_command(pdev, CMD_READ_SINGLE, index, 0 | MCI_INT_DATA_OVER); } else { /* do read multiple */ status = sdio_execute_command(pdev, CMD_READ_MULTIPLE, index, 0 | MCI_INT_DATA_OVER); } if (status != 0) cbRead = 0; /* return error if command fails */ wait_for_program_finish(); return cbRead; } /***************************************************************************** ** Function name: sdio_write_blocks ** ** Descriptions: Performs write of data to the SD/MMC card ** ** parameters: buffer: Pointer to data buffer to copy to ** start_block: Start block number ** end_block: End block number ** ** Returned value: Number of bytes actually written, or 0 on error ** *****************************************************************************/ int sdio_write_blocks(void* buffer, int start_block, int end_block) { MCI_CARD_INFO_T* pdev = &g_card_info; /*(end_block - start_block) * 512 */ int cbWrote = (end_block - start_block + 1) << 9; int status; int index; /* if card is not acquired return immediately */ if ((prv_card_acquired(pdev) == 0) || (end_block < start_block) /* check block index in range */ || (start_block < 0) || (end_block > pdev->blocknr) ) { return 0; } wait_for_program_finish(); /* put card in trans state */ if (prv_set_trans_state(pdev) != 0) return 0; /* set number of bytes to write */ LPC_SDMMC->BYTCNT = cbWrote; /* if high capacity card use block indexing */ if (pdev->card_type & CARD_TYPE_HC) index = start_block; else index = start_block << 9; sdio_dma_setup((uint32_t) buffer, cbWrote); wait_for_program_finish(); /* check how many blocks to write */ if (end_block == start_block) { status = sdio_execute_command(pdev, CMD_WRITE_SINGLE, index, 0 | MCI_INT_DATA_OVER); } else { /* do write multiple */ status = sdio_execute_command(pdev, CMD_WRITE_MULTIPLE, index, 0 | MCI_INT_DATA_OVER); } if (status != 0) cbWrote = 0; wait_for_program_finish(); return cbWrote; } /********************************************************************** ** Function name: SDIO_IRQHandler ** ** Description: SDIO controller interrupt handler ** ** Parameters: None ** ** Returned value: None **********************************************************************/ void SDIO_IRQHandler(void) { if (g_card_info.irq_callback) g_card_info.irq_callback(&g_card_info, LPC_SDMMC->RINTSTS); } /********************************************************************** ** Function name: sdio_get_device_size ** ** Description: Return the capacity of the SD card in bytes ** ** Parameters: None ** ** Returned value: Device capacity in bytes **********************************************************************/ int sdio_get_device_size(void) { return g_card_info.device_size; }