openwrt/target/linux/brcm63xx/patches-3.14/425-bcm63xxpart_parse_parit...

358 lines
9.2 KiB
Diff

--- a/drivers/mtd/bcm63xxpart.c
+++ b/drivers/mtd/bcm63xxpart.c
@@ -32,6 +32,7 @@
#include <linux/vmalloc.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/of.h>
#include <asm/mach-bcm63xx/bcm63xx_nvram.h>
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
@@ -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",
};