atheros: clean up the board data detection code and attempt to boot the kernel even...
[openwrt/svn-archive/archive.git] / target / linux / atheros / files / arch / mips / atheros / board.c
index 59aa134a3f69c36871392400aae1c572ba3f88a2..4fa0e78bdbaeeaacddddcff20a51309dc331cbef 100644 (file)
 #include <linux/kernel.h>
 #include <linux/serial.h>
 #include <linux/serial_core.h>
+#include <linux/random.h>
 #include <asm/bootinfo.h>
 #include <asm/irq_cpu.h>
 #include <asm/io.h>
 #include <ar531x.h>
 
-char *board_config, *radio_config;
+char *board_config = NULL;
+char *radio_config = NULL;
+int broken_boarddata = 0;
 
 extern int early_serial_setup(struct uart_port *port);
 
-static u8 *find_board_config(char *flash_limit)
+static inline bool
+check_radio_magic(unsigned char *addr)
+{
+       addr += 0x7a; /* offset for flash magic */
+       if ((addr[0] == 0x5a) && (addr[1] == 0xa5)) {
+               return 1;
+       }
+       return 0;
+}
+
+static inline bool
+check_board_data(unsigned char *flash_limit, unsigned char *addr, bool broken)
+{
+       /* config magic found */
+       if ( *(int *)addr == 0x35333132)
+               return 1;
+
+       if (!broken)
+               return 0;
+
+       if (check_radio_magic(addr + 0xf8))
+               radio_config = addr + 0xf8;
+       if ((addr < flash_limit + 0x10000) &&
+            check_radio_magic(addr + 0x10000))
+               radio_config = addr + 0x10000;
+
+       if (radio_config) {
+               /* broken board data detected, use radio data to find the offset,
+                * user will fix this */
+               broken_boarddata = 1;
+               return 1;
+       }
+       return 0;
+}
+
+static u8 *find_board_config(char *flash_limit, bool broken)
 {
        char *addr;
        int found = 0;
@@ -40,36 +78,34 @@ static u8 *find_board_config(char *flash_limit)
                addr >= (char *) (flash_limit - 0x30000);
                addr -= 0x1000) {
 
-               if ( *(int *)addr == 0x35333131) {
-                       /* config magic found */
+               if (check_board_data(flash_limit, addr, broken)) {
                        found = 1;
                        break;
                }
        }
 
-       if (!found) {
-               printk("WARNING: No board configuration data found!\n");
+       if (!found)
                addr = NULL;
-       }
-       
+
        return addr;
 }
 
 static u8 *find_radio_config(char *flash_limit, char *board_config)
 {
        int dataFound;
-       u32 radio_config;
-       
-       /* 
+       char *radio_config;
+
+       /*
         * Now find the start of Radio Configuration data, using heuristics:
         * Search forward from Board Configuration data by 0x1000 bytes
         * at a time until we find non-0xffffffff.
         */
        dataFound = 0;
-       for (radio_config = (u32) board_config + 0x1000;
-            (radio_config < (u32) flash_limit);
+       for (radio_config = board_config + 0x1000;
+            (radio_config < flash_limit);
             radio_config += 0x1000) {
-               if (*(int *)radio_config != 0xffffffff) {
+               if ((*(u32 *)radio_config != 0xffffffff) &&
+                   check_radio_magic(radio_config)) {
                        dataFound = 1;
                        break;
                }
@@ -77,10 +113,11 @@ static u8 *find_radio_config(char *flash_limit, char *board_config)
 
 #ifdef CONFIG_ATHEROS_AR5315
        if (!dataFound) { /* AR2316 relocates radio config to new location */
-           for (radio_config = (u32) board_config + 0xf8;
-               (radio_config < (u32) flash_limit - 0x1000 + 0xf8);
+           for (radio_config = board_config + 0xf8;
+               (radio_config < flash_limit - 0x1000 + 0xf8);
                         radio_config += 0x1000) {
-                       if (*(int *)radio_config != 0xffffffff) {
+                       if ((*(u32 *)radio_config != 0xffffffff) &&
+                               check_radio_magic(radio_config)) {
                                dataFound = 1;
                                break;
                        }
@@ -98,6 +135,7 @@ static u8 *find_radio_config(char *flash_limit, char *board_config)
 
 int __init ar531x_find_config(char *flash_limit)
 {
+       struct ar531x_boarddata *bd;
        unsigned int rcfg_size;
        char *bcfg, *rcfg;
 
@@ -105,32 +143,54 @@ int __init ar531x_find_config(char *flash_limit)
         * spiflash driver, accessing the mapped memory directly is no
         * longer safe */
 
-       bcfg = find_board_config(flash_limit);
+       bcfg = find_board_config(flash_limit, false);
        if (!bcfg)
+               bcfg = find_board_config(flash_limit, true);
+       if (!bcfg) {
+               printk("WARNING: No board configuration data found!\n");
                return -ENODEV;
+       }
 
        board_config = kzalloc(BOARD_CONFIG_BUFSZ, GFP_KERNEL);
        memcpy(board_config, bcfg, 0x100);
+       if (broken_boarddata) {
+               printk("WARNING: broken board data detected\n");
+               bd = (struct ar531x_boarddata *)board_config;
+               if (!memcmp(bd->enet0Mac, "\x00\x00\x00\x00\x00\x00", 6)) {
+                       printk("Fixing up empty mac addresses\n");
+                       bd->enet0Mac[1] = 0x13;
+                       bd->enet0Mac[2] = 0x37;
+                       get_random_bytes(bd->enet0Mac + 3, 3);
+                       bd->wlan0Mac[1] = 0x13;
+                       bd->wlan0Mac[2] = 0x37;
+                       get_random_bytes(bd->wlan0Mac + 3, 3);
+               }
+       }
+
 
        /* Radio config starts 0x100 bytes after board config, regardless
         * of what the physical layout on the flash chip looks like */
 
-       rcfg = find_radio_config(flash_limit, bcfg);
+       if (radio_config)
+               rcfg = radio_config;
+       else
+               rcfg = find_radio_config(flash_limit, bcfg);
+
        if (!rcfg)
                return -ENODEV;
 
        radio_config = board_config + 0x100 + ((rcfg - bcfg) & 0xfff);
        printk("Radio config found at offset 0x%x(0x%x)\n", rcfg - bcfg, radio_config - board_config);
-       rcfg_size = BOARD_CONFIG_BUFSZ - ((rcfg - bcfg) & (BOARD_CONFIG_BUFSZ - 1));
+       rcfg_size = BOARD_CONFIG_BUFSZ - (radio_config - board_config);
        memcpy(radio_config, rcfg, rcfg_size);
-       
+
        return 0;
 }
 
 void __init serial_setup(unsigned long mapbase, unsigned int uartclk)
 {
        struct uart_port s;
-       
+
        memset(&s, 0, sizeof(s));
 
        s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
@@ -162,7 +222,7 @@ const char *get_system_type(void)
 
        case MACH_ATHEROS_AR2312:
                return "Atheros AR2312";
-               
+
        case MACH_ATHEROS_AR2313:
                return "Atheros AR2313";
 #endif
@@ -225,7 +285,7 @@ static int __init ar531x_register_gpiodev(void)
        struct platform_device *pdev;
 
        printk(KERN_INFO "ar531x: Registering GPIODEV device\n");
-       
+
        pdev = platform_device_register_simple("GPIODEV", 0, &res, 1);
 
        if (!pdev) {