/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See the file COPYING * * 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. * * Copyright (C) 2010 Frank van Maarseveen */ #include #include #include "common.h" #include "types.h" #include "bits.h" #include "spm.h" #if PAGE_SIZE != SPM_PAGESIZE # error PAGE_SIZE and SPM_PAGESIZE not identical. #endif #if NPAGES != FLASHEND / SPM_PAGESIZE + 1 # error NPAGES and FLASHEND disagree. #endif #if PROGRAM_BASE % PAGE_SIZE # error PROGRAM_BASE must be PAGE_SIZE aligned. #endif #define PROGRAM_PAGE0 (PROGRAM_BASE >> PAGE_SHIFT) #define MAXPAGES (NPAGES - PROGRAM_PAGE0) /* * Ram is reused by the program, flash not. So, try to save code at the * expense of ram as much as possible. The array below is a factor 8 * larger than necessary since it uses only one bit per byte but it * tends to fit. */ static bool_t dirty[MAXPAGES]; /* * The first packet must be used to determine which pages are going to be * reprogrammed: they will all be marked dirty at start. spm_program() will * reprogram dirty pages only to avoid wear and mark them clear. spm_complete() * will tell if there's anything left to do. */ bool_t spm_setup(struct spm *spm) { uint8_t pn; if (spm->npages > MAXPAGES) return false; for (pn = 0; pn < spm->npages; ++pn) dirty[pn] = true; return true; } /* * Note: interrupts are off for a loong time. */ void spm_program(struct spm *spm) { uint8_t sreg, i, pn; uint16_t dst, w; uint8_t *src; /* * Check the page number and whether or not the page itself still * needs reprogramming. Mark it clean in advance while we're at it. */ if (spm->pageno < PROGRAM_PAGE0) return; pn = spm->pageno - PROGRAM_PAGE0; if (pn >= MAXPAGES || !dirty[pn]) return; dirty[pn] = false; /* * Reprogram the page. */ src = spm->data; dst = (uint16_t)spm->pageno << PAGE_SHIFT; saveicli(sreg); boot_page_erase(dst); for (i = 0; i < PAGE_SIZE; i += 2) { w = *(uint16_t *)(src + i); // w = ((uint16_t)src[i + 1] << 8) | src[i]; boot_page_fill(dst | i, w); } boot_page_write(dst); boot_spm_busy_wait(); restorei(sreg); } /* * Return true when all pages have been reprogrammed. */ bool_t spm_complete(void) { uint8_t pn; for (pn = 0; pn < MAXPAGES; ++pn) { if (dirty[pn]) return false; } return true; }