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/mtd/map.h>
25 #include <linux/mtd/mtd.h>
26 #include <linux/mtd/partitions.h>
27 #include <linux/vmalloc.h>
28 #include <linux/platform_device.h>
31 #include <asm/mach-bcm63xx/bcm963xx_tag.h>
33 #define BCM63XX_BUSWIDTH 2 /* Buswidth */
34 #define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
36 #define PFX KBUILD_MODNAME ": "
38 static struct mtd_partition
*parsed_parts
;
40 static struct mtd_info
*bcm963xx_mtd_info
;
42 static struct map_info bcm963xx_map
= {
44 .bankwidth
= BCM63XX_BUSWIDTH
,
47 static int parse_cfe_partitions(struct mtd_info
*master
,
48 struct mtd_partition
**pparts
)
50 /* CFE, NVRAM and global Linux are always present */
51 int nrparts
= 3, curpart
= 0;
53 struct mtd_partition
*parts
;
56 unsigned int rootfsaddr
, kerneladdr
, spareaddr
;
57 unsigned int rootfslen
, kernellen
, sparelen
, totallen
;
63 /* Allocate memory for buffer */
64 buf
= vmalloc(sizeof(struct bcm_tag
));
69 ret
= master
->read(master
, master
->erasesize
, sizeof(struct bcm_tag
),
70 &retlen
, (void *)buf
);
71 if (retlen
!= sizeof(struct bcm_tag
)) {
76 sscanf(buf
->kernel_address
, "%u", &kerneladdr
);
77 sscanf(buf
->kernel_length
, "%u", &kernellen
);
78 sscanf(buf
->total_length
, "%u", &totallen
);
79 tagversion
= &(buf
->tag_version
[0]);
80 boardid
= &(buf
->board_id
[0]);
82 printk(KERN_INFO PFX
"CFE boot tag found with version %s "
83 "and board type %s\n", tagversion
, boardid
);
85 kerneladdr
= kerneladdr
- BCM63XX_EXTENDED_SIZE
;
86 rootfsaddr
= kerneladdr
+ kernellen
;
87 spareaddr
= roundup(totallen
, master
->erasesize
) + master
->erasesize
;
88 sparelen
= master
->size
- spareaddr
- master
->erasesize
;
89 rootfslen
= spareaddr
- rootfsaddr
;
91 /* Determine number of partitions */
102 /* Ask kernel for more memory */
103 parts
= kzalloc(sizeof(*parts
) * nrparts
+ 10 * nrparts
, GFP_KERNEL
);
109 /* Start building partition list */
110 parts
[curpart
].name
= "CFE";
111 parts
[curpart
].offset
= 0;
112 parts
[curpart
].size
= master
->erasesize
;
116 parts
[curpart
].name
= "kernel";
117 parts
[curpart
].offset
= kerneladdr
;
118 parts
[curpart
].size
= kernellen
;
123 parts
[curpart
].name
= "rootfs";
124 parts
[curpart
].offset
= rootfsaddr
;
125 parts
[curpart
].size
= rootfslen
;
127 parts
[curpart
].size
+= sparelen
;
131 parts
[curpart
].name
= "nvram";
132 parts
[curpart
].offset
= master
->size
- master
->erasesize
;
133 parts
[curpart
].size
= master
->erasesize
;
135 /* Global partition "linux" to make easy firmware upgrade */
137 parts
[curpart
].name
= "linux";
138 parts
[curpart
].offset
= parts
[0].size
;
139 parts
[curpart
].size
= master
->size
- parts
[0].size
- parts
[3].size
;
141 for (i
= 0; i
< nrparts
; i
++)
142 printk(KERN_INFO PFX
"Partition %d is %s offset %lx and "
143 "length %lx\n", i
, parts
[i
].name
,
144 (long unsigned int)(parts
[i
].offset
),
145 (long unsigned int)(parts
[i
].size
));
147 printk(KERN_INFO PFX
"Spare partition is %x offset and length %x\n",
148 spareaddr
, sparelen
);
155 static int bcm963xx_detect_cfe(struct mtd_info
*master
)
157 int idoffset
= 0x4e0;
158 static char idstring
[8] = "CFE1CFE1";
163 ret
= master
->read(master
, idoffset
, 8, &retlen
, (void *)buf
);
165 printk(KERN_INFO PFX
"Read Signature value of %s\n", buf
);
167 return strncmp(idstring
, buf
, 8);
170 static int bcm963xx_probe(struct platform_device
*pdev
)
173 int parsed_nr_parts
= 0;
177 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
179 dev_err(&pdev
->dev
, "no resource supplied\n");
183 bcm963xx_map
.phys
= r
->start
;
184 bcm963xx_map
.size
= resource_size(r
);
185 bcm963xx_map
.virt
= ioremap(r
->start
, resource_size(r
));
186 if (!bcm963xx_map
.virt
) {
187 dev_err(&pdev
->dev
, "failed to ioremap\n");
191 dev_info(&pdev
->dev
, "0x%08lx at 0x%08x\n",
192 bcm963xx_map
.size
, bcm963xx_map
.phys
);
194 simple_map_init(&bcm963xx_map
);
196 bcm963xx_mtd_info
= do_map_probe("cfi_probe", &bcm963xx_map
);
197 if (!bcm963xx_mtd_info
) {
198 dev_err(&pdev
->dev
, "failed to probe using CFI\n");
199 bcm963xx_mtd_info
= do_map_probe("jedec_probe", &bcm963xx_map
);
200 if (bcm963xx_mtd_info
)
202 dev_err(&pdev
->dev
, "failed to probe using JEDEC\n");
208 bcm963xx_mtd_info
->owner
= THIS_MODULE
;
210 /* This is mutually exclusive */
211 if (bcm963xx_detect_cfe(bcm963xx_mtd_info
) == 0) {
212 dev_info(&pdev
->dev
, "CFE bootloader detected\n");
213 if (parsed_nr_parts
== 0) {
214 int ret
= parse_cfe_partitions(bcm963xx_mtd_info
,
218 parsed_nr_parts
= ret
;
222 dev_info(&pdev
->dev
, "unsupported bootloader\n");
227 return mtd_device_register(bcm963xx_mtd_info
, parsed_parts
,
231 iounmap(bcm963xx_map
.virt
);
235 static int bcm963xx_remove(struct platform_device
*pdev
)
237 if (bcm963xx_mtd_info
) {
238 mtd_device_unregister(bcm963xx_mtd_info
);
239 map_destroy(bcm963xx_mtd_info
);
242 if (bcm963xx_map
.virt
) {
243 iounmap(bcm963xx_map
.virt
);
244 bcm963xx_map
.virt
= 0;
250 static struct platform_driver bcm63xx_mtd_dev
= {
251 .probe
= bcm963xx_probe
,
252 .remove
= bcm963xx_remove
,
254 .name
= "bcm963xx-flash",
255 .owner
= THIS_MODULE
,
259 static int __init
bcm963xx_mtd_init(void)
261 return platform_driver_register(&bcm63xx_mtd_dev
);
264 static void __exit
bcm963xx_mtd_exit(void)
266 platform_driver_unregister(&bcm63xx_mtd_dev
);
269 module_init(bcm963xx_mtd_init
);
270 module_exit(bcm963xx_mtd_exit
);
272 MODULE_LICENSE("GPL");
273 MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot");
274 MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
275 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
276 MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");