firmware: removed flash safety hack
[osmocom-bb.git] / src / target / firmware / apps / loader / main.c
index 777a1df..7505373 100644 (file)
 #include <stdio.h>
 #include <string.h>
 
-#include <gsm.h>
 #include <debug.h>
 #include <memory.h>
+#include <delay.h>
 #include <rffe.h>
 #include <keypad.h>
 #include <board.h>
 #include <console.h>
+#include <manifest.h>
+
+#include <osmocore/crc16.h>
 
 #include <abb/twl3025.h>
-#include <display/st7558.h>
 #include <rf/trf6151.h>
 
 #include <comm/sercomm.h>
 #include <calypso/uart.h>
 #include <calypso/timer.h>
 
-#include <layer1/sync.h>
-#include <layer1/tpu_window.h>
-
-#include <machine/endian.h>
+#include <cfi_flash.h>
 
 #include "protocol.h"
 
-struct loader_mem_read {
-       uint8_t cmd;
-       uint8_t nbytes;
-       uint32_t address;
-       uint8_t  data[0];
-} __attribute__((__packed__));
-
-uint32_t htonl(uint32_t hostlong) {
-#if BYTE_ORDER==LITTLE_ENDIAN
-  return (hostlong>>24) | ((hostlong&0xff0000)>>8) |
-          ((hostlong&0xff00)<<8) | (hostlong<<24);
-#else
-  return hostlong;
-#endif
+/* Main Program */
+const char *hr = "======================================================================\n";
+
+static void key_handler(enum key_codes code, enum key_states state);
+static void cmd_handler(uint8_t dlci, struct msgb *msg);
+
+int flag = 0;
+
+static void flush_uart(void) {
+       unsigned i;
+       for(i = 0; i < 500; i++) {
+               uart_poll(SERCOMM_UART_NR);
+               delay_ms(1);
+       }
 }
 
-uint32_t ntohl(uint32_t hostlong) __attribute__((weak,alias("htonl")));
+static void device_poweroff(void) {
+       flush_uart();
+       twl3025_power_off();
+}
 
-uint16_t htons(uint16_t hostshort) {
-#if BYTE_ORDER==LITTLE_ENDIAN
-  return ((hostshort>>8)&0xff) | (hostshort<<8);
-#else
-  return hostshort;
-#endif
+static void device_reset(void) {
+       flush_uart();
+       wdog_reset();
 }
 
-uint16_t ntohs(uint16_t hostshort) __attribute__((weak,alias("htons")));
+static void device_enter_loader(unsigned char bootrom) {
+       flush_uart();
 
-#define SCAN
+       calypso_bootrom(bootrom);
+       void (*entry)( void ) = (void (*)(void))0;
+       entry();
+}
 
-#ifdef SCAN
-/* if scanning is enabled, scan from 0 ... 124 */
-#define BASE_ARFCN     0
-#else
-/* fixed ARFCN in GSM1800 at which Harald has his GSM test license */
-#define BASE_ARFCN     871
-#endif
+static void device_jump(void *entry) {
+       flush_uart();
 
-/* FIXME: We need proper calibrated delay loops at some point! */
-void delay_us(unsigned int us)
-{
-       volatile unsigned int i;
+       void (*f)( void ) = (void (*)(void))entry;
+       f();
+}
 
-       for (i= 0; i < us*4; i++) { i; }
+static void
+loader_send_simple(struct msgb *msg, uint8_t dlci, uint8_t command) {
+       msgb_put_u8(msg, command);
+       sercomm_sendmsg(dlci, msg);
 }
 
-void delay_ms(unsigned int ms)
-{
-       volatile unsigned int i;
+extern unsigned char _start;
 
-       for (i= 0; i < ms*1300; i++) { i; }
+static void
+loader_send_init(uint8_t dlci) {
+       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);
 }
 
-/* Main Program */
-const char *hr = "======================================================================\n";
+flash_t the_flash;
 
-static void key_handler(enum key_codes code, enum key_states state);
-static void cmd_handler(uint8_t dlci, struct msgb *msg);
+extern void puts_asm(char *s);
+extern void putchar_asm(uint32_t c);
 
-int flag = 0;
-
-void poweroff(void) {
-       unsigned i;
-       for(i = 0; i < 10; i++) {
-               uart_poll(SERCOMM_UART_NR);
-               delay_ms(10);
-       }
-       twl3025_power_off();
-}
+static const uint8_t phone_ack[]     = { 0x1b, 0xf6, 0x02, 0x00, 0x41, 0x03, 0x42 };
 
 int main(void)
 {
+       int i = 0;
+       for(i = 0; i < sizeof(phone_ack); i++) {
+               putchar_asm(phone_ack[i]);
+       }
+
        /* Always disable wdt (some platforms enable it on boot) */
        wdog_enable(0);
 
        /* Initialize TWL3025 for power control */
        twl3025_init();
 
+       /* Backlight */
+       bl_mode_pwl(1);
+       bl_level(50);
+
        /* Initialize UART without interrupts */
        uart_init(SERCOMM_UART_NR, 0);
        uart_baudrate(SERCOMM_UART_NR, UART_115200);
@@ -139,15 +142,26 @@ int main(void)
        sercomm_init();
 
        /* Say hi */
-       puts("\n\nOSMOCOM Calypso loader\n");
+       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);
+
        /* Set up a key handler for powering off */
        keypad_set_handler(&key_handler);
 
        /* Set up loader communications */
        sercomm_register_rx_cb(SC_DLCI_LOADER, &cmd_handler);
 
+       /* Notify any running osmoload about our startup */
+       loader_send_init(SC_DLCI_LOADER);
+
        /* Wait for events */
        while (1) {
                keypad_poll();
@@ -164,28 +178,50 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
                return;
        }
 
-       uint8_t command = 0; //= msgb_get_u8(msg);
+       uint8_t command = msgb_get_u8(msg);
 
-       printf("command %u\n", command);
+       int res;
 
-       msgb_free(msg);
+       flash_lock_t lock;
 
-       return;
+       void *data;
 
+       uint8_t  chip;
        uint8_t  nbytes;
+       uint16_t crc, mycrc;
        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:
+               loader_send_simple(reply, dlci, LOADER_PING);
+               break;
 
-               printf("ping\n");
+       case LOADER_RESET:
+               loader_send_simple(reply, dlci, LOADER_RESET);
+               device_reset();
+               break;
 
-               //sercomm_sendmsg(dlci, msg);
-               //msg = NULL;
+       case LOADER_POWEROFF:
+               loader_send_simple(reply, dlci, LOADER_POWEROFF);
+               device_poweroff();
+               break;
+
+       case LOADER_ENTER_ROM_LOADER:
+               loader_send_simple(reply, dlci, LOADER_ENTER_ROM_LOADER);
+               device_enter_loader(1);
+               break;
 
+       case LOADER_ENTER_FLASH_LOADER:
+               loader_send_simple(reply, dlci, LOADER_ENTER_FLASH_LOADER);
+               device_enter_loader(0);
                break;
 
        case LOADER_MEM_READ:
@@ -193,12 +229,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);
+               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);
@@ -207,11 +242,162 @@ static void cmd_handler(uint8_t dlci, struct msgb *msg) {
 
                break;
 
-       }
+       case LOADER_MEM_WRITE:
+
+               nbytes = msgb_get_u8(msg);
+               crc = msgb_get_u16(msg);
+               address = msgb_get_u32(msg);
+
+               data = msgb_get(msg, nbytes);
+
+               mycrc = crc16(0, data, nbytes);
+
+               if(mycrc == crc) {
+                       memcpy((void*)address, data, nbytes);
+               }
+
+               msgb_put_u8(reply, LOADER_MEM_WRITE);
+               msgb_put_u8(reply, nbytes);
+               msgb_put_u16(reply, mycrc);
+               msgb_put_u32(reply, address);
+
+               sercomm_sendmsg(dlci, reply);
+
+               break;
 
-       if(msg) {
-               msgb_free(msg);
+       case LOADER_JUMP:
+
+               address = msgb_get_u32(msg);
+
+               msgb_put_u8(reply, LOADER_JUMP);
+               msgb_put_u32(reply, address);
+
+               sercomm_sendmsg(dlci, reply);
+
+               device_jump((void*)address);
+
+               break;
+
+       case LOADER_FLASH_INFO:
+
+               msgb_put_u8(reply, LOADER_FLASH_INFO);
+               msgb_put_u8(reply, 1); // nchips
+
+               // 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);
+               }
+
+               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);
+
+               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;
+
+       case LOADER_FLASH_PROGRAM:
+
+               nbytes = msgb_get_u8(msg);
+               crc = msgb_get_u16(msg);
+               msgb_get_u8(msg); // XXX align
+               chip = msgb_get_u8(msg);
+               address = msgb_get_u32(msg);
+
+               data = msgb_get(msg, nbytes);
+
+               mycrc = crc16(0, data, nbytes);
+
+               if(mycrc == crc) {
+                       res = flash_program(&the_flash, address, data, nbytes);
+               }
+
+               msgb_put_u8(reply, LOADER_FLASH_PROGRAM);
+               msgb_put_u8(reply, nbytes);
+               msgb_put_u16(reply, mycrc);
+               msgb_put_u8(reply, 0); // XXX align
+               msgb_put_u8(reply, chip);
+               msgb_put_u32(reply, address);
+
+               msgb_put_u32(reply, (uint32_t)res); // XXX
+
+               sercomm_sendmsg(dlci, reply);
+
+               break;
+
+       default:
+               printf("unknown command %d\n", command);
+
+               msgb_free(reply);
+
+               break;
        }
+
+ out:
+
+       msgb_free(msg);
 }
 
 static void key_handler(enum key_codes code, enum key_states state)
@@ -221,7 +407,12 @@ static void key_handler(enum key_codes code, enum key_states state)
 
        switch (code) {
        case KEY_POWER:
-               poweroff();
+               puts("Powering off due to keypress.\n");
+               device_poweroff();
+               break;
+       case KEY_OK:
+               puts("Resetting due to keypress.\n");
+               device_reset();
                break;
        default:
                break;