bcm63xxpart.c revision 678eb9bb8114c47a7b89fd1288ff5dc760c53c1c
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
2770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/module.h>
2870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/kernel.h>
2970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/slab.h>
3070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/vmalloc.h>
3170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/mtd/mtd.h>
3270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <linux/mtd/partitions.h>
3370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
3470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#include <asm/mach-bcm63xx/bcm963xx_tag.h>
35f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski#include <asm/mach-bcm63xx/board_bcm963xx.h>
3670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
3770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski#define BCM63XX_EXTENDED_SIZE	0xBFC00000	/* Extended flash address */
3870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
39678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski#define BCM63XX_MIN_CFE_SIZE	0x10000		/* always at least 64KiB */
40678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski#define BCM63XX_MIN_NVRAM_SIZE	0x10000		/* always at least 64KiB */
41678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski
42f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski#define BCM63XX_CFE_MAGIC_OFFSET 0x4e0
43f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
4470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic int bcm63xx_detect_cfe(struct mtd_info *master)
4570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
4670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	char buf[9];
4770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int ret;
4870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	size_t retlen;
4970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
50f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	ret = master->read(master, BCM963XX_CFE_VERSION_OFFSET, 5, &retlen,
51f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski			   (void *)buf);
52f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	buf[retlen] = 0;
53f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
54f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	if (ret)
55f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski		return ret;
56f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
57f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	if (strncmp("cfe-v", buf, 5) == 0)
58f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski		return 0;
59f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski
60f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	/* very old CFE's do not have the cfe-v string, so check for magic */
61f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	ret = master->read(master, BCM63XX_CFE_MAGIC_OFFSET, 8, &retlen,
62f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski			   (void *)buf);
6370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	buf[retlen] = 0;
6470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
65f2d9739b8e0bc9bdcc972950dd433b5083edf72fJonas Gorski	return strncmp("CFE1CFE1", buf, 8);
6670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski}
6770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
6870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic int bcm63xx_parse_cfe_partitions(struct mtd_info *master,
6970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski					struct mtd_partition **pparts,
7070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski					struct mtd_part_parser_data *data)
7170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
7270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* CFE, NVRAM and global Linux are always present */
7370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int nrparts = 3, curpart = 0;
7470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	struct bcm_tag *buf;
7570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	struct mtd_partition *parts;
7670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int ret;
7770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	size_t retlen;
7870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	unsigned int rootfsaddr, kerneladdr, spareaddr;
7970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	unsigned int rootfslen, kernellen, sparelen, totallen;
80678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	unsigned int cfelen, nvramlen;
8170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int namelen = 0;
8270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	int i;
8370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	char *boardid;
8470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	char *tagversion;
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 */
98678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	ret = master->read(master, cfelen, sizeof(struct bcm_tag), &retlen,
99678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski			   (void *)buf);
100678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski
10170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (retlen != sizeof(struct bcm_tag)) {
10270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		vfree(buf);
10370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		return -EIO;
10470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
10570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
10670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	sscanf(buf->kernel_address, "%u", &kerneladdr);
10770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	sscanf(buf->kernel_length, "%u", &kernellen);
10870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	sscanf(buf->total_length, "%u", &totallen);
10970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	tagversion = &(buf->tag_version[0]);
11070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	boardid = &(buf->board_id[0]);
11170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
11270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	pr_info("CFE boot tag found with version %s and board type %s\n",
11370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		tagversion, boardid);
11470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
11570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
11670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	rootfsaddr = kerneladdr + kernellen;
117678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	spareaddr = roundup(totallen, master->erasesize) + cfelen;
118678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	sparelen = master->size - spareaddr - nvramlen;
11970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	rootfslen = spareaddr - rootfsaddr;
12070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
12170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Determine number of partitions */
12270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	namelen = 8;
12370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (rootfslen > 0) {
12470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		nrparts++;
12570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		namelen += 6;
12670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
12770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (kernellen > 0) {
12870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		nrparts++;
12970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		namelen += 6;
13070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
13170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
13270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Ask kernel for more memory */
13370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
13470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (!parts) {
13570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		vfree(buf);
13670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		return -ENOMEM;
13770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
13870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
13970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Start building partition list */
14070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].name = "CFE";
14170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].offset = 0;
142678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	parts[curpart].size = cfelen;
14370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	curpart++;
14470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
14570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (kernellen > 0) {
14670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].name = "kernel";
14770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].offset = kerneladdr;
14870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].size = kernellen;
14970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		curpart++;
15070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
15170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
15270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	if (rootfslen > 0) {
15370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].name = "rootfs";
15470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].offset = rootfsaddr;
15570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		parts[curpart].size = rootfslen;
15670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		if (sparelen > 0)
15770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski			parts[curpart].size += sparelen;
15870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		curpart++;
15970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	}
16070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
16170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].name = "nvram";
162678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	parts[curpart].offset = master->size - nvramlen;
163678eb9bb8114c47a7b89fd1288ff5dc760c53c1cJonas Gorski	parts[curpart].size = nvramlen;
16470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
16570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	/* Global partition "linux" to make easy firmware upgrade */
16670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	curpart++;
16770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].name = "linux";
16870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].offset = parts[0].size;
16970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	parts[curpart].size = master->size - parts[0].size - parts[3].size;
17070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
17170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	for (i = 0; i < nrparts; i++)
17270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		pr_info("Partition %d is %s offset %lx and length %lx\n", i,
17370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski			parts[i].name, (long unsigned int)(parts[i].offset),
17470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski			(long unsigned int)(parts[i].size));
17570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
17670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	pr_info("Spare partition is offset %x and length %x\n",	spareaddr,
17770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski		sparelen);
17870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
17970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	*pparts = parts;
18070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	vfree(buf);
18170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
18270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	return nrparts;
18370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski};
18470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
18570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic struct mtd_part_parser bcm63xx_cfe_parser = {
18670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	.owner = THIS_MODULE,
18770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	.parse_fn = bcm63xx_parse_cfe_partitions,
18870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	.name = "bcm63xxpart",
18970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski};
19070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
19170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic int __init bcm63xx_cfe_parser_init(void)
19270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
19370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	return register_mtd_parser(&bcm63xx_cfe_parser);
19470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski}
19570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
19670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskistatic void __exit bcm63xx_cfe_parser_exit(void)
19770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski{
19870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski	deregister_mtd_parser(&bcm63xx_cfe_parser);
19970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski}
20070a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
20170a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskimodule_init(bcm63xx_cfe_parser_init);
20270a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorskimodule_exit(bcm63xx_cfe_parser_exit);
20370a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas Gorski
20470a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_LICENSE("GPL");
20570a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
20670a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
20770a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
20870a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_AUTHOR("Jonas Gorski <jonas.gorski@gmail.com");
20970a3c167c4bf38b5ffd07d8506230ecc20ef7ab1Jonas GorskiMODULE_DESCRIPTION("MTD partitioning for BCM63XX CFE bootloaders");
210