3 * Copyright (C) 2006 Florian Fainelli <florian@openwrt.org>
4 * Mike Albon <malbon@openwrt.org>
5 * Copyright (C) $Date$ $Author$
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 /* This is the BCM963xx flash map driver, in its actual state it only supports BCM96348 devices
23 * this driver is able to manage both bootloader we found on these boards : CFE and RedBoot
26 * - this bootloader allows us to parse partitions and therefore deduce the MTD partition table
29 * - CFE partitionning can be detected as for BCM947xx devices
34 #include <linux/init.h>
35 #include <linux/kernel.h>
36 #include <linux/mtd/map.h>
37 #include <linux/mtd/mtd.h>
38 #include <linux/mtd/partitions.h>
39 #include <linux/vmalloc.h>
42 #define WINDOW_ADDR 0x1FC00000 /* Real address of the flash */
43 #define WINDOW_SIZE 0x400000 /* Size of flash */
44 #define BUSWIDTH 2 /* Buswidth */
45 #define EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
46 #define IMAGE_LEN 10 /* Length of Length Field */
47 #define ADDRESS_LEN 12 /* Length of Address field */
48 #define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y))
50 extern int boot_loader_type
; /* For RedBoot / CFE detection */
51 extern int parse_redboot_partitions(struct mtd_info
*master
, struct mtd_partition
**pparts
, unsigned long fis_origin
);
52 static struct mtd_partition
*parsed_parts
;
54 static void __exit
bcm963xx_mtd_cleanup(void);
56 static struct mtd_info
*bcm963xx_mtd_info
;
58 static struct map_info bcm963xx_map
= {
61 .bankwidth
= BUSWIDTH
,
66 int parse_cfe_partitions( struct mtd_info
*master
, struct mtd_partition
**pparts
)
68 int nrparts
= 2, curpart
= 0; // CFE and NVRAM always present.
69 struct bcm963xx_cfe_map
{
70 unsigned char tagVersion
[4]; // Version of the image tag
71 unsigned char sig_1
[20]; // Company Line 1
72 unsigned char sig_2
[14]; // Company Line 2
73 unsigned char chipid
[6]; // Chip this image is for
74 unsigned char boardid
[16]; // Board name
75 unsigned char bigEndian
[2]; // Map endianness -- 1 BE 0 LE
76 unsigned char totalLength
[IMAGE_LEN
]; //Total length of image
77 unsigned char cfeAddress
[ADDRESS_LEN
]; // Address in memory of CFE
78 unsigned char cfeLength
[IMAGE_LEN
]; // Size of CFE
79 unsigned char rootAddress
[ADDRESS_LEN
]; // Address in memory of rootfs
80 unsigned char rootLength
[IMAGE_LEN
]; // Size of rootfs
81 unsigned char kernelAddress
[ADDRESS_LEN
]; // Address in memory of kernel
82 unsigned char kernelLength
[IMAGE_LEN
]; // Size of kernel
83 unsigned char dualImage
[2]; // Unused at present
84 unsigned char inactiveFlag
[2]; // Unused at present
85 unsigned char reserved1
[74]; // Reserved area not in use
86 unsigned char imageCRC
[4]; // CRC32 of images
87 unsigned char reserved2
[16]; // Unused at present
88 unsigned char headerCRC
[4]; // CRC32 of header excluding tagVersion
89 unsigned char reserved3
[16]; // Unused at present
91 struct mtd_partition
*parts
;
94 unsigned int rootfsaddr
, kerneladdr
, spareaddr
;
95 unsigned int rootfslen
, kernellen
, sparelen
, totallen
;
98 // Allocate memory for buffer
99 buf
= vmalloc(sizeof(struct bcm963xx_cfe_map
));
105 ret
= master
->read(master
,master
->erasesize
,sizeof(struct bcm963xx_cfe_map
), &retlen
, (void *)buf
);
106 if (retlen
!= sizeof(struct bcm963xx_cfe_map
)){
110 printk("bcm963xx: CFE boot tag found with version %s and board type %s.\n",buf
->tagVersion
,buf
->boardid
);
111 // Get the values and calculate
112 sscanf(buf
->rootAddress
,"%u", &rootfsaddr
);
113 rootfsaddr
= rootfsaddr
- EXTENDED_SIZE
;
114 sscanf(buf
->rootLength
, "%u", &rootfslen
);
115 sscanf(buf
->kernelAddress
, "%u", &kerneladdr
);
116 kerneladdr
= kerneladdr
- EXTENDED_SIZE
;
117 sscanf(buf
->kernelLength
, "%u", &kernellen
);
118 sscanf(buf
->totalLength
, "%u", &totallen
);
119 spareaddr
= ROUNDUP(totallen
,master
->erasesize
) + master
->erasesize
;
120 sparelen
= master
->size
- spareaddr
- master
->erasesize
;
121 // Determine number of partitions
135 // Ask kernel for more memory.
136 parts
= kmalloc(sizeof(*parts
)*nrparts
+10*nrparts
, GFP_KERNEL
);
141 memset(parts
,0,sizeof(*parts
)*nrparts
+10*nrparts
);
142 // Start building partition list
143 parts
[curpart
].name
= "CFE";
144 parts
[curpart
].offset
= 0;
145 parts
[curpart
].size
= master
->erasesize
;
148 parts
[curpart
].name
= "Kernel";
149 parts
[curpart
].offset
= kerneladdr
;
150 parts
[curpart
].size
= kernellen
;
154 parts
[curpart
].name
= "Rootfs";
155 parts
[curpart
].offset
= rootfsaddr
;
156 parts
[curpart
].size
= rootfslen
;
160 parts
[curpart
].name
= "OpenWrt";
161 parts
[curpart
].offset
= spareaddr
;
162 parts
[curpart
].size
= sparelen
;
165 parts
[curpart
].name
= "NVRAM";
166 parts
[curpart
].offset
= master
->size
- master
->erasesize
;
167 parts
[curpart
].size
= master
->erasesize
;
168 for (i
= 0; i
< nrparts
; i
++) {
169 printk("bcm963xx: Partition %d is %s offset %x and length %x\n", i
, parts
[i
].name
, parts
[i
].offset
, parts
[i
].size
);
176 static struct mtd_partition bcm963xx_parts
[] = {
177 { name
: "bootloader", size
: 0, offset
: 0, mask_flags
: MTD_WRITEABLE
},
178 { name
: "rootfs", size
: 0, offset
: 0},
179 { name
: "jffs2", size
: 5 * 0x10000, offset
: 57*0x10000}
182 static int bcm963xx_parts_size
= sizeof(bcm963xx_parts
) / sizeof(bcm963xx_parts
[0]);
184 static int bcm963xx_detect_cfe(struct mtd_info
*master
)
186 int idoffset
= 0x4e0;
187 static char idstring
[8] = "CFE1CFE1";
192 ret
= master
->read(master
, idoffset
, 8, &retlen
, (void *)buf
);
193 printk("bcm963xx: Read Signature value of %s\n", buf
);
194 return strcmp(idstring
,buf
);
197 static int __init
bcm963xx_mtd_init(void)
199 printk("bcm963xx: 0x%08x at 0x%08x\n", WINDOW_SIZE
, WINDOW_ADDR
);
200 bcm963xx_map
.virt
= ioremap(WINDOW_ADDR
, WINDOW_SIZE
);
202 if (!bcm963xx_map
.virt
) {
203 printk("bcm963xx: Failed to ioremap\n");
207 simple_map_init(&bcm963xx_map
);
209 bcm963xx_mtd_info
= do_map_probe("cfi_probe", &bcm963xx_map
);
211 if (bcm963xx_mtd_info
) {
212 bcm963xx_mtd_info
->owner
= THIS_MODULE
;
214 //if (boot_loader_type == BOOT_CFE)
215 if (bcm963xx_detect_cfe(bcm963xx_mtd_info
) == 0)
217 int parsed_nr_parts
= 0;
219 printk("bcm963xx: CFE bootloader detected\n");
220 //add_mtd_device(bcm963xx_mtd_info);
221 //add_mtd_partitions(bcm963xx_mtd_info, bcm963xx_parts, bcm963xx_parts_size);
222 if (parsed_nr_parts
== 0) {
223 int ret
= parse_cfe_partitions(bcm963xx_mtd_info
, &parsed_parts
);
226 parsed_nr_parts
= ret
;
229 add_mtd_partitions(bcm963xx_mtd_info
, parsed_parts
, parsed_nr_parts
);
234 int parsed_nr_parts
= 0;
237 if (bcm963xx_mtd_info
->size
> 0x00400000) {
238 printk("Support for extended flash memory size : 0x%08X ; ONLY 64MBIT SUPPORT\n", bcm963xx_mtd_info
->size
);
239 bcm963xx_map
.virt
= (unsigned long)(EXTENDED_SIZE
);
242 #ifdef CONFIG_MTD_REDBOOT_PARTS
243 if (parsed_nr_parts
== 0) {
244 int ret
= parse_redboot_partitions(bcm963xx_mtd_info
, &parsed_parts
, 0);
246 part_type
= "RedBoot";
247 parsed_nr_parts
= ret
;
251 add_mtd_partitions(bcm963xx_mtd_info
, parsed_parts
, parsed_nr_parts
);
256 iounmap(bcm963xx_map
.virt
);
260 static void __exit
bcm963xx_mtd_cleanup(void)
262 if (bcm963xx_mtd_info
) {
263 del_mtd_partitions(bcm963xx_mtd_info
);
264 map_destroy(bcm963xx_mtd_info
);
267 if (bcm963xx_map
.virt
) {
268 iounmap(bcm963xx_map
.virt
);
269 bcm963xx_map
.virt
= 0;
273 module_init(bcm963xx_mtd_init
);
274 module_exit(bcm963xx_mtd_cleanup
);
276 MODULE_LICENSE("GPL");
277 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org> Mike Albon <malbon@openwrt.org>");