Satellite Observers Workbench. NOT yet complete, just published for forum posters to \"cherry pick\" pieces of code as requiered as an example.
Diff: flash/flash_write.c
- Revision:
- 0:0a841b89d614
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flash/flash_write.c Mon Oct 11 10:34:55 2010 +0000 @@ -0,0 +1,224 @@ +/**************************************************************************** + * Copyright 2010 Andy Kirkham, Stellar Technologies Ltd + * + * This file is part of the Satellite Observers Workbench (SOWB). + * + * SOWB 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 3 of the License, or + * (at your option) any later version. + * + * SOWB 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 SOWB. If not, see <http://www.gnu.org/licenses/>. + * + * $Id: main.cpp 5 2010-07-12 20:51:11Z ajk $ + * + ***************************************************************************/ + +#include "sowb.h" +#include "flash.h" +#include "ssp0.h" +#include "dma.h" +#include "gpio.h" +#include "rit.h" +#include "user.h" +#include "utils.h" +#include "debug.h" + +/* Flags to show what state we are in. */ +bool page_write_in_progress = false; +bool page_write_buffer_in_use = false; + +/* Flag used by the flash_erase.c file. */ +extern bool sector_erase_in_progress; + +/* Buffer used to hold a copy of the page to write. Used + to ensure the DMA has a valid buffer to copy. */ +char flash_page_write_buffer[FLASH_PAGE_SIZE]; + +/** flash_write_in_progress + */ +bool flash_write_in_progress(void) { + return page_write_in_progress; +} + +/** flash_page_write + */ +int flash_page_write(int page, char *buffer) { + + /* Wait for the write page buffer to be released by + the DMA ISR handler, if in use, then make a copy + of the source buffer for the DMA. */ + while (page_write_buffer_in_use) WHILE_WAITING_DO_PROCESS_FUNCTIONS; + memcpy(flash_page_write_buffer, buffer, FLASH_PAGE_SIZE); + page_write_buffer_in_use = true; + + + /* Below this we check for conditions that should stall + us before continuing. However, sector erase is different, + it can take quite some time to complete. If this is the + case, rather than block (wait), we'll return zero (not + done) and allow the caller to schedule a write later. */ + if (sector_erase_in_progress) return 0; + + /* Do not start a page write while another page write + is in progress. This flag is released by the RIT + timer callback when the WIP flag shows a previous + write has completed. */ + while(page_write_in_progress) WHILE_WAITING_DO_PROCESS_FUNCTIONS; + + /* Do not start a page write while a page read is in operation. */ + while (flash_read_in_progress()) WHILE_WAITING_DO_PROCESS_FUNCTIONS; + + /* Request DMA channel0. */ + while(!DMA_request_channel(0)) WHILE_WAITING_DO_PROCESS_FUNCTIONS; + + /* Request the use of SSP0. */ + while(!SSP0_request()) WHILE_WAITING_DO_PROCESS_FUNCTIONS; + + /* Flag a write is in progress. */ + page_write_in_progress = true; + + /* Switch on WIP/WEL. */ + FLASH_CS_ASSERT; + FLASH_SHORT_COMMAND(FLASH_WREN); + FLASH_CS_DEASSERT; + + /* Originally I dropped into a do { ... } while(); here but + found the time between the CS deassert and reassert inside + the loop was *very* short and the flash device wasn't to + keen on this. So, I switched to a "flush rx fifo" and the + use a while() { ... } loop, just puts some delay between + the CS manipulation. */ + SSP0_FLUSH_RX_FIFO; + + /* Wait until the flash device has the WEL bit on. */ + while ((LPC_SSP0->DR & 0x2) == 0) { + FLASH_CS_ASSERT; + FLASH_SHORT_COMMAND(FLASH_RDSR); + SSP0_FLUSH_RX_FIFO; + SSP0_WRITE_BYTE(0); + while (SSP0_IS_BUSY); + FLASH_CS_DEASSERT; + } + + FLASH_CS_ASSERT; + FLASH_LONG_COMMAND(FLASH_PP, page); + + LPC_GPDMA->DMACIntTCClear = 0x1; + LPC_GPDMA->DMACSoftSReq = 0xC; + + /* Prep Channel0 to send the buffer to the flash device. */ + LPC_GPDMACH0->DMACCSrcAddr = (uint32_t)flash_page_write_buffer; + LPC_GPDMACH0->DMACCDestAddr = (uint32_t)&LPC_SSP0->DR; + LPC_GPDMACH0->DMACCLLI = 0; + LPC_GPDMACH0->DMACCControl = DMA_CHANNEL_TCIE | DMA_CHANNEL_SRC_INC | (uint32_t)FLASH_PAGE_SIZE; + + /* Enable SSP0 DMA. */ + LPC_SSP0->DMACR = 0x2; + + /* Enable Channel0 */ + LPC_GPDMACH0->DMACCConfig = DMA_CHANNEL_ENABLE | + DMA_CHANNEL_DST_PERIPHERAL_SSP0_TX | + DMA_TRANSFER_TYPE_M2P | + DMA_MASK_IE | + DMA_MASK_ITC; + + + /* SSP0 CS line and "page_write_in_progress" flag are now + under DMA/SSP0 interrupt control. See ISR handlers for + more information. */ + + return 1; +} + +/** _flash_write_timer_callback + * + * RIT timer callback. + * + * After the write operation is complete this callback + * is used to examine the WIP flag in the flash status + * register. If it's still set then we reset the timer + * and try again in the future. If it's clear then we + * can mark the process as complete. + * + * @param int index The index of the timer the RIT modulr used. + */ +void _flash_write_timer_callback(int index) { + uint32_t sr = 1; + + /* Read the WIP flag from the flash device status + register and if the write cycle is complete mark + the operation as complete. Otherwise, reset the + timer to test again in the future. */ + FLASH_CS_ASSERT; + FLASH_SHORT_COMMAND(FLASH_RDSR); + SSP0_WRITE_BYTE(0); + while (SSP0_IS_BUSY); + FLASH_CS_DEASSERT; + while(LPC_SSP0->SR & (1UL << 2)) { + /* This loop ensures we read the last byte in the + RX FIFO and test that. */ + sr = LPC_SSP0->DR; + } + if (sr & 0x1) { + if (sector_erase_in_progress) rit_timer_set_counter(index, 100); + else rit_timer_set_counter(index, FLASH_WIP_TEST_TIME); + } + else { + FLASH_CS_ASSERT; + FLASH_SHORT_COMMAND(FLASH_WRDI); + SSP0_FLUSH_RX_FIFO; + FLASH_CS_DEASSERT; + if (sector_erase_in_progress) sector_erase_in_progress = false; + if (page_write_in_progress) page_write_in_progress = false; + SSP0_release(); + } +} + +/** flash_write_dma0_irq + * + * DMA transfer irq callback. + */ +int flash_write_dma0_irq(int channel) { + int rval = 0; + + /* If we were using DMA to transfer our buffer to the flash + device then mark the buffer as "released" and no longer + in use, release the DMA channel and start the "detect WIP + indicates complete" timer. */ + if (page_write_buffer_in_use) { + page_write_buffer_in_use = false; + LPC_GPDMACH0->DMACCConfig = 0; + DMA_release_channel(0); + LPC_SSP0->IMSC = (1UL << 3); + rit_timer_set_counter(FLASH_WRITE_CB, FLASH_WIP_TEST_TIME); + rval = 1; + } + + return rval; +} + +/** flash_read_ssp0_irq + * + * Called by the SSP0 ISR handler. + */ +int flash_write_ssp0_irq(void) { + if (page_write_in_progress) { + if (LPC_SSP0->MIS & (1UL << 3)) { + LPC_SSP0->IMSC &= ~(1UL << 3); + while(SSP0_IS_BUSY); + FLASH_CS_DEASSERT; + SSP0_release(); + return 1; + } + } + return 0; +} + +