loader: flash locking commands, various flash rework
authorIngo Albrecht <prom@berlin.ccc.de>
Sun, 11 Apr 2010 05:26:42 +0000 (07:26 +0200)
committerIngo Albrecht <prom@berlin.ccc.de>
Tue, 20 Jul 2010 12:41:19 +0000 (14:41 +0200)
src/host/osmocon/osmoload.c
src/target/firmware/apps/loader/main.c
src/target/firmware/apps/loader/protocol.h
src/target/firmware/flash/cfi_flash.c
src/target/firmware/include/cfi_flash.h

index ef98a35..ef00c3d 100644 (file)
@@ -66,25 +66,26 @@ static struct {
        /* quit flag for main loop */
        unsigned char quit;
 
-       /* state machine */
+       /* main state machine */
        int state;
 
        /* pending query command */
        uint8_t command;
+
+       /* general timeout */
        struct timer_list timeout;
 
        /* binary i/o for firmware images */
        FILE *binfile;
-
        /* buffer containing binfile data */
        char *binbuf;
 
        /* memory operation state */
-       uint32_t membase;
-       uint32_t memlen;
-       uint32_t memoff;
-       uint16_t memcrc;
-       uint16_t memreq;
+       uint32_t membase; /* target base address of operation */
+       uint32_t memlen;  /* length of entire operation */
+       uint32_t memoff;  /* offset for next request */
+       uint16_t memcrc;  /* crc for current request */
+       uint16_t memreq;  /* length of current request */
 } osmoload;
 
 static int usage(const char *name)
@@ -94,6 +95,11 @@ static int usage(const char *name)
        puts("  memput <hex-address> <hex-bytes>         - Poke at memory");
        puts("  memdump <hex-address> <hex-length> <file>- Dump memory to file");
        puts("  memload <hex-address> <file>             - Load file into memory");
+       puts("  finfo                                    - Information about flash chips");
+       puts("  funlock <chip> <address>                 - Unlock flash block");
+       puts("  flock <chip> <address>                   - Lock flash block");
+       puts("  flockdown <chip> <address>               - Lock down flash block");
+       puts("  fgetlock <chip> <address>                - Get locking state of block");
        puts("  jump <hex-address>                       - Jump to address");
        puts("  jumpflash                                - Jump to flash loader");
        puts("  jumprom                                  - Jump to rom loader");
@@ -187,6 +193,36 @@ static void memop_timeout(void *dummy) {
        return;
 }
 
+static void
+loader_dump_flash_info(struct msgb *msg) {
+       uint8_t nchips;
+
+       nchips = msgb_get_u8(msg);
+
+       int chip;
+       for(chip = 0; chip < nchips; chip++) {
+
+               uint32_t address;
+               address = msgb_get_u32(msg);
+
+               uint32_t chipsize;
+               chipsize = msgb_get_u32(msg);
+
+               uint8_t nregions;
+               nregions = msgb_get_u8(msg);
+
+               printf("chip %d at 0x%8.8x of %d bytes in %d regions\n", chip, address, chipsize, nregions);
+
+               int region;
+               for(region = 0; region < nregions; region++) {
+                       uint16_t c = msgb_get_u32(msg);
+                       uint32_t s = msgb_get_u32(msg);
+
+                       printf("  region %d with %d blocks of %d bytes each\n", region, c, s);
+               }
+       }
+}
+
 static void
 loader_handle_reply(struct msgb *msg) {
        if(osmoload.print_replies) {
@@ -196,10 +232,12 @@ loader_handle_reply(struct msgb *msg) {
 
        uint8_t cmd = msgb_get_u8(msg);
 
+       uint8_t chip;
        uint8_t length;
        uint16_t crc;
        uint32_t address;
        uint32_t entrypoint;
+       uint32_t status;
 
        void *data;
 
@@ -217,6 +255,7 @@ loader_handle_reply(struct msgb *msg) {
                break;
        case LOADER_MEM_READ:
                length = msgb_get_u8(msg);
+               crc = msgb_get_u16(msg);
                address = msgb_get_u32(msg);
                data = msgb_get(msg, length);
                break;
@@ -228,6 +267,17 @@ loader_handle_reply(struct msgb *msg) {
        case LOADER_JUMP:
                address = msgb_get_u32(msg);
                break;
+       case LOADER_FLASH_INFO:
+               break;
+       case LOADER_FLASH_GETLOCK:
+       case LOADER_FLASH_ERASE:
+       case LOADER_FLASH_UNLOCK:
+       case LOADER_FLASH_LOCK:
+       case LOADER_FLASH_LOCKDOWN:
+               chip = msgb_get_u8(msg);
+               address = msgb_get_u32(msg);
+               status = msgb_get_u32(msg);
+               break;
        default:
                printf("Received unknown reply %d:\n", cmd);
                hexdump(msg->data, msg->len);
@@ -264,6 +314,32 @@ loader_handle_reply(struct msgb *msg) {
                case LOADER_JUMP:
                        printf("Confirmed jump to 0x%x.\n", address);
                        break;
+               case LOADER_FLASH_ERASE:
+                       printf("Confirmed flash erase of chip %d address 0x%8.8x, status %s\n",
+                                  chip, address, status ? "FAILED" : "ok");
+                       break;
+               case LOADER_FLASH_GETLOCK:
+                       printf("Lock state of chip %d address 0x%8.8x is %s\n",
+                                  chip, address, (status == LOADER_FLASH_LOCKED ? "locked"
+                                                                  : (status == LOADER_FLASH_LOCKED_DOWN ? "locked down"
+                                                                         : (status == LOADER_FLASH_UNLOCKED ? "unlocked"
+                                                                                : "UNKNOWN"))));
+                       break;
+               case LOADER_FLASH_UNLOCK:
+                       printf("Confirmed flash unlock of chip %d address 0x%8.8x, status %s\n",
+                                  chip, address, status ? "FAILED" : "ok");
+                       break;
+               case LOADER_FLASH_LOCK:
+                       printf("Confirmed flash lock of chip %d address 0x%8.8x, status %s\n",
+                                  chip, address, status ? "FAILED" : "ok");
+                       break;
+               case LOADER_FLASH_LOCKDOWN:
+                       printf("Confirmed flash lockdown of chip %d address 0x%8.8x, status %s\n",
+                                  chip, address, status ? "FAILED" : "ok");
+                       break;
+               case LOADER_FLASH_INFO:
+                       loader_dump_flash_info(msg);
+                       break;
                default:
                        break;
                }
@@ -275,6 +351,13 @@ loader_handle_reply(struct msgb *msg) {
                break;
        case STATE_DUMP_IN_PROGRESS:
                if(cmd == LOADER_MEM_READ) {
+                       osmoload.memcrc = crc16(0, data, length);
+                       if(osmoload.memcrc != crc) {
+                               osmoload.memoff -= osmoload.memreq;
+                               printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff);
+                       } else {
+                               putchar('.');
+                       }
                        loader_do_memdump(data, length);
                }
                break;
@@ -282,7 +365,9 @@ loader_handle_reply(struct msgb *msg) {
                if(cmd == LOADER_MEM_WRITE) {
                        if(osmoload.memcrc != crc) {
                                osmoload.memoff -= osmoload.memreq;
-                               printf("bad CRC %x\n", crc);
+                               printf("\nbad crc %4.4x (not %4.4x) at offset 0x%8.8x", crc, osmoload.memcrc, osmoload.memoff);
+                       } else {
+                               putchar('.');
                        }
                        loader_do_memload();
                }
@@ -290,6 +375,8 @@ loader_handle_reply(struct msgb *msg) {
        default:
                break;
        }
+
+       fflush(stdout);
 }
 
 static int
@@ -376,6 +463,19 @@ loader_send_query(uint8_t command) {
        osmoload.command = command;
 }
 
+static void
+loader_send_flash_query(uint8_t command, uint8_t chip, uint32_t address) {
+       struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
+       msgb_put_u8(msg, command);
+       msgb_put_u8(msg, chip);
+       msgb_put_u32(msg, address);
+       loader_send_request(msg);
+       msgb_free(msg);
+
+       osmoload.state = STATE_QUERY_PENDING;
+       osmoload.command = command;
+}
+
 static void
 loader_send_memget(uint8_t length, uint32_t address) {
        struct msgb *msg = msgb_alloc(MSGB_MAX, "loader");
@@ -438,6 +538,7 @@ loader_do_memdump(void *data, size_t length) {
                        rc = fwrite(p, 1, c, osmoload.binfile);
                        if(ferror(osmoload.binfile)) {
                                printf("Could not read from file: %s\n", strerror(errno));
+                               exit(1);
                        }
                        c -= rc;
                        p += rc;
@@ -555,6 +656,7 @@ loader_start_memload(uint32_t address, char *file) {
                rc = fread(p, 1, c, osmoload.binfile);
                if(ferror(osmoload.binfile)) {
                        printf("Could not read from file: %s\n", strerror(errno));
+                       exit(1);
                }
                c -= rc;
                p += rc;
@@ -598,6 +700,68 @@ loader_command(char *name, int cmdc, char **cmdv) {
                loader_send_query(LOADER_ENTER_ROM_LOADER);
        } else if(!strcmp(cmd, "jumpflash")) {
                loader_send_query(LOADER_ENTER_FLASH_LOADER);
+       } else if(!strcmp(cmd, "flashinfo")) {
+               loader_send_query(LOADER_FLASH_INFO);
+       } else if(!strcmp(cmd, "flashgetlock")) {
+               uint8_t chip;
+               uint32_t address;
+
+               if(cmdc < 3) {
+                       usage(name);
+               }
+
+               chip = strtoul(cmdv[1], NULL, 10);
+               address = strtoul(cmdv[2], NULL, 16);
+
+               loader_send_flash_query(LOADER_FLASH_GETLOCK, chip, address);
+       } else if(!strcmp(cmd, "flashunlock")) {
+               uint8_t chip;
+               uint32_t address;
+
+               if(cmdc < 3) {
+                       usage(name);
+               }
+
+               chip = strtoul(cmdv[1], NULL, 10);
+               address = strtoul(cmdv[2], NULL, 16);
+
+               loader_send_flash_query(LOADER_FLASH_UNLOCK, chip, address);
+       } else if(!strcmp(cmd, "flashlock")) {
+               uint8_t chip;
+               uint32_t address;
+
+               if(cmdc < 3) {
+                       usage(name);
+               }
+
+               chip = strtoul(cmdv[1], NULL, 10);
+               address = strtoul(cmdv[2], NULL, 16);
+
+               loader_send_flash_query(LOADER_FLASH_LOCK, chip, address);
+       } else if(!strcmp(cmd, "flashlockdown")) {
+               uint8_t chip;
+               uint32_t address;
+
+               if(cmdc < 3) {
+                       usage(name);
+               }
+
+               chip = strtoul(cmdv[1], NULL, 10);
+               address = strtoul(cmdv[2], NULL, 16);
+
+               loader_send_flash_query(LOADER_FLASH_LOCKDOWN, chip, address);
+       } else if(!strcmp(cmd, "flasherase")) {
+               uint8_t chip;
+               uint32_t address;
+
+               if(cmdc < 3) {
+                       usage(name);
+               }
+
+               chip = strtoul(cmdv[1], NULL, 10);
+               address = strtoul(cmdv[2], NULL, 16);
+
+               loader_send_flash_query(LOADER_FLASH_ERASE, chip, address);
        } else if(!strcmp(cmd, "memput")) {
                uint32_t address;
 
@@ -690,7 +854,7 @@ loader_command(char *name, int cmdc, char **cmdv) {
 
        if(osmoload.state == STATE_QUERY_PENDING) {
                osmoload.timeout.cb = &query_timeout;
-               bsc_schedule_timer(&osmoload.timeout, 0, 500000);
+               bsc_schedule_timer(&osmoload.timeout, 0, 5000000);
        }
        if(osmoload.state == STATE_LOAD_IN_PROGRESS) {
                osmoload.timeout.cb = &memop_timeout;
index a3aefa8..d1fdb31 100644 (file)
@@ -48,8 +48,7 @@
 #include <calypso/uart.h>
 #include <calypso/timer.h>
 
-#include <layer1/sync.h>
-#include <layer1/tpu_window.h>
+#include <cfi_flash.h>
 
 #include "protocol.h"
 
@@ -95,11 +94,7 @@ static void device_jump(void *entry) {
 }
 
 static void
-loader_send_simple(uint8_t dlci, uint8_t command) {
-       struct msgb *msg = sercomm_alloc_msgb(1);
-       if(!msg) {
-               puts("Failed to allocate message buffer!\n");
-       }
+loader_send_simple(struct msgb *msg, uint8_t dlci, uint8_t command) {
        msgb_put_u8(msg, command);
        sercomm_sendmsg(dlci, msg);
 }
@@ -108,16 +103,15 @@ extern unsigned char _start;
 
 static void
 loader_send_init(uint8_t dlci) {
-       struct msgb *msg = sercomm_alloc_msgb(1);
-       if(!msg) {
-               puts("Failed to allocate message buffer!\n");
-       }
+       struct msgb *msg = sercomm_alloc_msgb(9);
        msgb_put_u8(msg, LOADER_INIT);
        msgb_put_u32(msg, 0);
        msgb_put_u32(msg, &_start);
        sercomm_sendmsg(dlci, msg);
 }
 
+flash_t the_flash;
+
 int main(void)
 {
        /* Always disable wdt (some platforms enable it on boot) */
@@ -137,6 +131,11 @@ int main(void)
        puts("\n\nOSMOCOM Calypso loader (revision " GIT_REVISION ")\n");
        puts(hr);
 
+       /* Initialize flash driver */
+       if(flash_init(&the_flash, 0)) {
+               puts("Failed to initialize flash!\n");
+       }
+
        /* Identify environment */
        printf("Running on %s in environment %s\n", manifest_board, manifest_environment);
 
@@ -167,42 +166,45 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
 
        uint8_t command = msgb_get_u8(msg);
 
-       printf("command %u: ", command);
+       int res;
+
+       flash_lock_t lock;
 
+       uint8_t  chip;
        uint8_t  nbytes;
        uint16_t crc;
        uint32_t address;
 
-       struct msgb *reply;
+       struct msgb *reply = sercomm_alloc_msgb(256); // XXX
+
+       if(!reply) {
+               printf("Failed to allocate reply buffer!\n");
+               goto out;
+       }
 
        switch(command) {
 
        case LOADER_PING:
-               puts("ping\n");
-               loader_send_simple(dlci, LOADER_PING);
+               loader_send_simple(reply, dlci, LOADER_PING);
                break;
 
        case LOADER_RESET:
-               puts("reset\n");
-               loader_send_simple(dlci, LOADER_RESET);
+               loader_send_simple(reply, dlci, LOADER_RESET);
                device_reset();
                break;
 
        case LOADER_POWEROFF:
-               puts("poweroff\n");
-               loader_send_simple(dlci, LOADER_POWEROFF);
+               loader_send_simple(reply, dlci, LOADER_POWEROFF);
                device_poweroff();
                break;
 
        case LOADER_ENTER_ROM_LOADER:
-               puts("jump to rom loader\n");
-               loader_send_simple(dlci, LOADER_ENTER_ROM_LOADER);
+               loader_send_simple(reply, dlci, LOADER_ENTER_ROM_LOADER);
                device_enter_loader(1);
                break;
 
        case LOADER_ENTER_FLASH_LOADER:
-               puts("jump to flash loader\n");
-               loader_send_simple(dlci, LOADER_ENTER_FLASH_LOADER);
+               loader_send_simple(reply, dlci, LOADER_ENTER_FLASH_LOADER);
                device_enter_loader(0);
                break;
 
@@ -211,16 +213,11 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
                nbytes = msgb_get_u8(msg);
                address = msgb_get_u32(msg);
 
-               printf("mem read %u @ %p\n", nbytes, (void*)address);
-
-               reply = sercomm_alloc_msgb(6 + nbytes);
-
-               if(!reply) {
-                       printf("Failed to allocate reply buffer!\n");
-               }
+               crc = crc16(0, (void*)address, nbytes);
 
                msgb_put_u8(reply, LOADER_MEM_READ);
                msgb_put_u8(reply, nbytes);
+               msgb_put_u16(reply, crc);
                msgb_put_u32(reply, address);
 
                memcpy(msgb_put(reply, nbytes), (void*)address, nbytes);
@@ -235,27 +232,14 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
                crc = msgb_get_u16(msg);
                address = msgb_get_u32(msg);
 
-               printf("mem write %u @ %p\n", nbytes, (void*)address);
-
                void *data = msgb_get(msg, nbytes);
 
                uint16_t mycrc = crc16(0, data, nbytes);
 
-#if 0
-               printf("crc %x got %x\n", mycrc, crc);
-               hexdump(data, nbytes);
-#endif
-
                if(mycrc == crc) {
                        memcpy((void*)address, data, nbytes);
                }
 
-               reply = sercomm_alloc_msgb(8);
-
-               if(!reply) {
-                       printf("Failed to allocate reply buffer!\n");
-               }
-
                msgb_put_u8(reply, LOADER_MEM_WRITE);
                msgb_put_u8(reply, nbytes);
                msgb_put_u16(reply, mycrc);
@@ -269,29 +253,105 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
 
                address = msgb_get_u32(msg);
 
-               printf("jump to 0x%x\n", address);
+               msgb_put_u8(reply, LOADER_JUMP);
+               msgb_put_u32(reply, address);
+
+               sercomm_sendmsg(dlci, reply);
+
+               device_jump((void*)address);
+
+               break;
+
+       case LOADER_FLASH_INFO:
 
-               reply = sercomm_alloc_msgb(5);
+               msgb_put_u8(reply, LOADER_FLASH_INFO);
+               msgb_put_u8(reply, 1); // nchips
 
-               if(!reply) {
-                       printf("Failed to allocate reply buffer!\n");
+               // chip 1
+               msgb_put_u32(reply, the_flash.f_base);
+               msgb_put_u32(reply, the_flash.f_size);
+               msgb_put_u8(reply, the_flash.f_nregions);
+
+               int i;
+               for(i = 0; i < the_flash.f_nregions; i++) {
+                       msgb_put_u32(reply, the_flash.f_regions[i].fr_bnum);
+                       msgb_put_u32(reply, the_flash.f_regions[i].fr_bsize);
                }
 
-               msgb_put_u8(reply, LOADER_JUMP);
+               sercomm_sendmsg(dlci, reply);
+
+               break;
+
+       case LOADER_FLASH_ERASE:
+       case LOADER_FLASH_UNLOCK:
+       case LOADER_FLASH_LOCK:
+       case LOADER_FLASH_LOCKDOWN:
+
+               chip = msgb_get_u8(msg);
+               address = msgb_get_u32(msg);
+
+               if(command == LOADER_FLASH_ERASE) {
+                       res = flash_block_erase(&the_flash, address);
+               }
+               if(command == LOADER_FLASH_UNLOCK) {
+                       res = flash_block_unlock(&the_flash, address);
+               }
+               if(command == LOADER_FLASH_LOCK) {
+                       res = flash_block_lock(&the_flash, address);
+               }
+               if(command == LOADER_FLASH_LOCKDOWN) {
+                       res = flash_block_lockdown(&the_flash, address);
+               }
+
+               msgb_put_u8(reply, command);
+               msgb_put_u8(reply, chip);
                msgb_put_u32(reply, address);
+               msgb_put_u32(reply, (res != 0));
 
                sercomm_sendmsg(dlci, reply);
 
-               device_jump((void*)address);
+               break;
+
+       case LOADER_FLASH_GETLOCK:
+
+               chip = msgb_get_u8(msg);
+               address = msgb_get_u32(msg);
+
+               lock = flash_block_getlock(&the_flash, address);
+
+               msgb_put_u8(reply, command);
+               msgb_put_u8(reply, chip);
+               msgb_put_u32(reply, address);
+
+               switch(lock) {
+               case FLASH_UNLOCKED:
+                       msgb_put_u32(reply, LOADER_FLASH_UNLOCKED);
+                       break;
+               case FLASH_LOCKED:
+                       msgb_put_u32(reply, LOADER_FLASH_LOCKED);
+                       break;
+               case FLASH_LOCKED_DOWN:
+                       msgb_put_u32(reply, LOADER_FLASH_LOCKED_DOWN);
+                       break;
+               default:
+                       msgb_put_u32(reply, 0xFFFFFFFF);
+                       break;
+               }
+
+               sercomm_sendmsg(dlci, reply);
 
                break;
 
        default:
                printf("unknown command %d\n", command);
-               break;
 
+               msgb_free(reply);
+
+               break;
        }
 
+ out:
+
        msgb_free(msg);
 }
 
index c2ea25c..0a61c89 100644 (file)
@@ -1,12 +1,37 @@
 
 enum loader_command {
+       /* init message from loader */
        LOADER_INIT,
+
+       /* ping / pong */
        LOADER_PING,
+
+       /* lifecycle requests */
        LOADER_RESET,
        LOADER_POWEROFF,
+
+       /* jumps */
+       LOADER_JUMP,
        LOADER_ENTER_ROM_LOADER,
        LOADER_ENTER_FLASH_LOADER,
+
+       /* generic memory ops */
        LOADER_MEM_READ,
        LOADER_MEM_WRITE,
-       LOADER_JUMP,
+
+       /* flash operations */
+       LOADER_FLASH_INFO,
+       LOADER_FLASH_ERASE,
+       LOADER_FLASH_UNLOCK,
+       LOADER_FLASH_LOCK,
+       LOADER_FLASH_LOCKDOWN,
+       LOADER_FLASH_GETLOCK,
+       LOADER_FLASH_PROGRAM,
+
+};
+
+enum loader_flash_lock {
+       LOADER_FLASH_UNLOCKED = 0,
+       LOADER_FLASH_LOCKED,
+       LOADER_FLASH_LOCKED_DOWN,
 };
index a269142..624d143 100644 (file)
 #include <debug.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <errno.h>
 #include <memory.h>
 #include <cfi_flash.h>
 
 /* XXX: memdump_range() */
 #include <calypso/misc.h>
+#include <calypso/uart.h>
+#include <comm/sercomm.h>
+
+/* global definitions */
+#define CFI_FLASH_MAX_ERASE_REGIONS 4
+
+/* structure of erase region descriptor */
+struct cfi_region {
+       uint16_t b_count;
+       uint16_t b_size;
+} __attribute__((packed));
+
+/* structure of cfi query response */
+struct cfi_query {
+       uint8_t qry[3];
+       uint16_t        p_id;
+       uint16_t        p_adr;
+       uint16_t        a_id;
+       uint16_t        a_adr;
+       uint8_t vcc_min;
+       uint8_t vcc_max;
+       uint8_t vpp_min;
+       uint8_t vpp_max;
+       uint8_t word_write_timeout_typ;
+       uint8_t buf_write_timeout_typ;
+       uint8_t block_erase_timeout_typ;
+       uint8_t chip_erase_timeout_typ;
+       uint8_t word_write_timeout_max;
+       uint8_t buf_write_timeout_max;
+       uint8_t block_erase_timeout_max;
+       uint8_t chip_erase_timeout_max;
+       uint8_t dev_size;
+       uint16_t        interface_desc;
+       uint16_t        max_buf_write_size;
+       uint8_t num_erase_regions;
+       struct cfi_region  erase_regions[CFI_FLASH_MAX_ERASE_REGIONS];
+} __attribute__((packed));
+
+/* manufacturer ids */
+enum cfi_manuf {
+       CFI_MANUF_INTEL = 0x0089,
+};
+
+/* algorithm ids */
+enum cfi_algo {
+       CFI_ALGO_INTEL_3 = 0x03
+};
 
-enum flash_cmd {
-       FLASH_CMD_RESET         = 0xff,
-       FLASH_CMD_READ_ID       = 0x90,
-       FLASH_CMD_CFI           = 0x98,
-       FLASH_CMD_READ_STATUS   = 0x70,
-       FLASH_CMD_CLEAR_STATUS  = 0x50,
-       FLASH_CMD_WRITE         = 0x40,
-       FLASH_CMD_BLOCK_ERASE   = 0x20,
-       FLASH_CMD_ERASE_CONFIRM = 0xD0,
-       FLASH_CMD_PROTECT = 0x60,
+/* various command bytes */
+enum cfi_flash_cmd {
+       CFI_CMD_RESET           = 0xff,
+       CFI_CMD_READ_ID = 0x90,
+       CFI_CMD_CFI             = 0x98,
+       CFI_CMD_READ_STATUS     = 0x70,
+       CFI_CMD_CLEAR_STATUS    = 0x50,
+       CFI_CMD_WRITE           = 0x40,
+       CFI_CMD_BLOCK_ERASE     = 0x20,
+       CFI_CMD_ERASE_CONFIRM = 0xD0,
+       CFI_CMD_PROTECT = 0x60,
 };
 
+/* protection commands */
 enum flash_prot_cmd {
-       FLASH_PROT_LOCK = 0x01,
-       FLASH_PROT_UNLOCK = 0xD0,
-       FLASH_PROT_LOCKDOWN = 0x2F
+       CFI_PROT_LOCK = 0x01,
+       CFI_PROT_UNLOCK = 0xD0,
+       CFI_PROT_LOCKDOWN = 0x2F
 };
 
+/* offsets from base */
 enum flash_offset {
-       FLASH_OFFSET_MANUFACTURER_ID    = 0x00,
-       FLASH_OFFSET_DEVICE_ID          = 0x01,
-       FLASH_OFFSET_INTEL_PROTECTION   = 0x81,
-       FLASH_OFFSET_CFI_RESP           = 0x10
+       CFI_OFFSET_MANUFACTURER_ID      = 0x00,
+       CFI_OFFSET_DEVICE_ID            = 0x01,
+       CFI_OFFSET_INTEL_PROTECTION     = 0x81,
+       CFI_OFFSET_CFI_RESP             = 0x10
 };
 
+/* offsets from block base */
 enum flash_block_offset {
-       FLASH_OFFSET_BLOCK_LOCKSTATE = 0x02
+       CFI_OFFSET_BLOCK_LOCKSTATE = 0x02
 };
 
+/* status masks */
 enum flash_status {
-       FLASH_STATUS_READY = 0x80,
-       FLASH_STATUS_ERASE_SUSPENDED = 0x40,
-       FLASH_STATUS_ERASE_ERROR = 0x20,
-       FLASH_STATUS_PROGRAM_ERROR = 0x10,
-       FLASH_STATUS_VPP_LOW = 0x08,
-       FLASH_STATUS_PROGRAM_SUSPENDED = 0x04,
-       FLASH_STATUS_LOCKED_ERROR = 0x02,
-       FLASH_STATUS_RESERVED = 0x01
+       CFI_STATUS_READY = 0x80,
+       CFI_STATUS_ERASE_SUSPENDED = 0x40,
+       CFI_STATUS_ERASE_ERROR = 0x20,
+       CFI_STATUS_PROGRAM_ERROR = 0x10,
+       CFI_STATUS_VPP_LOW = 0x08,
+       CFI_STATUS_PROGRAM_SUSPENDED = 0x04,
+       CFI_STATUS_LOCKED_ERROR = 0x02,
+       CFI_STATUS_RESERVED = 0x01
 };
 
 static inline void flash_write_cmd(const void *base_addr, uint16_t cmd)
@@ -83,105 +136,158 @@ static char flash_protected(uint32_t block_offset) {
        return block_offset < 64*1024;
 }
 
-uint8_t flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset) {
+
+flash_lock_t flash_block_getlock(flash_t *flash, uint32_t block_offset) {
        const void *base_addr = flash->f_base;
+
        uint8_t lockstate;
-       flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
-       lockstate = flash_read16(base_addr, block_offset + FLASH_OFFSET_BLOCK_LOCKSTATE);
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
-       return lockstate;
+       flash_write_cmd(base_addr, CFI_CMD_READ_ID);
+       lockstate = flash_read16(base_addr, (block_offset>>1) + CFI_OFFSET_BLOCK_LOCKSTATE);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
+
+       if(lockstate & 0x2) {
+               return FLASH_LOCKED_DOWN;
+       } else if(lockstate & 0x01) {
+               return FLASH_LOCKED;
+       } else {
+               return FLASH_UNLOCKED;
+       }
 }
 
-void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset) {
+int flash_block_unlock(flash_t *flash, uint32_t block_offset) {
        const void *base_addr = flash->f_base;
-       printf("Unlocking block at 0x%08x\n", block_offset);
+
+       if(block_offset >= flash->f_size) {
+               return -EINVAL;
+       }
 
        if(flash_protected(block_offset)) {
-               puts("error: block is soft-protected\n");
-               return;
+               return -EPERM;
        }
 
-       flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
-       flash_write_cmd(base_addr + block_offset, FLASH_PROT_UNLOCK);
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+       printf("Unlocking block at 0x%08x, meaning %08x\n", block_offset, base_addr + block_offset);
+
+       flash_write_cmd(base_addr, CFI_CMD_PROTECT);
+       flash_write_cmd(base_addr + block_offset, CFI_PROT_UNLOCK);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
+
+       return 0;
 }
 
-void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset) {
+int flash_block_lock(flash_t *flash, uint32_t block_offset) {
        const void *base_addr = flash->f_base;
+
+       if(block_offset >= flash->f_size) {
+               return -EINVAL;
+       }
+
        printf("Locking block at 0x%08x\n", block_offset);
-       flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
-       flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCK);
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+
+       flash_write_cmd(base_addr, CFI_CMD_PROTECT);
+       flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCK);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
+
+       return 0;
 }
 
-void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset) {
+int flash_block_lockdown(flash_t *flash, uint32_t block_offset) {
        const void *base_addr = flash->f_base;
+
+       if(block_offset >= flash->f_size) {
+               return -EINVAL;
+       }
+
        printf("Locking down block at 0x%08x\n", block_offset);
-       flash_write_cmd(base_addr, FLASH_CMD_PROTECT);
-       flash_write_cmd(base_addr + block_offset, FLASH_PROT_LOCKDOWN);
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+
+       flash_write_cmd(base_addr, CFI_CMD_PROTECT);
+       flash_write_cmd(base_addr + block_offset, CFI_PROT_LOCKDOWN);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
+
+       return 0;
 }
 
-void flash_block_erase(cfi_flash_t *flash, uint32_t block_offset) {
+int flash_block_erase(flash_t *flash, uint32_t block_offset) {
        const void *base_addr = flash->f_base;
-       printf("Erasing block 0x%08x...", block_offset);
+
+       if(block_offset >= flash->f_size) {
+               return -EINVAL;
+       }
 
        if(flash_protected(block_offset)) {
-               puts("error: block is soft-protected\n");
-               return;
+               return -EPERM;
        }
 
+       printf("Erasing block 0x%08x...", block_offset);
+
        void *block_addr = ((uint8_t*)base_addr) + block_offset;
 
-       flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS);
+       flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
 
-       flash_write_cmd(block_addr, FLASH_CMD_BLOCK_ERASE);
-       flash_write_cmd(block_addr, FLASH_CMD_ERASE_CONFIRM);
+       flash_write_cmd(block_addr, CFI_CMD_BLOCK_ERASE);
+       flash_write_cmd(block_addr, CFI_CMD_ERASE_CONFIRM);
 
-       flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS);
+       flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
        uint16_t status;
        do {
                status = flash_read16(base_addr, 0);
-       } while(!(status&FLASH_STATUS_READY));
+       } while(!(status&CFI_STATUS_READY));
 
-       if(status&FLASH_STATUS_ERASE_ERROR) {
+       int res = 0;
+       if(status&CFI_STATUS_ERASE_ERROR) {
                puts("error: ");
-               if(status&FLASH_STATUS_VPP_LOW) {
+               if(status&CFI_STATUS_VPP_LOW) {
                        puts("vpp insufficient\n");
-               }
-               if(status&FLASH_STATUS_LOCKED_ERROR) {
+                       res = -EFAULT;
+               } else if(status&CFI_STATUS_LOCKED_ERROR) {
                        puts("block is lock-protected\n");
+                       res = -EPERM;
+               } else {
+                       puts("unknown fault\n");
+                       res = -EFAULT;
                }
        } else {
                puts("done\n");
        }
 
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
+
+       return res;
+
 }
 
-void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) {
+int flash_program(flash_t *flash, uint32_t dst, void *src, uint32_t nbytes) {
        const void *base_addr = flash->f_base;
+       int res = 0;
        uint32_t i;
 
-       printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src);
+       /* check destination bounds */
+       if(dst >= flash->f_size) {
+               return -EINVAL;
+       }
+       if(dst + nbytes > flash->f_size) {
+               return -EINVAL;
+       }
 
+       /* check destination alignment */
        if(dst%2) {
-               puts("error: unaligned destination\n");
-               return;
+               return -EINVAL;
        }
-
        if(nbytes%2) {
-               puts("error: unaligned count\n");
-               return;
+               return -EINVAL;
        }
 
+       /* check permissions */
        if(flash_protected(dst)) {
-               puts("error: block is soft-protected\n");
-               return;
+               return -EPERM;
        }
 
-       flash_write_cmd(base_addr, FLASH_CMD_CLEAR_STATUS);
+       /* say something */
+       printf("Programming %u bytes to 0x%08x from 0x%p...", nbytes, dst, src);
+
+       /* clear status register */
+       flash_write_cmd(base_addr, CFI_CMD_CLEAR_STATUS);
 
+       /* write the words */
        puts("writing...");
        for(i = 0; i < nbytes; i += 2) {
                uint16_t *src_addr = (uint16_t*)(src + i);
@@ -189,248 +295,228 @@ void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes)
 
                uint16_t data = *src_addr;
 
-               flash_write_cmd(dst_addr, FLASH_CMD_WRITE);
+               flash_write_cmd(dst_addr, CFI_CMD_WRITE);
                flash_write_cmd(dst_addr, data);
 
-               flash_write_cmd(base_addr, FLASH_CMD_READ_STATUS);
+               flash_write_cmd(base_addr, CFI_CMD_READ_STATUS);
                uint16_t status;
                do {
                        status = flash_read16(base_addr, 0);
-               } while(!(status&FLASH_STATUS_READY));
+               } while(!(status&CFI_STATUS_READY));
 
-               if(status&FLASH_STATUS_PROGRAM_ERROR) {
+               if(status&CFI_STATUS_PROGRAM_ERROR) {
                        puts("error: ");
-                       if(status&FLASH_STATUS_VPP_LOW) {
+                       if(status&CFI_STATUS_VPP_LOW) {
                                puts("vpp insufficient");
-                       }
-                       if(status&FLASH_STATUS_LOCKED_ERROR) {
+                               res = -EFAULT;
+                       } else if(status&CFI_STATUS_LOCKED_ERROR) {
                                puts("block is lock-protected");
+                               res = -EPERM;
+                       } else {
+                               puts("unknown fault");
+                               res = -EFAULT;
                        }
                        goto err_reset;
                }
        }
 
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
 
+       /* verify the result */
        puts("verifying...");
        for(i = 0; i < nbytes; i += 2) {
                uint16_t *src_addr = (uint16_t*)(src + i);
                uint16_t *dst_addr = (uint16_t*)(base_addr + dst + i);
                if(*src_addr != *dst_addr) {
                        puts("error: verification failed");
+                       res = -EFAULT;
                        goto err;
                }
        }
 
        puts("done\n");
 
-       return;
+       return res;
 
  err_reset:
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
 
  err:
        printf(" at offset 0x%x\n", i);
-}
-
-typedef void (*flash_block_cb_t)(cfi_flash_t *flash,
-                                                                uint32_t block_offset,
-                                                                uint32_t block_size);
 
-void flash_iterate_blocks(cfi_flash_t *flash, struct cfi_query *qry,
-                                                 uint32_t start_offset, uint32_t end_offset,
-                                                 flash_block_cb_t callback)
-{
-       int region, block;
-
-       uint32_t block_start = 0;
-       for(region = 0; region < qry->num_erase_regions; region++) {
-               uint16_t actual_count = qry->erase_regions[region].b_count + 1;
-               uint32_t actual_size = qry->erase_regions[region].b_size * 256;
-               for(block = 0; block < actual_count; block++) {
-                       uint32_t block_end = block_start + actual_size;
-                       if(block_start >= start_offset && block_end-1 <= end_offset) {
-                               callback(flash, block_start, actual_size);
-                       }
-                       block_start = block_end;
-               }
-       }
+       return res;
 }
 
-static void get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) {
-       flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
+/* Internal: retrieve manufacturer and device id from id space */
+static int get_id(void *base_addr, uint16_t *manufacturer_id, uint16_t *device_id) {
+       flash_write_cmd(base_addr, CFI_CMD_READ_ID);
 
-       *manufacturer_id = flash_read16(base_addr, FLASH_OFFSET_MANUFACTURER_ID);
-       *device_id = flash_read16(base_addr, FLASH_OFFSET_DEVICE_ID);
+       *manufacturer_id = flash_read16(base_addr, CFI_OFFSET_MANUFACTURER_ID);
+       *device_id = flash_read16(base_addr, CFI_OFFSET_DEVICE_ID);
 
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
+
+       return 0;
 }
 
-static void get_query(void *base_addr, struct cfi_query *query) {
-       unsigned int i;
+/* Internal: retrieve cfi query response data */
+static int get_query(void *base_addr, struct cfi_query *query) {
+       int res = 0;
+       int i;
 
-       flash_write_cmd(base_addr, FLASH_CMD_CFI);
+       flash_write_cmd(base_addr, CFI_CMD_CFI);
 
        for(i = 0; i < sizeof(struct cfi_query); i++) {
-               uint16_t byte = flash_read16(base_addr, FLASH_OFFSET_CFI_RESP+i);
+               uint16_t byte = flash_read16(base_addr, CFI_OFFSET_CFI_RESP+i);
                *(((unsigned char*)query)+i) = byte;
        }
 
        if(query->qry[0] != 'Q' || query->qry[1] != 'R' || query->qry[2] != 'Y') {
-               puts("Error: CFI query signature not found\n");
+               res = -ENOENT;
        }
 
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
-}
-
-static void dump_query(void *base_addr, struct cfi_query *query) {
-       unsigned int i;
-
-       flash_write_cmd(base_addr, FLASH_CMD_CFI);
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
 
-       for(i = 0; i < sizeof(struct cfi_query); i++) {
-               uint8_t byte = *(((uint8_t*)query)+i);
-               printf("%04X: %02X\n", FLASH_OFFSET_CFI_RESP+i, byte);
-       }
-
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
+       return res;
 }
 
-static void dump_layout(void *base_addr, const struct cfi_query *qry) {
-       int region;
+/* Internal: retrieve intel protection data */
+static int get_intel_protection(void *base_addr, uint16_t *lockp, uint8_t protp[8]) {
+       int i;
 
-       flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
-       for(region = 0; region < qry->num_erase_regions; region++) {
-               uint16_t actual_count = qry->erase_regions[region].b_count + 1;
-               uint32_t actual_size = qry->erase_regions[region].b_size * 256;
-               printf("Region of 0x%04x times 0x%6x bytes\n", actual_count,
-                       actual_size);
+       /* check args */
+       if(!lockp) {
+               return -EINVAL;
        }
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
-}
-
-static void dump_locks(void *base_addr, const struct cfi_query *qry) {
-       int region, block;
-
-       uint32_t block_addr = 0;
-       flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
-       for(region = 0; region < qry->num_erase_regions; region++) {
-               uint16_t actual_count = qry->erase_regions[region].b_count + 1;
-               uint32_t actual_size = qry->erase_regions[region].b_size * 256;
-               for(block = 0; block < actual_count; block++) {
-                       uint8_t lock = flash_read16(base_addr, block_addr+2);
-                       printf("Block 0x%08x lock 0x%02x\n", block_addr*2, lock);
-                       block_addr += actual_size / 2;
-               }
+       if(!protp) {
+               return -EINVAL;
        }
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
-}
 
-static void dump_protection(void *base_addr) {
-       flash_write_cmd(base_addr, FLASH_CMD_READ_ID);
+       /* enter read id mode */
+       flash_write_cmd(base_addr, CFI_CMD_READ_ID);
 
-       uint16_t lock = flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION);
-       printf("Protection Lock: 0x%04x\n", lock);
+       /* get lock */
+       *lockp = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION);
 
-       puts("Protection Data: ");
-       int i;
+       /* get data */
        for(i = 0; i < 8; i++) {
-               printf("%04x", flash_read16(base_addr, FLASH_OFFSET_INTEL_PROTECTION + 1 + i));
+               protp[i] = flash_read16(base_addr, CFI_OFFSET_INTEL_PROTECTION + 1 + i);
        }
-       putchar('\n');
 
-       flash_write_cmd(base_addr, FLASH_CMD_RESET);
-}
+       /* leave read id mode */
+       flash_write_cmd(base_addr, CFI_CMD_RESET);
 
-static void dump_timing(void *base_addr, struct cfi_query *qry) {
-       uint32_t block_erase_typ = 1<<qry->block_erase_timeout_typ;
-       uint32_t block_erase_max = (1<<qry->block_erase_timeout_max) * block_erase_typ;
-       uint32_t word_program_typ = 1<<qry->word_write_timeout_typ;
-       uint32_t word_program_max = (1<<qry->word_write_timeout_max) * word_program_typ;
-       printf("Block Erase Typical: %u ms\n", block_erase_typ);
-       printf("Block Erase Maximum: %u ms\n", block_erase_max);
-       printf("Word Program Typical: %u us\n", word_program_typ);
-       printf("Word Program Maximum: %u us\n", word_program_max);
+       return 0;
 }
 
-static void dump_algorithms(void *base_addr, struct cfi_query *qry) {
-       printf("Primary Algorithm ID: %04x\n", qry->p_id);
-       printf("Primary Extended Query: %04x\n", qry->p_adr);
+#if 0
 
-       printf("Alternate Algorithm ID: %04x\n", qry->a_id);
-       printf("Alternate Extended Query: %04x\n", qry->a_adr);
+static void dump_intel_protection(uint16_t lock, uint8_t data[8]) {
+       printf("  protection lock 0x%4.4x data 0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
+                  lock, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
 }
 
-void
-lockdown_block_cb(cfi_flash_t *flash,
-                                 uint32_t block_offset,
-                                 uint32_t block_size)
-{
-       flash_block_lockdown(flash, block_offset);
+static void dump_query_algorithms(struct cfi_query *qry) {
+       printf("  primary algorithm 0x%4.4x\n", qry->p_id);
+       printf("  primary extended query 0x%4.4x\n", qry->p_adr);
+       printf("  alternate algorithm 0x%4.4x\n", qry->a_id);
+       printf("  alternate extended query 0x%4.4x\n", qry->a_adr);
 }
 
-void
-print_block_cb(cfi_flash_t *flash,
-                          uint32_t block_offset,
-                          uint32_t block_size)
-{
-       printf("%08x size %08x\n", block_offset, block_size);
+static void dump_query_timing(struct cfi_query *qry) {
+       uint32_t block_erase_typ = 1<<qry->block_erase_timeout_typ;
+       uint32_t block_erase_max = (1<<qry->block_erase_timeout_max) * block_erase_typ;
+       uint32_t word_program_typ = 1<<qry->word_write_timeout_typ;
+       uint32_t word_program_max = (1<<qry->word_write_timeout_max) * word_program_typ;
+       printf("  block erase typ %u ms\n", block_erase_typ);
+       printf("  block erase max %u ms\n", block_erase_max);
+       printf("  word program typ %u us\n", word_program_typ);
+       printf("  word program max %u us\n", word_program_max);
 }
 
-void flash_dump_info(cfi_flash_t *flash) {
-       void *base_addr = flash->f_base;
-       struct cfi_query *qry = &flash->f_query;
-
-       printf("Flash Manufacturer ID: %04x\n", flash->f_manuf_id);
-       printf("Flash Device ID: %04x\n", flash->f_dev_id);
-
-       printf("Flash Size: 0x%08x bytes\n", flash->f_size);
-
-       dump_algorithms(base_addr, qry);
+void flash_dump_info(flash_t *flash) {
+       int i;
+       printf("flash at 0x%p of %d bytes with %d regions\n", flash->f_base, flash->f_size, flash->f_nregions);
 
-       dump_timing(base_addr, qry);
+       uint16_t m_id, d_id;
+       if(get_id(flash->f_base, &m_id, &d_id)) {
+               puts("  failed to get id\n");
+       } else {
+               printf("  manufacturer 0x%4.4x device 0x%4.4x\n", m_id, d_id);
+       }
 
-       dump_protection(base_addr);
+       uint16_t plock;
+       uint8_t pdata[8];
+       if(get_intel_protection(flash->f_base, &plock, pdata)) {
+               puts("  failed to get protection data\n");
+       } else {
+               dump_intel_protection(plock, pdata);
+       }
 
-       dump_layout(base_addr, qry);
+       struct cfi_query qry;
+       if(get_query(flash->f_base, &qry)) {
+               puts("  failed to get cfi query response\n");
+       } else {
+               dump_query_algorithms(&qry);
+               dump_query_timing(&qry);
+       }
 
-       dump_locks(base_addr, qry);
+       for(i = 0; i < flash->f_nregions; i++) {
+               flash_region_t *fr = &flash->f_regions[i];
+               printf("  region %d: %d blocks of %d bytes at 0x%p\n", i, fr->fr_bnum, fr->fr_bsize, fr->fr_base);
+       }
 }
 
-void flash_init(cfi_flash_t *flash, void *base_addr) {
-       printd("Initializing CFI flash at 0x%p\n", base_addr);
+#endif
 
-       flash->f_base = base_addr;
+int flash_init(flash_t *flash, void *base_addr) {
+       int res, i;
+       uint16_t m_id, d_id;
+       uint32_t base;
+       struct cfi_query qry;
 
-       get_id(base_addr, &flash->f_manuf_id, &flash->f_dev_id);
+       /* retrieve and check manufacturer and device id */
+       res = get_id(base_addr, &m_id, &d_id);
+       if(res) {
+               return res;
+       }
+       if(m_id != CFI_MANUF_INTEL) {
+               /* we only support intel devices */
+               return -ENOTSUP;
+       }
 
-       get_query(base_addr, &flash->f_query);
+       /* retrieve and check query response */
+       res = get_query(base_addr, &qry);
+       if(res) {
+               return res;
+       }
+       if(qry.p_id != CFI_ALGO_INTEL_3) {
+               /* we only support algo 3 */
+               return -ENOTSUP;
+       }
+       if(qry.num_erase_regions > FLASH_MAX_REGIONS) {
+               /* we have a hard limit on the number of regions */
+               return -ENOTSUP;
+       }
 
-       flash->f_size = 1<<flash->f_query.dev_size;
-}
+       /* fill in basic information */
+       flash->f_base = base_addr;
+       flash->f_size = 1<<qry.dev_size;
 
-void flash_test() {
-       /* block iterator test */
-#if 0
-       flash_iterate_blocks(flash, qry, 0x0000, 0xFFFF, &lockdown_block_cb);
-#endif
+       /* determine number of erase regions */
+       flash->f_nregions = qry.num_erase_regions;
 
-       /* programming test */
-#if 0
-       static uint8_t magic[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xDE, 0xAD, 0xBE, 0xEF};
+       /* compute actual erase region info from cfi junk */
+       base = 0;
+       for(i = 0; i < flash->f_nregions; i++) {
+               flash_region_t *fr = &flash->f_regions[i];
 
-       memdump_range(&magic, sizeof(magic));
+               fr->fr_base = base;
+               fr->fr_bnum = qry.erase_regions[i].b_count + 1;
+               fr->fr_bsize = qry.erase_regions[i].b_size * 256;
 
-#if 0
-#define ADDR 0x001E0000
-       flash_block_unlock(flash, ADDR);
-       memdump_range(ADDR, 16);
-       flash_block_erase(flash, ADDR);
-       memdump_range(ADDR, 16);
-       flash_program(flash, ADDR, &magic, sizeof(magic));
-       memdump_range(ADDR, 16);
-#undef ADDR
-#endif
+               base += fr->fr_bnum * fr->fr_bsize;
+       }
 
-#endif
+       return 0;
 }
index 45435c4..9d8b33a 100644 (file)
@@ -4,67 +4,38 @@
 
 #include <stdint.h>
 
-
-#define CFI_FLASH_MAX_ERASE_REGIONS 4
-
-/* structure of erase region descriptor */
-struct cfi_region {
-       uint16_t b_count;
-       uint16_t b_size;
-} __attribute__((packed));
-
-
-/* structure of cfi query response */
-struct cfi_query {
-       uint8_t qry[3];
-       uint16_t        p_id;
-       uint16_t        p_adr;
-       uint16_t        a_id;
-       uint16_t        a_adr;
-       uint8_t vcc_min;
-       uint8_t vcc_max;
-       uint8_t vpp_min;
-       uint8_t vpp_max;
-       uint8_t word_write_timeout_typ;
-       uint8_t buf_write_timeout_typ;
-       uint8_t block_erase_timeout_typ;
-       uint8_t chip_erase_timeout_typ;
-       uint8_t word_write_timeout_max;
-       uint8_t buf_write_timeout_max;
-       uint8_t block_erase_timeout_max;
-       uint8_t chip_erase_timeout_max;
-       uint8_t dev_size;
-       uint16_t        interface_desc;
-       uint16_t        max_buf_write_size;
-       uint8_t num_erase_regions;
-       struct cfi_region  erase_regions[CFI_FLASH_MAX_ERASE_REGIONS];
-} __attribute__((packed));
+#define FLASH_MAX_REGIONS 4
 
 typedef struct {
-       void *f_base;
+       void  *fr_base;
+       size_t fr_bnum;
+       size_t fr_bsize;
+} flash_region_t;
 
-       uint32_t f_size;
-
-       uint16_t f_manuf_id;
-       uint16_t f_dev_id;
-
-       struct cfi_query f_query;
-} cfi_flash_t;
+typedef struct {
+       void  *f_base;
+       size_t f_size;
 
-typedef uint8_t flash_lock;
+       size_t f_nregions;
+       flash_region_t f_regions[FLASH_MAX_REGIONS];
+} flash_t;
 
-void flash_init(cfi_flash_t *flash, void *base_addr);
+typedef enum {
+       FLASH_UNLOCKED = 0,
+       FLASH_LOCKED,
+       FLASH_LOCKED_DOWN
+} flash_lock_t;
 
-void flash_dump_info(cfi_flash_t *flash);
+int flash_init(flash_t *flash, void *base_addr);
 
-flash_lock flash_block_getlock(cfi_flash_t *flash, uint32_t block_offset);
+flash_lock_t flash_block_getlock(flash_t *flash, uint32_t block_offset);
 
-void flash_block_unlock(cfi_flash_t *flash, uint32_t block_offset);
-void flash_block_lock(cfi_flash_t *flash, uint32_t block_offset);
-void flash_block_lockdown(cfi_flash_t *flash, uint32_t block_offset);
+int flash_block_unlock(flash_t *flash, uint32_t block_offset);
+int flash_block_lock(flash_t *flash, uint32_t block_offset);
+int flash_block_lockdown(flash_t *flash, uint32_t block_offset);
 
-void flash_block_erase(cfi_flash_t *flash, uint32_t block_addr);
+int flash_block_erase(flash_t *flash, uint32_t block_offset);
 
-void flash_program(cfi_flash_t *flash, uint32_t dst, void *src, uint32_t nbytes);
+int flash_program(flash_t *flash, uint32_t dst_offset, void *src, uint32_t nbytes);
 
 #endif