/* --------------------------------------------------------------------------- * mmc.c (c) 2008 Micro-key bv * --------------------------------------------------------------------------- * Micro-key bv * Industrieweg 28, 9804 TG Noordhorn * Postbus 92, 9800 AB Zuidhorn * The Netherlands * Tel: +31 594 503020 * Fax: +31 594 505825 * Email: support@microkey.nl * Web: www.microkey.nl * --------------------------------------------------------------------------- * Description: * --------------------------------------------------------------------------- * Version(s): 0.1, Apr 22, 2008, MMi * Creation. * --------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------- * System include files * --------------------------------------------------------------------------- */ /* Compiler Includes */ #include #include #include "LPC23xx.h" #include "types.h" /* --------------------------------------------------------------------------- * Application include files * --------------------------------------------------------------------------- */ #include "mmc.h" #include "mmc_transfer.h" #include "dio.h" /* --------------------------------------------------------------------------- * Local constant and macro definitions * --------------------------------------------------------------------------- */ #define MMC_GLOBAL #define __no_init #define CSD_GET_TRAN_SPEED_EXP() (MmcSdCsd[ 0]&0x07) #define CSD_GET_TRAN_SPEED_MANT() ((MmcSdCsd[ 0]&0xF8)>>3 ) #define CSD_GET_NSAC() (MmcSdCsd[ 1] ) #define CSD_GET_TAAC_EXP() (MmcSdCsd[ 2]&0x7) #define CSD_GET_TAAC_MANT() ((MmcSdCsd[ 2]&0xF8)>>3 ) #define CSD_GET_R2W_FACTOR() ((MmcSdCsd[15]&0x1C)>>2 ) #define CSD_GET_READ_BL_LEN() (MmcSdCsd[ 6]&0x0F) #define CSD_GET_C_SIZE() (((MmcSdCsd[ 5]&0x03)<<10) + (MmcSdCsd[4]<<2) + ((MmcSdCsd[11]&0xc0)>>6)) #define CSD_GET_C_SIZE_MULT() (((MmcSdCsd[10]&0x03)<<1 ) +((MmcSdCsd[9]&0x80)>>7)) #define CSD_GET_PERM_WRITE_PROTECT() ((MmcSdCsd[13]&0x20)>>5 ) #define CSD_GET_TMP_WRITE_PROTECT() ((MmcSdCsd[13]&0x10)>>4 ) volatile UINT32 i; #define SpcInquiryRemovableMedium 0x80 #define SpcInquiryStandartVersion 5 #define SizeOfInquiryDescMmcDsk 36 const UINT32 MmcTransfExp[] = { 10000UL, 100000UL, 1000000UL, 10000000UL, 0UL, 0UL, 0UL, 0UL, }; const UINT32 MmmcAccessTime [] = { 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, }; const UINT32 MmcCsdMant[] = { 0UL, 10UL, 12UL, 13UL, 15UL, 20UL, 25UL, 30UL, 35UL, 40UL, 45UL, 50UL, 55UL, 60UL, 70UL, 80UL, }; const MmcCommads_t MmcCmd[CMD_END] = { /* {TxData, Arg, Resp} */ { 0x00, MmcNoArg, MmcNoResp }, /* CMD0 */ { 0x01, MmcOcr, MmcR3 }, /* CMD1 */ { 0x02, MmcNoArg, MmcR2 }, /* CMD2 */ { 0x03, MmcRelAddr, MmcR1 }, /* CMD3 */ { 0x07, MmcRelAddr, MmcR1 }, /* CMD7 */ { 0x09, MmcRelAddr, MmcR2 }, /* CMD9 */ { 0x0A, MmcRelAddr, MmcR2 }, /* CMD10 */ { 0x0C, MmcNoArg, MmcR1b }, /* CMD12 */ { 0x0D, MmcRelAddr, MmcR1 }, /* CMD13 */ { 0x10, MmcBlockLen, MmcR1 }, /* CMD16 */ { 0x11, MmcDataAdd, MmcR1 }, /* CMD17 */ { 0x12, MmcDataAdd, MmcR1 }, /* CMD18 */ { 0x18, MmcDataAdd, MmcR1 }, /* CMD24 */ { 0x19, MmcDataAdd, MmcR1 }, /* CMD25 */ { 0x1B, MmcOcr, MmcR1 }, /* CMD27 */ { 0x1C, MmcDataAdd, MmcR1b }, /* CMD28 */ { 0x1D, MmcDataAdd, MmcR1b }, /* CMD29 */ { 0x1E, MmcDataAdd, MmcR1 }, /* CMD30 */ { 0x20, MmcDataAdd, MmcR1 }, /* CMD32 */ { 0x21, MmcDataAdd, MmcR1 }, /* CMD33 */ { 0x22, MmcDataAdd, MmcR1 }, /* CMD34 */ { 0x23, MmcDataAdd, MmcR1 }, /* CMD35 */ { 0x24, MmcDataAdd, MmcR1 }, /* CMD36 */ { 0x25, MmcDataAdd, MmcR1 }, /* CMD37 */ { 0x26, MmcNoArg, MmcR1b }, /* CMD38 */ { 0x2A, MmcNoArg, MmcR1b }, /* CMD42 */ { 0x37, MmcRelAddr, MmcR1 }, /* CMD55 */ { 0x38, MmcNoArg, MmcR1 }, /* CMD56 */ { 0x06, MmcDataAdd, MmcR1 }, /* ACMD46 */ { 0x29, MmcDataAdd, MmcR3 }, /* ACMD41 */ }; const UINT8 MmcDskInquiry[]__attribute__((aligned (4)))= { SbcDirectAccess, /* 0 PERIPHERAL QUALIFIER AND DEVICE*/ SpcInquiryRemovableMedium, /* 1 RMB */ SpcInquiryStandartVersion, /* 2 VERSION */ 0x02, /* 3 NORMACA HISUP RESPONSE FORMAT */ 36-4, /* 4 ADDITIONAL LENGTH (n-4) */ 0x00, /* 5 SCCS */ /* for parallel SCSI only */ 0x00, /* 6 BQUE ENCSERV VS MULTIP MCHNGR Obsolete Obsolete ADDR16 */ 0x00, /* 7 RELADR Obsolete WBUS16† SYNC† LINKED Obsolete CMDQUE VS*/ 'I','A','R',' ','S','y','s','.', /* 8 - 15 VENDOR IDENTIFICATION */ /* 16 - 31 PRODUCT IDENTIFICATION */ 'L','P','C','2','3','7','8',' ','S','t','o','r','a','g','e',' ', '1','.','0','0', /* 32 - 35 PRODUCT REVISION LEVEL */ }; /* --------------------------------------------------------------------------- * Global variable definitions * --------------------------------------------------------------------------- */ static UINT32 CardRCA; UINT32 MmcLastError; UINT32 dlycnt; UINT32 Tnac; UINT32 Twr; UINT8 MmcSdCsd[16]__attribute__ ((aligned (4))); static BOOLEAN bMmcPermWriteProtect; BOOLEAN bMmcChanged; DiskCtrlBlk_t MmcDskCtrlBlk; /* --------------------------------------------------------------------------- * Local variable definitions * --------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------- * Local function definitions * --------------------------------------------------------------------------- */ /* Function returns TRUE if Card is inserted, FALSE if Card is removed */ inline BOOLEAN MmcPresent(void) { return (!(MMC_CP_FIO & MMC_CP_MASK)); } /* Function returns TRUE is WriteProtection is activated, FALSE if * Protection is off. If no Card is inserted, Function will also return * TRUE. This results from the physical Switch Connection (See Datasheet). * Usage of this Function only makes Sense with previous TRUE-Return * from MmcPresent. */ inline BOOLEAN MmcWriteProtect(void) { if (!(MMC_WP_FIO & MMC_WP_MASK)) { return (FALSE); } else { return (TRUE); } } UINT32 MmcSetClockFreq (UINT32 Frequency) { UINT32 Pclk = SYS_GetFPclk(MCI_PCLK_OFFSET); UINT32 Div; Frequency <<= 1; for(Div = 1; Div <= 256; ++Div) { if((Frequency * Div)> Pclk) { break; } } MCI_CLOCK = ((Div - 1) & 0xFF); /* Set Clock Devider (Bit 0-7) */ for(dlycnt = 10; dlycnt > 0;dlycnt--); MCI_CLOCK |= (1 << 8); /* enable Clock (Bit 8) */ for(dlycnt = 10; dlycnt > 0;dlycnt--); MCI_CLOCK |= (1 << 9); /* Set PowerSave (Bit 9) */ for(dlycnt = 10; dlycnt > 0;dlycnt--); return(Pclk/(Div<<1)); /* Return real frequency */ } void MmcInit(void) { UINT32 i; MMC_CP_DIR &= ~MMC_CP_MASK; /* Set CP to Input */ MMC_CP_MODE &=~(1 << 24); /* Neither PullUp nor PullDown */ MMC_CP_MODE |= (1 << 25); MMC_WP_DIR &= ~MMC_WP_MASK; /* Set WP to Input */ MMC_WP_MODE &=~(1 << 22); /* Neither PullUp nor PullDown */ MMC_WP_MODE |= (1 << 23); PCLKSEL1 |= (1 << 24); /* set divider to /1 (Bit 24&25) */ PCLKSEL1 &=~(1 << 25); PCONP |= 0x30000000; /* Set Power up */ PINSEL1 &=~(1 << 6); /* Assign Pin 0.19 (Bit 6&7) */ PINSEL1 |= (1 << 7); PINSEL1 &=~(1 << 8); /* Assign Pin 0.20 (Bit 8&9) */ PINSEL1 |= (1 << 9); PINSEL1 &=~(1 << 10); /* Assign Pin 0.21 (Bit 10&11) */ PINSEL1 |= (1 << 11); PINSEL1 &=~(1 << 12); /* Assign Pin 0.22 (Bit 12&13) */ PINSEL1 |= (1 << 13); PINSEL4 &=~(1 << 22); /* Assign Pin 2.11 (Bit 22&23) */ PINSEL4 |= (1 << 23); PINSEL4 &=~(1 << 24); /* Assign Pin 2.12 (Bit 24&25) */ PINSEL4 |= (1 << 25); PINSEL4 &=~(1 << 26); /* Assign Pin 2.13 (Bit 26&27) */ PINSEL4 |= (1 << 27); PINMODE1 &=~(1 << 6) & ~(1 << 7); /* Set PullUp Pin 0.19 (Bit 6&7) */ PINMODE1 &=~(1 << 8) & ~(1 << 9); /* Set PullUp Pin 0.20 (Bit 8&9) */ PINMODE1 &=~(1 << 10) & ~(1 << 11); /* Set PullUp Pin 0.21 (Bit 10&11) */ PINMODE1 &=~(1 << 12) & ~(1 << 13); /* Set PullUp Pin 0.22 (Bit 12&13) */ MCI_COMMAND = 0; /* Clear MCI Command Register */ MCI_DATA_CTRL = 0; /* Clear MCI Data Control Register */ MCI_CLEAR = 0x7FF; /* Clear all pending interrupts. */ /* Power up, switch on VCC for the Flash Card - Wait shortly */ MCI_POWER = 0x02; for (i = 0; i < 50000; i++); MCI_POWER |= 0x01; /* Power on the Flash Card. */ for (i = 0; i < 50000; i++); /* Wait shortly */ MmcPowerDown(); GPDMA_SYNC = 0; /* DMA sync enable */ GPDMA_INT_ERR_CLR = 3; /* Clear DMA Error Interrupts */ GPDMA_INT_TCCLR = 3; /* Clear DMA Interrupts */ } void MmcPowerDown(void) { SCS |= (1 << 3); /* Init Power State (Bit 3) */ MCI_POWER = 0; for(dlycnt = 10; dlycnt > 0;dlycnt--); MCI_MASK0 = 0; /* Disable all interrupts for now */ MCI_MASK1 = 0; MCI_COMMAND = 0; for(dlycnt = 10; dlycnt > 0;dlycnt--); MCI_DATA_CTRL = 0; for(dlycnt = 10; dlycnt > 0;dlycnt--); MCI_CLEAR = 0x7FF; MCI_CLOCK &=~(1 << 11); /* clear all pending interrupts */ for(dlycnt = 10; dlycnt > 0;dlycnt--); MmcSetClockFreq(IdentificationModeClock); /* ClkFreq Ident Mode< 400kHz */ } MmcState_t MmcSendCmd(MmcSpiCmdInd_t ComdInd, pUINT32 pArg) { UINT32 Status; union { UINT32 Data; struct { UINT32 CMDINDEX : 6; UINT32 RESPONSE : 1; UINT32 LONGRSP : 1; UINT32 INTERRUPT : 1; UINT32 PENDING : 1; UINT32 ENABLE : 1; UINT32 :21; }; } Command; /* The Command engine has to be Disabled when modifying the Argument * of the Peripheral */ while ((MCI_STATUS << 11) == TRUE) /* While a Command is in Progress */ { MCI_COMMAND = 0; /* Clear MCI Command Register */ for(dlycnt = 10; dlycnt > 0;dlycnt--); } Command.Data = 0; /* Copy Command-specific Index-Argument from MmcCmd LookUp Table to * Command struct */ Command.CMDINDEX = MmcCmd[ComdInd].TxData; if (pArg != NULL) /* Set Command Response depending on*/ { /* asked Argument */ switch (MmcCmd[ComdInd].Resp) { case MmcR2: Command.LONGRSP = 1; case MmcR1: case MmcR1b: case MmcR3: Command.RESPONSE = 1; case MmcNoResp:; } } Command.ENABLE = 1; if (MmcCmd[ComdInd].Arg != MmcNoArg) /* If Argument exists */ { MCI_ARGUMENT = *pArg; /* Copy Argument to MCI Argument Reg*/ } else { MCI_ARGUMENT = 0; } MCI_COMMAND = Command.Data; /* Send Command to Card */ for(dlycnt = 10; dlycnt > 0;dlycnt--); while ((Status = MCI_STATUS & 0x000000C5) == 0);/* Wait command respond */ MCI_CLEAR = Status; /* Clear actual Status */ if (Status & (1UL << 2)) /* If Command TimeOut occourses */ { MCI_COMMAND = 0; /* reset Command Register */ for(dlycnt = 10; dlycnt > 0;dlycnt--); return (MmcNoResponse); /* Return with Error Message */ } if (Status & (1UL << 0)) /* Command CRC Error */ { switch (MCI_COMMAND & 0x3F) /* Ignore CRC Error for following */ { /* Commands */ case 1: /* CMD1 */ case 41: /* ACMD42 */ case 12: /* CMD12 */ MCI_COMMAND = 0; for(dlycnt = 10; dlycnt > 0;dlycnt--); MCI_ARGUMENT = 0xFFFFFFFF; break; default: /* No CRC Error Ignoring */ MCI_COMMAND = 0; for(dlycnt = 10; dlycnt > 0;dlycnt--); return (MmcCardError); } } if (pArg != NULL) { switch (MmcCmd[ComdInd].Resp) /* Handle Command Response */ { case MmcNoResp: break; case MmcR3: *pArg = MCI_RESP0; break; case MmcR2: *pArg++ = MCI_RESP0; *pArg++ = MCI_RESP1; *pArg++ = MCI_RESP2; *pArg++ = MCI_RESP3; break; default: if (MmcCmd[ComdInd].TxData != (MCI_RESP_CMD & 0x3F)) /* Bit 0-5*/ { return (MmcCardError); } *pArg = MCI_RESP0; } } MCI_COMMAND = 0; for(dlycnt = 10; dlycnt > 0;dlycnt--); return (MmcOk); } UINT32 Status; MmcState_t MmcInitMedia(void) { UINT32 i; UINT32 res; UINT32 loopcnt; volatile UINT32 Dly; UINT8 MmcSdCid[16]; BOOLEAN CardDetected = FALSE; Tnac = 1; if (MmcPresent() == FALSE) /* If no Card inserted */ { if (MCI_POWER & 0x3) /* Is Power up? */ { MmcPowerDown(); /* Set to power down state */ } return (MmcNoPresent); /* Return with Error Message */ } MmcPowerDown(); /* Power Down */ MmcDly_1ms(100); MCI_POWER &=~(1 << 0); /* power up (Bit 1) */ MCI_POWER |= (1 << 1); for(dlycnt = 10; dlycnt > 0;dlycnt--); while ((MCI_POWER & 0x3) != 0x2); MCI_POWER |= (1 << 0); /* power reserved (Bit 0) */ MCI_POWER &=~(1 << 1); MmcDly_1ms(100); MCI_POWER &=~(1 << 6); /* Disable OpenDrain Mode */ for(dlycnt = 10; dlycnt > 0;dlycnt--); MCI_POWER = 0x02; /* Power up (Bit 1) */ for (loopcnt = 0; loopcnt < 50000; loopcnt++); MCI_POWER |= 0x01; /* Power on the Flash Card */ for (loopcnt = 0; loopcnt < 50000; loopcnt++); if (MmcSendCmd(CMD0, NULL) != MmcOk) /* Send Cmd0, Reset Card */ { return (MmcNoResponse); } MCI_POWER |= 0x40; /* Set OpenDrain output control */ MmcDskCtrlBlk.DiskType = DiskMMC; /* Determinate Card type SD or MMC */ for (i=100; i; --i) /* Try detecting several Times */ { MCI_POWER &=~(1 << 6); /* Disable OpenDrain Mode */ for(dlycnt = 10; dlycnt > 0;dlycnt--); res = 0; if (((Status = MmcSendCmd(CMD55, &res)) == MmcOk) && (res & 0x100)) { /* Send Notification for ACMD41 */ res = OcrReg; if ((MmcSendCmd(ACMD41, &res) == MmcOk) && (res & 0x80000000)) /* Activates Init Process (only SD) */ { /* Gets here when SD Card detected */ CardDetected = TRUE; MmcDskCtrlBlk.DiskType = DiskSD; break; } } else /* If Cmd55 fails */ { MCI_POWER |= (1 << 6); /* enable OpenDrain Mode */ for(dlycnt = 10; dlycnt > 0;dlycnt--); res = OcrReg; if (MmcSendCmd(CMD1, &res) == MmcOk && (res & 0x80000000)) /* CMD1 for MMC Init sequence */ { /* Gets here when MMC Card detected */ CardDetected = TRUE; break; } } MmcDly_1ms(50); } /* end Card Detection */ if (CardDetected == FALSE) /* If no Card recognized */ { return (MmcNoResponse); /* Return with Error Message */ } if (MmcSendCmd(CMD2, (pUINT32)MmcSdCid) != MmcOk) /* Read CID from Card */ { return (MmcNoResponse); /* Return with Error Message */ } /* Set address */ CardRCA = (MmcDskCtrlBlk.DiskType == DiskMMC) ? 0x00010000 : 0x00000000; if (MmcSendCmd(CMD3, &CardRCA) != MmcOk) { return (MmcNoResponse); } if (MmcDskCtrlBlk.DiskType == DiskSD) { CardRCA &= 0xFFFF0000; } else { CardRCA = 0x00010000; } MCI_POWER &=~(1 << 6); /* Disable OpenDrain Mode */ for(dlycnt = 10; dlycnt > 0;dlycnt--); /* Read Card specific Data from Card */ MmcSdCsd[0] = 0; MmcSdCsd[1] = 0; MmcSdCsd[2] = CardRCA >> 16; MmcSdCsd[3] = CardRCA >> 24; if (MmcSendCmd(CMD9, (pUINT32)MmcSdCsd) != MmcOk) { return (MmcNoResponse); } MmcCsdImplemet(); /* Implement CSD data */ res = CardRCA; if (MmcSendCmd(CMD7, &res) != MmcOk) /* Select Card */ { return (MmcNoResponse); } res = CardRCA; if (MmcOk != MmcSendCmd(CMD13, &res)) /* Send Card Status Register */ { return (MmcNoResponse); } else if (!(res & READY_FOR_DATA) || ((res & CURRENT_STATE) != CARD_TRAN)) { return (MmcCardError); } if (MmcDskCtrlBlk.DiskType == DiskSD) { MCI_CLOCK |= (1 << 11); /* Use wide bus for SD */ for (loopcnt = 0; loopcnt < 100; loopcnt++); res = CardRCA; if (((Status = MmcSendCmd(CMD55, &res)) != MmcOk) || !(res & 0x100)) /* Send Notification for ACMD6 */ { return (MmcCardError); } res = 2; if (MmcSendCmd(ACMD6, &res) != MmcOk) /* Set bus width 4bits */ { return (MmcCardError); } } res = MmcDskCtrlBlk.BlockSize; if (MmcSendCmd(CMD16, &res)) /* Set Block size */ { return (MmcNoResponse); } return (MmcOk); } UINT32 Tets; void MmcCsdImplemet(void) { UINT32 Freq; UINT64 Tmp; // Calculate SPI max clock Freq = MmcTransfExp[CSD_GET_TRAN_SPEED_EXP()] * MmcCsdMant[CSD_GET_TRAN_SPEED_MANT()]; Freq = MmcSetClockFreq(Freq); if (MmcDskCtrlBlk.DiskType == DiskMMC) { // Calculate Time outs for MMC cards Tmp = MmmcAccessTime[CSD_GET_TAAC_EXP()] * MmcCsdMant[CSD_GET_TAAC_MANT()]; Tmp /= 10000; // us // Freq [Hz], Tmp[1 us], *10 Tmp = (Freq*Tmp)/100000LL; // NSAC*100*10 Tmp += 1000*CSD_GET_NSAC(); // Max time out Tnac = Tmp; Twr = Tmp * (1<CapacityListLength = sizeof(Mmc3FormatCapDescriptor_t); if (MmcDskCtrlBlk.DiskStatus != DiskCommandPass) { pFormatCapacity->MaximumDescriptor.DescriptorType = FormattedMedia; pFormatCapacity->MaximumDescriptor.BlockLength[0] = (MmcDskCtrlBlk.BlockSize >> 16) & 0xFF; pFormatCapacity->MaximumDescriptor.BlockLength[1] = (MmcDskCtrlBlk.BlockSize >> 8) & 0xFF; pFormatCapacity->MaximumDescriptor.BlockLength[2] = (MmcDskCtrlBlk.BlockSize ) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[0] = (MmcDskCtrlBlk.BlockNumb >> 24) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[1] = (MmcDskCtrlBlk.BlockNumb >> 16) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[2] = (MmcDskCtrlBlk.BlockNumb >> 8) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[3] = (MmcDskCtrlBlk.BlockNumb ) & 0xFF; } else { pFormatCapacity->MaximumDescriptor.DescriptorType = NoMediaPresent; pFormatCapacity->MaximumDescriptor.BlockLength[0] = (2048 >> 16) & 0xFF; pFormatCapacity->MaximumDescriptor.BlockLength[1] = (2048 >> 8) & 0xFF; pFormatCapacity->MaximumDescriptor.BlockLength[2] = (2048 ) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[0] = (0xFFFFFFFF >> 24) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[1] = (0xFFFFFFFF >> 16) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[2] = (0xFFFFFFFF >> 8) & 0xFF; pFormatCapacity->MaximumDescriptor.NumberofBlocks[3] = (0xFFFFFFFF ) & 0xFF; } return(sizeof(Mmc3FormatCapResponse_t)); } return(0); } #endif pDiskCtrlBlk_t MmcGetDiskCtrlBkl(void) { return (&MmcDskCtrlBlk); } UINT32 SYS_GetFSclk(void) { UINT32 Mul = 1, Div = 1, Osc, Fsclk; if (PLLSTAT & (1 << 25)) { // when PLL is connected Mul = (PLLSTAT & 0x7FFF) +1; Div = ((PLLSTAT >> 16) & 0xFF) +1; } // Find clk source switch (CLKSRCSEL & 0x3) { case 0: Osc = I_RC_OSC_FREQ; break; case 1: Osc = MAIN_OSC_FREQ; break; case 2: Osc = RTC_OSC_FREQ; break; default: Osc = 0; } // Calculate system frequency Fsclk = Osc*Mul*2; Fsclk /= Div*(CCLKCFG+1); return (Fsclk); } UINT32 SYS_GetFPclk(UINT32 Periphery) { UINT32 Fpclk; pUINT32 pReg = (pUINT32)((Periphery < 32) ? &PCLKSEL0 : &PCLKSEL1); Periphery &= 0x1F; // %32 Fpclk = SYS_GetFSclk(); // find peripheral appropriate periphery divider switch ((*pReg >> Periphery) & 3) { case 0: Fpclk /= 4; break; case 1: break; case 2: Fpclk /= 2; break; default: Fpclk /= 8; } return (Fpclk); } void Dly100us(void *arg) { volatile UINT32 Dly = (UINT32)arg, Dly100; for (; Dly; Dly--) for (Dly100 = 500; Dly100; Dly100--) ; }