]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/mtd/bcm63xxpart.c
Merge remote branch 'wireless-next/master' into ath6kl-next
[karo-tx-linux.git] / drivers / mtd / bcm63xxpart.c
index ac7d3c823949b562eb83ab2f51c233ebff2494d2..608321ee056e5cb69c22f950ad2eb72f974fd6da 100644 (file)
@@ -24,6 +24,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/crc32.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mtd/partitions.h>
 
 #include <asm/mach-bcm63xx/bcm963xx_tag.h>
+#include <asm/mach-bcm63xx/board_bcm963xx.h>
 
 #define BCM63XX_EXTENDED_SIZE  0xBFC00000      /* Extended flash address */
 
+#define BCM63XX_MIN_CFE_SIZE   0x10000         /* always at least 64KiB */
+#define BCM63XX_MIN_NVRAM_SIZE 0x10000         /* always at least 64KiB */
+
+#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
+
 static int bcm63xx_detect_cfe(struct mtd_info *master)
 {
-       int idoffset = 0x4e0;
-       static char idstring[8] = "CFE1CFE1";
        char buf[9];
        int ret;
        size_t retlen;
 
-       ret = master->read(master, idoffset, 8, &retlen, (void *)buf);
+       ret = mtd_read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen,
+                      (void *)buf);
+       buf[retlen] = 0;
+
+       if (ret)
+               return ret;
+
+       if (strncmp("cfe-v", buf, 5) == 0)
+               return 0;
+
+       /* very old CFE's do not have the cfe-v string, so check for magic */
+       ret = mtd_read(master, BCM63XX_CFE_MAGIC_OFFSET, 8, &retlen,
+                      (void *)buf);
        buf[retlen] = 0;
-       pr_info("Read Signature value of %s\n", buf);
 
-       return strncmp(idstring, buf, 8);
+       return strncmp("CFE1CFE1", buf, 8);
 }
 
 static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
@@ -62,41 +78,58 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
        size_t retlen;
        unsigned int rootfsaddr, kerneladdr, spareaddr;
        unsigned int rootfslen, kernellen, sparelen, totallen;
+       unsigned int cfelen, nvramlen;
        int namelen = 0;
        int i;
-       char *boardid;
-       char *tagversion;
+       u32 computed_crc;
 
        if (bcm63xx_detect_cfe(master))
                return -EINVAL;
 
+       cfelen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_CFE_SIZE);
+       nvramlen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_NVRAM_SIZE);
+
        /* Allocate memory for buffer */
        buf = vmalloc(sizeof(struct bcm_tag));
        if (!buf)
                return -ENOMEM;
 
        /* Get the tag */
-       ret = master->read(master, master->erasesize, sizeof(struct bcm_tag),
-                                                       &retlen, (void *)buf);
+       ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen,
+                      (void *)buf);
+
        if (retlen != sizeof(struct bcm_tag)) {
                vfree(buf);
                return -EIO;
        }
 
-       sscanf(buf->kernel_address, "%u", &kerneladdr);
-       sscanf(buf->kernel_length, "%u", &kernellen);
-       sscanf(buf->total_length, "%u", &totallen);
-       tagversion = &(buf->tag_version[0]);
-       boardid = &(buf->board_id[0]);
-
-       pr_info("CFE boot tag found with version %s and board type %s\n",
-               tagversion, boardid);
-
-       kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
-       rootfsaddr = kerneladdr + kernellen;
-       spareaddr = roundup(totallen, master->erasesize) + master->erasesize;
-       sparelen = master->size - spareaddr - master->erasesize;
-       rootfslen = spareaddr - rootfsaddr;
+       computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
+                               offsetof(struct bcm_tag, header_crc));
+       if (computed_crc == buf->header_crc) {
+               char *boardid = &(buf->board_id[0]);
+               char *tagversion = &(buf->tag_version[0]);
+
+               sscanf(buf->kernel_address, "%u", &kerneladdr);
+               sscanf(buf->kernel_length, "%u", &kernellen);
+               sscanf(buf->total_length, "%u", &totallen);
+
+               pr_info("CFE boot tag found with version %s and board type %s\n",
+                       tagversion, boardid);
+
+               kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
+               rootfsaddr = kerneladdr + kernellen;
+               spareaddr = roundup(totallen, master->erasesize) + cfelen;
+               sparelen = master->size - spareaddr - nvramlen;
+               rootfslen = spareaddr - rootfsaddr;
+       } else {
+               pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n",
+                       buf->header_crc, computed_crc);
+               kernellen = 0;
+               rootfslen = 0;
+               rootfsaddr = 0;
+               spareaddr = cfelen;
+               sparelen = master->size - cfelen - nvramlen;
+       }
 
        /* Determine number of partitions */
        namelen = 8;
@@ -119,7 +152,7 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
        /* Start building partition list */
        parts[curpart].name = "CFE";
        parts[curpart].offset = 0;
-       parts[curpart].size = master->erasesize;
+       parts[curpart].size = cfelen;
        curpart++;
 
        if (kernellen > 0) {
@@ -139,14 +172,14 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
        }
 
        parts[curpart].name = "nvram";
-       parts[curpart].offset = master->size - master->erasesize;
-       parts[curpart].size = master->erasesize;
+       parts[curpart].offset = master->size - nvramlen;
+       parts[curpart].size = nvramlen;
 
        /* Global partition "linux" to make easy firmware upgrade */
        curpart++;
        parts[curpart].name = "linux";
-       parts[curpart].offset = parts[0].size;
-       parts[curpart].size = master->size - parts[0].size - parts[3].size;
+       parts[curpart].offset = cfelen;
+       parts[curpart].size = master->size - cfelen - nvramlen;
 
        for (i = 0; i < nrparts; i++)
                pr_info("Partition %d is %s offset %lx and length %lx\n", i,