summaryrefslogtreecommitdiff
path: root/src/flash.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/flash.c')
-rw-r--r--src/flash.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/src/flash.c b/src/flash.c
new file mode 100644
index 0000000..f70dde1
--- /dev/null
+++ b/src/flash.c
@@ -0,0 +1,292 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */
+/* Artur Pundsack, Pitronics. */
+
+/* This program 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 2 */
+/* of the License, or (at your option) any later version. */
+
+/* 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. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+/* Artur Pundsack can be reached by email: ap@pa-tec.de */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include "sp12.h"
+
+/* Accepts a 16-bit address. Reads the codeWord in two steps */
+/* from the device connected to the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - The adresswidth has been increased from */
+/* 16 to 32 bits (int -> long), */
+/* - Extended addressing support has been added */
+
+unsigned int readCodeWord(unsigned long address) {
+
+ unsigned char readByte;
+ unsigned int codeWord;
+ unsigned long readCmd;
+ unsigned long writeCmd;
+
+ /* For large ATMegas like ATMega2561 an extended address has to */
+ /* be set when crossing the 64k boundary. */
+ if (address > 0x0000FFFF) {
+ writeCmd = LOAD_EXT_ADDR | ((address & 0x00FF0000) >> 8);
+ clockOutCommand(writeCmd);
+ }
+
+ codeWord = 0;
+ readCmd = READ_PROG_HI | ((address & 0x0000FFFF) << 8);
+ readByte = (unsigned char) clockOutCommand(readCmd);
+ codeWord = codeWord | readByte;
+ readCmd = READ_PROG_LO | ((address & 0x0000FFFF) << 8);
+ readByte = (unsigned char) clockOutCommand(readCmd);
+ codeWord = (codeWord << 8) | readByte;
+ return(codeWord);
+}
+
+/* Accepts a 16-bit codeWord and a 32-bit address. Writes the code */
+/* in two steps with no delays into the ATMega device connected to */
+/* the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - The adresswidth has been increased from */
+/* 16 to 32 bits (int -> long), */
+/* - Extended addressing support has been added */
+void loadCodePage(unsigned long address, unsigned int codeWord) {
+
+ unsigned char loByte;
+ unsigned char hiByte;
+ unsigned long writeCmd;
+
+ loByte = (unsigned char) codeWord & 0x00FF;
+ hiByte = (unsigned char) ((codeWord & 0xFF00) >> 8);
+
+ /* For large ATMegas like ATMega2561 an extended address has to */
+ /* be set once for the first page and when crossing the 64k */
+ /* boundary. */
+ if ((ExtAddr != 1) && (address > 0x0000FFFF)) {
+ writeCmd = LOAD_EXT_ADDR | ((address & 0x00FF0000) >> 8);
+ clockOutCommand(writeCmd);
+ ExtAddr = 1; /* The 64k boundary has been detected */
+ }
+
+ writeCmd = LOAD_PAGE_LO | ((address & 0x000000FF) << 8);
+ writeCmd = writeCmd | loByte;
+ clockOutCommand(writeCmd);
+ writeCmd = LOAD_PAGE_HI | ((address & 0x000000FF) << 8);
+ writeCmd = writeCmd | hiByte;
+ clockOutCommand(writeCmd);
+}
+
+/* Accepts a 16-bit page number and programs it into the ATMega */
+/* device connected to the Centronics parallel port. */
+/* The page is read back and compared with the buffer; if all */
+/* is well, 0 is returned, else 1. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeCodePage(unsigned int flashBuf[], unsigned long address) {
+
+ unsigned long writeCmd;
+ unsigned int idx;
+ unsigned char readByte;
+ unsigned char testByte;
+
+ writeCmd = WRITE_PAGE | ((address & 0x0000FFFF) << 8);
+ clockOutCommand(writeCmd);
+ if (POLL_RDY_BSY) { /* time-out 64ms */
+ testByte = (unsigned char) ~POLL_RDY_BSY;
+ for (idx = 0; idx < 64; idx++) {
+ delay(1);
+ readByte = (unsigned char) clockOutCommand(POLL_RDY_BSY);
+ if ((readByte & testByte) == 0)
+ break;
+ }
+ } else {
+ loopDelay(timeConst.pageWrite);
+ }
+ for (idx = address + 1 - device.pageSize; idx <= address; idx++) {
+ if (flashBuf[idx] != 0xffff) {
+ if (readCodeWord(idx) != flashBuf[idx])
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/* Accepts a 16-bit codeWord and a 16-bit address. Writes the code */
+/* in two steps into the device connected to the Centronics port and */
+/* verifies arrival. Returns 1 if verify fails three times, else 0. */
+/* The time constant byteWrite is adjusted dynamically, */
+/* if the device is not a 1200(A) and optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: This function is only available for non page mode!*/
+
+int writeFlashVerified(unsigned int address, unsigned int codeWord,
+ int optimizeFlag) {
+
+ unsigned char loByte;
+ unsigned char hiByte;
+ unsigned long readCmd, writeCmd;
+ int failure;
+
+ loByte = (unsigned char) codeWord & 0x00FF;
+ hiByte = (unsigned char) ((codeWord & 0xFF00) >> 8);
+
+ writeCmd = WRITE_PROG_HI | ((long) address << 8);
+ writeCmd = writeCmd | hiByte;
+ readCmd = READ_PROG_HI | ((long) address << 8);
+ failure = writeByteVerified(writeCmd, readCmd, optimizeFlag);
+ if (failure == 0) {
+ writeCmd = WRITE_PROG_LO | ((long) address << 8);
+ writeCmd = writeCmd | loByte;
+ readCmd = READ_PROG_LO | ((long) address << 8);
+ failure = writeByteVerified(writeCmd, readCmd, optimizeFlag);
+ }
+ return(failure);
+}
+
+/* Expects flashBuf[] to be filled with data. */
+/* If a (page) write fails, 1 is returned, else 0. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - Added reset ext. address marker */
+/* - Set the extended adress byte if necessary */
+
+int writeFlashArea(unsigned int flashBuf[], long bufLimit,
+ int optimizeFlag) {
+
+ long address, previous, writeCmd;
+ int writeFlg = 0;
+ int idx = 1;
+
+ previous = 0;
+ printf("...............................................................\r");
+ if (optimizeFlag != 11)
+ timeConst.pageWriteAdjusted = 1;
+
+ /* AP, 15.01.2008: Added, Reset the extended address marker and */
+ /* set the extended adress byte if neccessary. */
+ ExtAddr = 0;
+ if (device.flashLimit > 0x0000FFFF){
+ writeCmd = LOAD_EXT_ADDR;
+ clockOutCommand(writeCmd);
+ }
+
+ for (address = 0; address < bufLimit; address++) {
+ if (device.pageMode) {
+ /*
+ * To speed things up: Skip locations containing 0xffff
+ * (The device has been erased before writing started)
+ */
+ if (flashBuf[address] != 0xffff) {
+ loadCodePage(address, flashBuf[address]);
+ writeFlg = 1;
+ }
+ if (writeFlg && (address > 0) &&
+ ((address & (device.pageSize - 1)) == device.pageSize - 1)) {
+ writeFlg = 0;
+ if (writeCodePage(flashBuf, address) == 1) {
+ if (++idx < 6) {
+ /*
+ * recalculate first address of the failed page
+ * once only. Note: The calculation below puts
+ * address at the last address value of the previous
+ * page. But address is incremented _after_ this
+ * calculation, _before_ loadCodePage is called
+ * again.
+ */
+ if (idx == 2) address = address - device.pageSize;
+ if (timeConst.pageWriteAdjusted == 1) writeRetries++;
+ } else {
+ printf("!!\n");
+ return (1);
+ }
+ if (optimizeFlag == 11) {
+ timeConst.pageWrite += timeConst.pageWrite / 2;
+ if (timeConst.pageWrite > 1.5 * timeConst.pageWriteDefault) {
+ printf("!!\n");
+ return (1);
+ }
+ timeConst.pageWriteAdjusted = 1;
+ }
+ } else {
+ if (optimizeFlag == 11 && timeConst.pageWriteAdjusted == 0) {
+ timeConst.pageWrite -= timeConst.pageWrite / 2;
+ }
+ idx = 1;
+ }
+ }
+ } else {
+ if (flashBuf[address] != 0xffff) {
+ if (writeFlashVerified((unsigned int) address,
+ flashBuf[address], optimizeFlag) == 1) {
+ printf("!!\n");
+ return (1);
+ }
+ }
+ }
+ if (address >= (previous + bufLimit / 64)) {
+ putchar('o');
+ fflush(stdout);
+ previous = address;
+ }
+ }
+
+ /*
+ * In page mode, a sequence like pageWrite reducing from 64ms
+ * to 32, 16, 8 and 4ms, then adjusting back upwards to 6ms
+ * and finally 9ms is valid; this would cause writeRetries
+ * to be incremented once. Therefore:
+ */
+ if (device.pageMode && writeRetries == 1)
+ writeRetries = 0;
+ printf("\n");
+ return (0);
+}
+
+/* (expects flashBuf[] to be filled with 0xFFFF) */
+/* Loops readCodeWord into flashBuf[] */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - Set the extended adress byte if necessary */
+
+void readFlashArea(unsigned int flashBuf[], long bufLimit) {
+
+ long address, previous, writeCmd;
+
+ previous = 0;
+ printf("...............................................................\r");
+
+ if (device.flashLimit > 0x0000FFFF){
+ writeCmd = LOAD_EXT_ADDR;
+ clockOutCommand(writeCmd);
+ }
+
+ for (address = 0; address < bufLimit; address++) {
+ flashBuf[address] = readCodeWord(address);
+ if (address >= (previous + bufLimit / 64)) {
+ putchar('o');
+ fflush(stdout);
+ previous = address;
+ }
+ }
+ printf("\n");
+}