170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski/*
270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * BCM63XX CFE image tag parser
370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski *
470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * Copyright © 2006-2008  Florian Fainelli <florian@openwrt.org>
570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski *			  Mike Albon <malbon@openwrt.org>
670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * Copyright © 2009-2010  Daniel Dickinson <openwrt@cshore.neomailbox.net>
770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * Copyright © 2011 Jonas Gorski <jonas.gorski@gmail.com>
870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski *
970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * This program is free software; you can redistribute it and/or modify
1070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * it under the terms of the GNU General Public License as published by
1170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * the Free Software Foundation; either version 2 of the License, or
1270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * (at your option) any later version.
1370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski *
1470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * This program is distributed in the hope that it will be useful,
1570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * but WITHOUT ANY WARRANTY; without even the implied warranty of
1670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * GNU General Public License for more details.
1870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski *
1970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * You should have received a copy of the GNU General Public License
2070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * along with this program; if not, write to the Free Software
2170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
2270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski *
2370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski */
2470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
2570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
27f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski#include <linux/crc32.h>
2870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/module.h>
2970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/kernel.h>
3070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/slab.h>
3170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/vmalloc.h>
3270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/mtd/mtd.h>
3370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/mtd/partitions.h>
3470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
3570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <asm/mach-bcm63xx/bcm963xx_tag.h>
36f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski#include <asm/mach-bcm63xx/board_bcm963xx.h>
3770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
3870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#define BCM63XX_EXTENDED_SIZE	0xBFC00000	/* Extended flash address */
3970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
40678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski#define BCM63XX_MIN_CFE_SIZE	0x10000		/* always at least 64KiB */
41678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski#define BCM63XX_MIN_NVRAM_SIZE	0x10000		/* always at least 64KiB */
42678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski
43f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
44f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
4570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic int bcm63xx_detect_cfe(struct mtd_info *master)
4670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
4770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	char buf[9];
4870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int ret;
4970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	size_t retlen;
5070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
51329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy	ret = mtd_read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen,
52329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy		       (void *)buf);
53f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	buf[retlen] = 0;
54f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
55f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	if (ret)
56f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski		return ret;
57f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
58f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	if (strncmp("cfe-v", buf, 5) == 0)
59f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski		return 0;
60f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
61f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	/* very old CFE's do not have the cfe-v string, so check for magic */
62329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy	ret = mtd_read(master, BCM63XX_CFE_MAGIC_OFFSET, 8, &retlen,
63329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy		       (void *)buf);
6470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	buf[retlen] = 0;
6570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
66f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	return strncmp("CFE1CFE1", buf, 8);
6770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski}
6870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
6970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
7070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski					struct mtd_partition **pparts,
7170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski					struct mtd_part_parser_data *data)
7270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
7370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* CFE, NVRAM and global Linux are always present */
7470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int nrparts = 3, curpart = 0;
7570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	struct bcm_tag *buf;
7670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	struct mtd_partition *parts;
7770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int ret;
7870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	size_t retlen;
7970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	unsigned int rootfsaddr, kerneladdr, spareaddr;
8070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	unsigned int rootfslen, kernellen, sparelen, totallen;
81678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	unsigned int cfelen, nvramlen;
8270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int namelen = 0;
8370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int i;
84f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski	u32 computed_crc;
8570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
8670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (bcm63xx_detect_cfe(master))
8770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		return -EINVAL;
8870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
89678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	cfelen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_CFE_SIZE);
90678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	nvramlen = max_t(uint32_t, master->erasesize, BCM63XX_MIN_NVRAM_SIZE);
91678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski
9270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Allocate memory for buffer */
9370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	buf = vmalloc(sizeof(struct bcm_tag));
9470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (!buf)
9570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		return -ENOMEM;
9670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
9770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Get the tag */
98329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy	ret = mtd_read(master, cfelen, sizeof(struct bcm_tag), &retlen,
99329ad399a9b3adf52c90637b21ca029fcf7f8795Artem Bityutskiy		       (void *)buf);
100678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski
10170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (retlen != sizeof(struct bcm_tag)) {
10270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		vfree(buf);
10370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		return -EIO;
10470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
10570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
106f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski	computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)buf,
107f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski				offsetof(struct bcm_tag, header_crc));
108f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski	if (computed_crc == buf->header_crc) {
109f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		char *boardid = &(buf->board_id[0]);
110f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		char *tagversion = &(buf->tag_version[0]);
111f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski
112f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		sscanf(buf->kernel_address, "%u", &kerneladdr);
113f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		sscanf(buf->kernel_length, "%u", &kernellen);
114f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		sscanf(buf->total_length, "%u", &totallen);
115f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski
116f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		pr_info("CFE boot tag found with version %s and board type %s\n",
117f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski			tagversion, boardid);
118f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski
119f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
120f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		rootfsaddr = kerneladdr + kernellen;
121f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		spareaddr = roundup(totallen, master->erasesize) + cfelen;
122f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		sparelen = master->size - spareaddr - nvramlen;
123f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		rootfslen = spareaddr - rootfsaddr;
124f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski	} else {
125f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		pr_warn("CFE boot tag CRC invalid (expected %08x, actual %08x)\n",
126f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski			buf->header_crc, computed_crc);
127f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		kernellen = 0;
128f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		rootfslen = 0;
129f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		rootfsaddr = 0;
130f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		spareaddr = cfelen;
131f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski		sparelen = master->size - cfelen - nvramlen;
132f98872fc14ecb96f796443911b6bc4767e58e885Jonas Gorski	}
13370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
13470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Determine number of partitions */
13570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	namelen = 8;
13670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (rootfslen > 0) {
13770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		nrparts++;
13870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		namelen += 6;
13970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
14070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (kernellen > 0) {
14170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		nrparts++;
14270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		namelen += 6;
14370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
14470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
14570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Ask kernel for more memory */
14670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
14770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (!parts) {
14870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		vfree(buf);
14970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		return -ENOMEM;
15070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
15170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
15270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Start building partition list */
15370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].name = "CFE";
15470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].offset = 0;
155678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	parts[curpart].size = cfelen;
15670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	curpart++;
15770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
15870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (kernellen > 0) {
15970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].name = "kernel";
16070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].offset = kerneladdr;
16170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].size = kernellen;
16270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		curpart++;
16370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
16470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
16570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (rootfslen > 0) {
16670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].name = "rootfs";
16770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].offset = rootfsaddr;
16870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].size = rootfslen;
16970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		if (sparelen > 0)
17070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski			parts[curpart].size += sparelen;
17170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		curpart++;
17270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
17370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
17470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].name = "nvram";
175678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	parts[curpart].offset = master->size - nvramlen;
176678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	parts[curpart].size = nvramlen;
17770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
17870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Global partition "linux" to make easy firmware upgrade */
17970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	curpart++;
18070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].name = "linux";
181327c62c554a78af399938445094a7dc834b7fd0bJonas Gorski	parts[curpart].offset = cfelen;
182327c62c554a78af399938445094a7dc834b7fd0bJonas Gorski	parts[curpart].size = master->size - cfelen - nvramlen;
18370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
18470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	for (i = 0; i < nrparts; i++)
18570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		pr_info("Partition %d is %s offset %lx and length %lx\n", i,
18670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski			parts[i].name, (long unsigned int)(parts[i].offset),
18770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski			(long unsigned int)(parts[i].size));
18870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
18970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	pr_info("Spare partition is offset %x and length %x\n",	spareaddr,
19070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		sparelen);
19170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
19270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	*pparts = parts;
19370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	vfree(buf);
19470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
19570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	return nrparts;
19670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski};
19770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
19870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic struct mtd_part_parser bcm63xx_cfe_parser = {
19970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	.owner = THIS_MODULE,
20070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	.parse_fn = bcm63xx_parse_cfe_partitions,
20170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	.name = "bcm63xxpart",
20270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski};
20370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
20470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic int __init bcm63xx_cfe_parser_init(void)
20570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
20670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	return register_mtd_parser(&bcm63xx_cfe_parser);
20770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski}
20870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
20970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic void __exit bcm63xx_cfe_parser_exit(void)
21070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
21170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	deregister_mtd_parser(&bcm63xx_cfe_parser);
21270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski}
21370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
21470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskimodule_init(bcm63xx_cfe_parser_init);
21570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskimodule_exit(bcm63xx_cfe_parser_exit);
21670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
21770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_LICENSE("GPL");
21870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
21970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
22070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
22170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Jonas Gorski <jonas.gorski@gmail.com");
22270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_DESCRIPTION("MTD partitioning for BCM63XX CFE bootloaders");
223