2 * Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
3 * Mike Albon <malbon@openwrt.org>
4 * Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include <linux/init.h>
22 #include <linux/kernel.h>
23 #include <linux/slab.h>
24 #include <linux/module.h>
25 #include <linux/mtd/map.h>
26 #include <linux/mtd/mtd.h>
27 #include <linux/mtd/partitions.h>
28 #include <linux/vmalloc.h>
29 #include <linux/platform_device.h>
32 #include <asm/mach-bcm63xx/bcm963xx_tag.h>
34 #define BCM63XX_BUSWIDTH 2 /* Buswidth */
35 #define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
37 #define PFX KBUILD_MODNAME ": "
39 static struct mtd_partition
*parsed_parts
;
41 static struct mtd_info
*bcm963xx_mtd_info
;
43 static struct map_info bcm963xx_map
= {
45 .bankwidth
= BCM63XX_BUSWIDTH
,
48 static int parse_cfe_partitions(struct mtd_info
*master
,
49 struct mtd_partition
**pparts
)
51 /* CFE, NVRAM and global Linux are always present */
52 int nrparts
= 3, curpart
= 0;
54 struct mtd_partition
*parts
;
57 unsigned int rootfsaddr
, kerneladdr
, spareaddr
;
58 unsigned int rootfslen
, kernellen
, sparelen
, totallen
;
64 /* Allocate memory for buffer */
65 buf
= vmalloc(sizeof(struct bcm_tag
));
70 ret
= master
->read(master
, master
->erasesize
, sizeof(struct bcm_tag
),
71 &retlen
, (void *)buf
);
72 if (retlen
!= sizeof(struct bcm_tag
)) {
77 sscanf(buf
->kernel_address
, "%u", &kerneladdr
);
78 sscanf(buf
->kernel_length
, "%u", &kernellen
);
79 sscanf(buf
->total_length
, "%u", &totallen
);
80 tagversion
= &(buf
->tag_version
[0]);
81 boardid
= &(buf
->board_id
[0]);
83 printk(KERN_INFO PFX
"CFE boot tag found with version %s "
84 "and board type %s\n", tagversion
, boardid
);
86 kerneladdr
= kerneladdr
- BCM63XX_EXTENDED_SIZE
;
87 rootfsaddr
= kerneladdr
+ kernellen
;
88 spareaddr
= roundup(totallen
, master
->erasesize
) + master
->erasesize
;
89 sparelen
= master
->size
- spareaddr
- master
->erasesize
;
90 rootfslen
= spareaddr
- rootfsaddr
;
92 /* Determine number of partitions */
103 /* Ask kernel for more memory */
104 parts
= kzalloc(sizeof(*parts
) * nrparts
+ 10 * nrparts
, GFP_KERNEL
);
110 /* Start building partition list */
111 parts
[curpart
].name
= "CFE";
112 parts
[curpart
].offset
= 0;
113 parts
[curpart
].size
= master
->erasesize
;
117 parts
[curpart
].name
= "kernel";
118 parts
[curpart
].offset
= kerneladdr
;
119 parts
[curpart
].size
= kernellen
;
124 parts
[curpart
].name
= "rootfs";
125 parts
[curpart
].offset
= rootfsaddr
;
126 parts
[curpart
].size
= rootfslen
;
128 parts
[curpart
].size
+= sparelen
;
132 parts
[curpart
].name
= "nvram";
133 parts
[curpart
].offset
= master
->size
- master
->erasesize
;
134 parts
[curpart
].size
= master
->erasesize
;
136 /* Global partition "linux" to make easy firmware upgrade */
138 parts
[curpart
].name
= "linux";
139 parts
[curpart
].offset
= parts
[0].size
;
140 parts
[curpart
].size
= master
->size
- parts
[0].size
- parts
[3].size
;
142 for (i
= 0; i
< nrparts
; i
++)
143 printk(KERN_INFO PFX
"Partition %d is %s offset %lx and "
144 "length %lx\n", i
, parts
[i
].name
,
145 (long unsigned int)(parts
[i
].offset
),
146 (long unsigned int)(parts
[i
].size
));
148 printk(KERN_INFO PFX
"Spare partition is %x offset and length %x\n",
149 spareaddr
, sparelen
);
156 static int bcm963xx_detect_cfe(struct mtd_info
*master
)
158 int idoffset
= 0x4e0;
159 static char idstring
[8] = "CFE1CFE1";
164 ret
= master
->read(master
, idoffset
, 8, &retlen
, (void *)buf
);
166 printk(KERN_INFO PFX
"Read Signature value of %s\n", buf
);
168 return strncmp(idstring
, buf
, 8);
171 static int bcm963xx_probe(struct platform_device
*pdev
)
174 int parsed_nr_parts
= 0;
178 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
180 dev_err(&pdev
->dev
, "no resource supplied\n");
184 bcm963xx_map
.phys
= r
->start
;
185 bcm963xx_map
.size
= resource_size(r
);
186 bcm963xx_map
.virt
= ioremap(r
->start
, resource_size(r
));
187 if (!bcm963xx_map
.virt
) {
188 dev_err(&pdev
->dev
, "failed to ioremap\n");
192 dev_info(&pdev
->dev
, "0x%08lx at 0x%08x\n",
193 bcm963xx_map
.size
, bcm963xx_map
.phys
);
195 simple_map_init(&bcm963xx_map
);
197 bcm963xx_mtd_info
= do_map_probe("cfi_probe", &bcm963xx_map
);
198 if (!bcm963xx_mtd_info
) {
199 dev_err(&pdev
->dev
, "failed to probe using CFI\n");
200 bcm963xx_mtd_info
= do_map_probe("jedec_probe", &bcm963xx_map
);
201 if (bcm963xx_mtd_info
)
203 dev_err(&pdev
->dev
, "failed to probe using JEDEC\n");
209 bcm963xx_mtd_info
->owner
= THIS_MODULE
;
211 /* This is mutually exclusive */
212 if (bcm963xx_detect_cfe(bcm963xx_mtd_info
) == 0) {
213 dev_info(&pdev
->dev
, "CFE bootloader detected\n");
214 if (parsed_nr_parts
== 0) {
215 int ret
= parse_cfe_partitions(bcm963xx_mtd_info
,
219 parsed_nr_parts
= ret
;
223 dev_info(&pdev
->dev
, "unsupported bootloader\n");
228 return mtd_device_register(bcm963xx_mtd_info
, parsed_parts
,
232 iounmap(bcm963xx_map
.virt
);
236 static int bcm963xx_remove(struct platform_device
*pdev
)
238 if (bcm963xx_mtd_info
) {
239 mtd_device_unregister(bcm963xx_mtd_info
);
240 map_destroy(bcm963xx_mtd_info
);
243 if (bcm963xx_map
.virt
) {
244 iounmap(bcm963xx_map
.virt
);
245 bcm963xx_map
.virt
= 0;
251 static struct platform_driver bcm63xx_mtd_dev
= {
252 .probe
= bcm963xx_probe
,
253 .remove
= bcm963xx_remove
,
255 .name
= "bcm963xx-flash",
256 .owner
= THIS_MODULE
,
260 static int __init
bcm963xx_mtd_init(void)
262 return platform_driver_register(&bcm63xx_mtd_dev
);
265 static void __exit
bcm963xx_mtd_exit(void)
267 platform_driver_unregister(&bcm63xx_mtd_dev
);
270 module_init(bcm963xx_mtd_init
);
271 module_exit(bcm963xx_mtd_exit
);
273 MODULE_LICENSE("GPL");
274 MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot");
275 MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
276 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
277 MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");