You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
350 lines
8.9 KiB
350 lines
8.9 KiB
10 years ago
|
--- 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 <linux/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,145 @@ 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;
|
||
|
+
|
||
|
+ 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) {
|
||
|
+ nr_parts--;
|
||
|
+ 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 +256,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 +265,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 +291,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",
|
||
|
};
|
||
|
|