openfirmware/dev/flashwrite.fth
Mitch Bradley 40e1101e39 Big revision of the FLASH infrastructure so it's easier to
integrate FLASH write drivers into the system.  For OLPC,
there is no functional difference at the top level, but
the internal arrangement of the FLASH device node subtree
has changed.  It's now possible to compile an OLPC ROM
that can save configuration variables in an SPI FLASH
block (see config.fth:use-flash-nvram).  For the lxdevel
build, that option is now chosen by default.

git-svn-id: svn://coreboot.org/openfirmware@838 1552c027-8020-0410-b4b5-a757f869b4ce
2008-06-24 23:14:57 +00:00

105 lines
3.8 KiB
Forth

0 [if]
\ Write support is complicated by the need to erase before
\ writing and the possibly-different erase and write granularity.
\
\ For NOR FLASH, where you can write as many times as you want
\ while turning 1's into 0's, the algorithm is:
\
\ Break the entire write range into pieces each contained in one
\ erase unit. For each piece:
\
\ Compare the existing and new contents to see if the unit needs erasing
\
\ If no bits need to go from 0 to 1, erase is unnecessary, so just write.
\ (It's a little more complicated if the write granularity is >1 byte.)
\
\ Otherwise, copy the existing contents of the erase unit to a buffer,
\ merge in the new data, erase, then write back the buffer.
[then]
\ dev /flash
[ifndef] flash-write-enable
also forth definitions
defer flash-write-enable ( -- )
defer flash-write-disable ( -- )
defer flash-erase-block ( offset -- )
defer flash-write ( adr len offset -- )
defer flash-read ( adr len offset -- )
defer flash-verify ( adr len offset -- )
h# 10.0000 value /flash
h# 10000 value /flash-block
previous definitions
[then]
: left-in-block ( len offset -- #left )
\ Determine how many bytes are left in the page containing offset
/flash-block swap /flash-block 1- and - ( len left-in-page )
min ( #left )
;
: must-erase? ( adr len -- flag )
device-base seek-ptr + ( adr len dev-adr )
swap 0 ?do ( adr dev-adr )
over i + c@ over i + c@ ( adr dev-adr new-byte old-byte )
\ Must erase if a bit in old-byte is 0 and that bit in new-byte is 1
invert and if ( adr dev-adr )
2drop true unloop exit
then ( adr dev-adr )
loop ( adr dev-adr )
2drop false
;
: erase+write ( adr len -- )
dup /flash-block = if
\ If we are going to overwrite the entire block, there's no need to
\ preserve the old data. This can only happen if we are already
\ aligned on an erase block boundary.
seek-ptr flash-erase-block ( adr len )
seek-ptr flash-write ( )
else
\ Allocate a buffer to save the old block contents
/flash-block alloc-mem >r ( adr len )
seek-ptr /flash-block round-down ( adr len block-start )
\ Copy existing data from FLASH block to the buffer
dup device-base + r@ /flash-block lmove ( adr len block-start )
\ Merge new bytes into the buffer
-rot ( block-start adr len )
seek-ptr /flash-block mod ( block-start adr len buf-offset )
r@ + swap move ( block-start )
\ Erase the block and rewrite it from the buffer
dup flash-erase-block ( block-start )
r@ /flash-block rot flash-write ( )
\ Release the buffer
r> /flash-block free-mem
then
;
: handle-block ( adr len -- adr' len' )
dup seek-ptr left-in-block ( adr len #left )
>r ( adr len r: #left )
over r@ must-erase? if ( adr len r: #left )
over r@ erase+write ( adr len r: #left )
else ( adr len r: #left )
over r@ seek-ptr flash-write ( adr len r: #left )
then ( adr len r: #left )
seek-ptr r@ + to seek-ptr ( adr len r: #left )
r> /string ( adr' len' )
;
: write ( adr len -- #written )
flash-write-enable
tuck ( len adr len )
begin dup while handle-block repeat ( len adr' remain' )
2drop ( len )
flash-write-disable
;
\ dend