/** * \brief Writes data at the specified address on the serial firmware dataflash. The * page(s) to program must have been erased prior to writing. This function * handles page boundary crossing automatically. * * \param pAt25 Pointer to an AT25 driver instance. * \param pData Data buffer. * \param size Number of bytes in buffer. * \param address Write address. * * \return 0 if successful; otherwise, returns AT25_ERROR_PROGRAM is there has * been an error during the data programming. */ unsigned char AT25D_Write( At25 *pAt25, unsigned char *pData, unsigned int size, unsigned int address) { unsigned int pageSize; unsigned int writeSize; unsigned char error; unsigned char status; assert(pAt25); assert(pData); /* Retrieve device page size */ pageSize = AT25_PageSize(pAt25); /* Program one page after the other */ while (size > 0) { /* Compute number of bytes to program in page */ writeSize = min(size, pageSize - (address % pageSize)); /* Enable critical write operation */ AT25D_EnableWrite(pAt25); /* Program page */ error = AT25_SendCommand(pAt25, AT25_BYTE_PAGE_PROGRAM, 4, pData, writeSize, address, 0, 0); assert(!error); /* Wait for transfer to finish */ AT25D_Wait(pAt25); /* Poll the Serial flash status register until the operation is achieved */ AT25D_WaitReady(pAt25); /* Make sure that write was without error */ status = AT25D_ReadStatus(pAt25); if ((status & AT25_STATUS_EPE) == AT25_STATUS_EPE_ERROR) { return AT25_ERROR_PROGRAM; } pData += writeSize; size -= writeSize; address += writeSize; } return 0; }
int main(int argc, char **argv) { struct _Mailbox *pMailbox = (struct _Mailbox *) argv; /* Communication type with SAM-BA GUI. */ uint8_t comType; uint32_t jedecId; uint32_t bytesToWrite, bytesToRead, bufferAddr, startMemoryOffset, memoryOffset, packetSize; /* index on read/write buffer */ uint8_t *pBuffer; /* Temporary buffer used for non block aligned read/write */ uint32_t tempBufferAddr; /* Offset in destination buffer during buffer copy */ uint32_t bufferOffset; /* INIT */ /* Save communication link type */ comType = pMailbox->argument.inputInit.comType; if (pMailbox->command == APPLET_CMD_INIT) { #if (DYN_TRACES == 1) dwTraceLevel = pMailbox->argument.inputInit.traceLevel; #endif TRACE_INFO("-- SerialFlash AT25/AT26 applet %s --\n\r", SAM_BA_APPLETS_VERSION); TRACE_INFO("-- %s\n\r", BOARD_NAME); TRACE_INFO("-- Compiled: %s %s --\n\r", __DATE__, __TIME__); /* Configure pins */ PIO_Configure(pins, PIO_LISTSIZE(pins)); /* Initialize DMA driver instance with polling mode */ DMAD_Initialize( &dmad, 1 ); /* Initialize the SPI and serial flash */ SPID_Configure(&spid, SPI0, ID_SPI0, &dmad); AT25_Configure(&at25, &spid, SPI_CS, 1); TRACE_INFO("SPI and AT25/AT25 drivers initialized\n\r"); pMailbox->argument.outputInit.bufferAddress = (uint32_t) &_end ; /* Read the JEDEC ID of the device to identify it */ jedecId = AT25D_ReadJedecId(&at25); if (AT25_FindDevice(&at25, jedecId) == 0) { pMailbox->status = APPLET_DEV_UNKNOWN; pMailbox->argument.outputInit.bufferSize = 0; pMailbox->argument.outputInit.memorySize = 0; TRACE_INFO("Device Unknown\n\r"); goto exit; } else { /* Get device parameters */ pMailbox->status = APPLET_SUCCESS; pageSize = AT25_PageSize(&at25); blockSize = AT25_BlockSize(&at25); /* Program page */ if (AT25_ManId(&at25) == SST_SPI_FLASH) { /* SST Flash write is slower, we reduce buffer size to avoid USB timeout */ bufferSize = 10 * pageSize; } else { bufferSize = 4 * blockSize; } /* integer number of pages can be contained in each buffer */ bufferSize -= bufferSize % pageSize; if ( bufferSize < pageSize) { TRACE_INFO("No enought memory to load buffer.\n\r"); goto exit; } pMailbox->argument.outputInit.bufferSize = bufferSize; pMailbox->argument.outputInit.memorySize = AT25_Size(&at25); TRACE_INFO("%s blockSize : 0x%lx bufferAddr : 0x%lx\n\r", at25.pDesc->name, blockSize, pMailbox->argument.outputInit.bufferAddress); } } // ---------------------------------------------------------- // WRITE: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_WRITE) { startMemoryOffset = pMailbox->argument.inputWrite.memoryOffset; memoryOffset = startMemoryOffset; bufferAddr = pMailbox->argument.inputWrite.bufferAddr; tempBufferAddr = bufferAddr + bufferSize; bytesToWrite = pMailbox->argument.inputWrite.bufferSize; TRACE_INFO("WRITE at offset: 0x%lx buffer at : 0x%lx of: 0x%lx Bytes\n\r", memoryOffset, bufferAddr, bytesToWrite); /* Check word alignment */ if (memoryOffset % 4) { pMailbox->status = APPLET_ALIGN_ERROR; goto exit; } if (AT25D_Unprotect(&at25)) { TRACE_INFO("Can not unprotect the flash\n\r"); pMailbox->status = APPLET_UNPROTECT_FAIL; goto exit; } pBuffer = (uint8_t *) bufferAddr; if ((memoryOffset % pageSize) != 0) { /* We are not page aligned, retrieve first page content to update it*/ if (memoryOffset < writtenAddress) { lastErasedBlock = 0xFFFF; } /* Flush temp buffer */ memset((uint32_t *)tempBufferAddr, 0xFF, pageSize); bufferOffset = (memoryOffset % pageSize); packetSize = pageSize - bufferOffset; memoryOffset -= bufferOffset; /* Read page to be updated*/ AT25D_Read(&at25, (uint8_t *) tempBufferAddr, pageSize, memoryOffset); /* Fill retrieved page with data to be programmed */ memcpy((uint8_t *)(tempBufferAddr + bufferOffset), pBuffer, packetSize); if (((memoryOffset / blockSize) > lastErasedBlock) || (lastErasedBlock == 0xFFFF)) { /* Erase the block to be updated */ AT25D_EraseBlock(&at25, memoryOffset); lastErasedBlock = (memoryOffset / blockSize); } /* Write the page contents */ AT25D_Write(&at25, (uint8_t *) tempBufferAddr, pageSize, memoryOffset); bytesToWrite = (bytesToWrite > packetSize) ? (bytesToWrite - packetSize) : 0; pBuffer += packetSize; memoryOffset += pageSize; writtenAddress = memoryOffset; } /* If it remains more than one page to write */ while (bytesToWrite >= pageSize) { if (memoryOffset < writtenAddress) { lastErasedBlock = 0xFFFF; } if (((memoryOffset / blockSize) > lastErasedBlock) || (lastErasedBlock == 0xFFFF)) { /* Erase the block to be updated */ AT25D_EraseBlock(&at25, memoryOffset); lastErasedBlock = (memoryOffset / blockSize); } /* Write the page contents */ AT25D_Write(&at25, (uint8_t *) pBuffer, pageSize, memoryOffset); pBuffer += pageSize; memoryOffset += pageSize; bytesToWrite -= pageSize; writtenAddress = memoryOffset; } /* Write remaining data */ if (bytesToWrite > 0) { /* Read previous content of page */ AT25D_Read(&at25, (uint8_t *) tempBufferAddr, pageSize, memoryOffset); /* Fill retrieved block with data to be programmed */ memcpy((uint8_t *)tempBufferAddr, pBuffer, bytesToWrite); if (((memoryOffset / blockSize) > lastErasedBlock) || (lastErasedBlock == 0xFFFF)) { /* Erase the block to be updated */ AT25D_EraseBlock(&at25, memoryOffset); lastErasedBlock = (memoryOffset / blockSize); } /* Write the page contents */; AT25D_Write(&at25, (uint8_t *) tempBufferAddr, pageSize, memoryOffset); writtenAddress = memoryOffset + bytesToWrite; /* No more bytes to write */ bytesToWrite = 0; } TRACE_INFO("WRITE return byte written : 0x%lx Bytes\n\r", pMailbox->argument.inputWrite.bufferSize - bytesToWrite); pMailbox->argument.outputWrite.bytesWritten = pMailbox->argument.inputWrite.bufferSize - bytesToWrite; pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // READ: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_READ) { memoryOffset = pMailbox->argument.inputRead.memoryOffset; bufferAddr = pMailbox->argument.inputRead.bufferAddr; bytesToRead = pMailbox->argument.inputRead.bufferSize; TRACE_INFO("READ at offset: 0x%lx buffer at : 0x%lx of: 0x%lx Bytes\n\r", memoryOffset, bufferAddr, bytesToRead); /* Check word alignment */ if (memoryOffset % 4) { pMailbox->status = APPLET_ALIGN_ERROR; goto exit; } pBuffer = (uint8_t *) bufferAddr; /* Read packet after packets */ while (((uint32_t)pBuffer < (bufferAddr + bufferSize)) && (bytesToRead > 0)) { packetSize = min(MAX_COUNT, bytesToRead); AT25D_Read(&at25, pBuffer, packetSize, memoryOffset); pBuffer += packetSize; bytesToRead -= packetSize; memoryOffset += packetSize; } TRACE_INFO("READ return byte read : 0x%lx Bytes\n\r", pMailbox->argument.inputRead.bufferSize - bytesToRead); pMailbox->argument.outputRead.bytesRead = pMailbox->argument.inputRead.bufferSize - bytesToRead; pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // FULL ERASE: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_FULL_ERASE) { TRACE_INFO("FULL ERASE\n\r"); /* Unprotected the flash */ if (AT25D_Unprotect(&at25)) { TRACE_INFO("Can not unprotect the flash\n\r"); pMailbox->status = APPLET_UNPROTECT_FAIL; goto exit; } TRACE_INFO("Flash unprotected\n\r"); /* Erase the chip */ TRACE_INFO("Chip is being erased...\n\r"); if (AT25D_EraseChip(&at25)) { TRACE_INFO("Erasing error\n\r"); pMailbox->status = APPLET_ERASE_FAIL; goto exit; } TRACE_INFO("Full Erase achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } // ---------------------------------------------------------- // BUFFER ERASE: // ---------------------------------------------------------- else if (pMailbox->command == APPLET_CMD_BUFFER_ERASE) { TRACE_INFO("BUFFER ERASE \n\r"); /* Unprotected the flash */ if (AT25D_Unprotect(&at25)) { TRACE_INFO("Can not unprotect the flash\n\r"); pMailbox->status = APPLET_UNPROTECT_FAIL; goto exit; } memoryOffset = pMailbox->argument.inputBufferErase.memoryOffset; if (AT25D_EraseBlock(&at25, memoryOffset)) { pMailbox->status = APPLET_ERASE_FAIL; TRACE_INFO("Block erasing error\n\r"); goto exit; } pMailbox->argument.outputBufferErase.bytesErased = AT25_BlockSize(&at25); TRACE_INFO("Buffer Erase achieved\n\r"); pMailbox->status = APPLET_SUCCESS; } exit: /* Acknowledge the end of command */ TRACE_INFO("\tEnd of applet (command : %lx --- status : %lx)\n\r", pMailbox->command, pMailbox->status); /* Notify the host application of the end of the command processing */ pMailbox->command = ~(pMailbox->command); /* Send ACK character */ if (comType == DBGU_COM_TYPE) { /* Wait for the transmitter to be ready */ while ( (DBGU->DBGU_SR & DBGU_SR_TXEMPTY) == 0 ) ; /* Send character */ DBGU->DBGU_THR= 0x06 ; } return 0; }