--- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -43,66 +44,35 @@ #define BCM63XX_CFE_MAGIC_OFFSET 0x4e0 -static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, - struct mtd_partition **pparts, - struct mtd_part_parser_data *data) +static bool node_has_compatible(struct device_node *pp) +{ + return of_get_property(pp, "compatible", NULL); +} + +static int parse_bcmtag(struct mtd_info *master, struct mtd_partition *pparts, + int next_part, size_t offset, size_t size) { - /* CFE, NVRAM and global Linux are always present */ - int nrparts = 3, curpart = 0; struct bcm_tag *buf; - struct mtd_partition *parts; + u32 computed_crc; int ret; size_t retlen; - unsigned int rootfsaddr, kerneladdr, spareaddr, nvramaddr; - unsigned int rootfslen, kernellen, sparelen, totallen; - unsigned int cfelen, nvramlen; - unsigned int cfe_erasesize; - unsigned int caldatalen1 = 0, caldataaddr1 = 0; - unsigned int caldatalen2 = 0, caldataaddr2 = 0; - int i; - u32 computed_crc; + unsigned int rootfsaddr, kerneladdr; + unsigned int rootfslen, kernellen, totallen; bool rootfs_first = false; - - if (!bcm63xx_is_cfe_present()) - return -EINVAL; - - cfe_erasesize = max_t(uint32_t, master->erasesize, - BCM63XX_CFE_BLOCK_SIZE); - - cfelen = cfe_erasesize; - nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K; - nvramlen = roundup(nvramlen, cfe_erasesize); - nvramaddr = master->size - nvramlen; - - if (data) { - if (data->caldata[0]) { - caldatalen1 = cfe_erasesize; - caldataaddr1 = rounddown(data->caldata[0], - cfe_erasesize); - } - if (data->caldata[1]) { - caldatalen2 = cfe_erasesize; - caldataaddr2 = rounddown(data->caldata[1], - cfe_erasesize); - } - if (caldataaddr1 == caldataaddr2) { - caldataaddr2 = 0; - caldatalen2 = 0; - } - } + int curr_part = next_part; /* Allocate memory for buffer */ - buf = vmalloc(sizeof(struct bcm_tag)); + buf = vmalloc(sizeof(*buf)); if (!buf) return -ENOMEM; /* Get the tag */ - ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen, + ret = mtd_read(master, offset, sizeof(*buf), &retlen, (void *)buf); - if (retlen != sizeof(struct bcm_tag)) { + if (retlen != sizeof(*buf)) { vfree(buf); - return -EIO; + return 0; } computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf, @@ -121,7 +91,6 @@ static int bcm63xx_parse_cfe_partitions( kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE; rootfsaddr = rootfsaddr - BCM63XX_EXTENDED_SIZE; - spareaddr = roundup(totallen, master->erasesize) + cfelen; if (rootfsaddr < kerneladdr) { /* default Broadcom layout */ @@ -130,8 +99,8 @@ static int bcm63xx_parse_cfe_partitions( } else { /* OpenWrt layout */ rootfsaddr = kerneladdr + kernellen; - rootfslen = buf->real_rootfs_length; - spareaddr = rootfsaddr + rootfslen; + rootfslen = size - kernellen - + sizeof(*buf); } } else { pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n", @@ -139,16 +108,153 @@ static int bcm63xx_parse_cfe_partitions( kernellen = 0; rootfslen = 0; rootfsaddr = 0; - spareaddr = cfelen; } - sparelen = min_not_zero(nvramaddr, caldataaddr1) - spareaddr; - /* Determine number of partitions */ - if (rootfslen > 0) - nrparts++; + if (kernellen > 0) { + int kernelpart = curr_part; - if (kernellen > 0) - nrparts++; + if (rootfslen > 0 && rootfs_first) + kernelpart++; + pparts[kernelpart].name = "kernel"; + pparts[kernelpart].offset = kerneladdr; + pparts[kernelpart].size = kernellen; + curr_part++; + } + + if (rootfslen > 0) { + int rootfspart = curr_part; + + if (kernellen > 0 && rootfs_first) + rootfspart--; + pparts[rootfspart].name = "rootfs"; + pparts[rootfspart].offset = rootfsaddr; + pparts[rootfspart].size = rootfslen; + + curr_part++; + } + + vfree(buf); + + return curr_part - next_part; +} + + +static int bcm63xx_parse_cfe_partitions_of(struct mtd_info *master, + struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + struct device_node *dp = data->of_node; + struct device_node *pp; + int i, nr_parts = 0; + const char *partname; + int len; + + for_each_child_of_node(dp, pp) { + if (node_has_compatible(pp)) + continue; + + if (!of_get_property(pp, "reg", &len)) + continue; + + partname = of_get_property(pp, "label", &len); + if (!partname) + partname = of_get_property(pp, "name", &len); + + if (!strcmp(partname, "linux")) + nr_parts += 2; + + nr_parts++; + } + + *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL); + if (!*pparts) + return -ENOMEM; + + i = 0; + for_each_child_of_node(dp, pp) { + const __be32 *reg; + int a_cells, s_cells; + size_t size, offset; + + if (node_has_compatible(pp)) + continue; + + reg = of_get_property(pp, "reg", &len); + if (!reg) + continue; + + a_cells = of_n_addr_cells(pp); + s_cells = of_n_size_cells(pp); + offset = of_read_number(reg, a_cells); + size = of_read_number(reg + a_cells, s_cells); + partname = of_get_property(pp, "label", &len); + if (!partname) + partname = of_get_property(pp, "name", &len); + + if (!strcmp(partname, "linux")) + i += parse_bcmtag(master, *pparts, i, offset, size); + + if (of_get_property(pp, "read-only", &len)) + (*pparts)[i].mask_flags |= MTD_WRITEABLE; + + if (of_get_property(pp, "lock", &len)) + (*pparts)[i].mask_flags |= MTD_POWERUP_LOCK; + + (*pparts)[i].offset = offset; + (*pparts)[i].size = size; + (*pparts)[i].name = partname; + + i++; + } + + return i; +} + +static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + /* CFE, NVRAM and global Linux are always present */ + int nrparts = 5, curpart = 0; + struct mtd_partition *parts; + unsigned int nvramaddr; + unsigned int cfelen, nvramlen; + unsigned int cfe_erasesize; + unsigned int caldatalen1 = 0, caldataaddr1 = 0; + unsigned int caldatalen2 = 0, caldataaddr2 = 0; + unsigned int imageaddr, imagelen; + int i; + + if (!bcm63xx_is_cfe_present()) + return -EINVAL; + + cfe_erasesize = max_t(uint32_t, master->erasesize, + BCM63XX_CFE_BLOCK_SIZE); + + cfelen = cfe_erasesize; + nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K; + nvramlen = roundup(nvramlen, cfe_erasesize); + nvramaddr = master->size - nvramlen; + + if (data) { + if (data->caldata[0]) { + caldatalen1 = cfe_erasesize; + caldataaddr1 = rounddown(data->caldata[0], + cfe_erasesize); + } + if (data->caldata[1]) { + caldatalen2 = cfe_erasesize; + caldataaddr2 = rounddown(data->caldata[1], + cfe_erasesize); + } + if (caldataaddr1 == caldataaddr2) { + caldataaddr2 = 0; + caldatalen2 = 0; + } + } + + imageaddr = cfelen; + imagelen = min_not_zero(nvramaddr, caldataaddr1) - imageaddr; if (caldatalen1 > 0) nrparts++; @@ -158,10 +264,8 @@ static int bcm63xx_parse_cfe_partitions( /* Ask kernel for more memory */ parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); - if (!parts) { - vfree(buf); + if (!parts) return -ENOMEM; - } /* Start building partition list */ parts[curpart].name = "CFE"; @@ -169,29 +273,7 @@ static int bcm63xx_parse_cfe_partitions( parts[curpart].size = cfelen; curpart++; - if (kernellen > 0) { - int kernelpart = curpart; - - if (rootfslen > 0 && rootfs_first) - kernelpart++; - parts[kernelpart].name = "kernel"; - parts[kernelpart].offset = kerneladdr; - parts[kernelpart].size = kernellen; - curpart++; - } - - if (rootfslen > 0) { - int rootfspart = curpart; - - if (kernellen > 0 && rootfs_first) - rootfspart--; - parts[rootfspart].name = "rootfs"; - parts[rootfspart].offset = rootfsaddr; - parts[rootfspart].size = rootfslen; - if (sparelen > 0 && !rootfs_first) - parts[rootfspart].size += sparelen; - curpart++; - } + curpart += parse_bcmtag(master, parts, curpart, imageaddr, imagelen); if (caldatalen1 > 0) { if (caldatalen2 > 0) @@ -217,25 +299,33 @@ static int bcm63xx_parse_cfe_partitions( /* Global partition "linux" to make easy firmware upgrade */ parts[curpart].name = "linux"; - parts[curpart].offset = cfelen; - parts[curpart].size = min_not_zero(nvramaddr, caldataaddr1) - cfelen; + parts[curpart].offset = imageaddr; + parts[curpart].size = imagelen; + curpart++; - for (i = 0; i < nrparts; i++) + for (i = 0; i < curpart; i++) pr_info("Partition %d is %s offset %llx and length %llx\n", i, parts[i].name, parts[i].offset, parts[i].size); - pr_info("Spare partition is offset %x and length %x\n", spareaddr, - sparelen); - *pparts = parts; - vfree(buf); return nrparts; }; + +static int bcm63xx_parse_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + if (data && data->of_node) + return bcm63xx_parse_cfe_partitions_of(master, pparts, data); + else + return bcm63xx_parse_cfe_partitions(master, pparts, data); +} + static struct mtd_part_parser bcm63xx_cfe_parser = { .owner = THIS_MODULE, - .parse_fn = bcm63xx_parse_cfe_partitions, + .parse_fn = bcm63xx_parse_partitions, .name = "bcm63xxpart", };