/drivers/mtd/maps/bcm963xx-flash.c
C | 276 lines | 205 code | 44 blank | 27 comment | 22 complexity | 00e707747a8615071e4f4a573dd5047e MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
1/* 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> 5 * 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. 10 * 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. 15 * 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 19 */ 20 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> 29#include <linux/io.h> 30 31#include <asm/mach-bcm63xx/bcm963xx_tag.h> 32 33#define BCM63XX_BUSWIDTH 2 /* Buswidth */ 34#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */ 35 36#define PFX KBUILD_MODNAME ": " 37 38static struct mtd_partition *parsed_parts; 39 40static struct mtd_info *bcm963xx_mtd_info; 41 42static struct map_info bcm963xx_map = { 43 .name = "bcm963xx", 44 .bankwidth = BCM63XX_BUSWIDTH, 45}; 46 47static int parse_cfe_partitions(struct mtd_info *master, 48 struct mtd_partition **pparts) 49{ 50 /* CFE, NVRAM and global Linux are always present */ 51 int nrparts = 3, curpart = 0; 52 struct bcm_tag *buf; 53 struct mtd_partition *parts; 54 int ret; 55 size_t retlen; 56 unsigned int rootfsaddr, kerneladdr, spareaddr; 57 unsigned int rootfslen, kernellen, sparelen, totallen; 58 int namelen = 0; 59 int i; 60 char *boardid; 61 char *tagversion; 62 63 /* Allocate memory for buffer */ 64 buf = vmalloc(sizeof(struct bcm_tag)); 65 if (!buf) 66 return -ENOMEM; 67 68 /* Get the tag */ 69 ret = master->read(master, master->erasesize, sizeof(struct bcm_tag), 70 &retlen, (void *)buf); 71 if (retlen != sizeof(struct bcm_tag)) { 72 vfree(buf); 73 return -EIO; 74 } 75 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]); 81 82 printk(KERN_INFO PFX "CFE boot tag found with version %s " 83 "and board type %s\n", tagversion, boardid); 84 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; 90 91 /* Determine number of partitions */ 92 namelen = 8; 93 if (rootfslen > 0) { 94 nrparts++; 95 namelen += 6; 96 }; 97 if (kernellen > 0) { 98 nrparts++; 99 namelen += 6; 100 }; 101 102 /* Ask kernel for more memory */ 103 parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); 104 if (!parts) { 105 vfree(buf); 106 return -ENOMEM; 107 }; 108 109 /* Start building partition list */ 110 parts[curpart].name = "CFE"; 111 parts[curpart].offset = 0; 112 parts[curpart].size = master->erasesize; 113 curpart++; 114 115 if (kernellen > 0) { 116 parts[curpart].name = "kernel"; 117 parts[curpart].offset = kerneladdr; 118 parts[curpart].size = kernellen; 119 curpart++; 120 }; 121 122 if (rootfslen > 0) { 123 parts[curpart].name = "rootfs"; 124 parts[curpart].offset = rootfsaddr; 125 parts[curpart].size = rootfslen; 126 if (sparelen > 0) 127 parts[curpart].size += sparelen; 128 curpart++; 129 }; 130 131 parts[curpart].name = "nvram"; 132 parts[curpart].offset = master->size - master->erasesize; 133 parts[curpart].size = master->erasesize; 134 135 /* Global partition "linux" to make easy firmware upgrade */ 136 curpart++; 137 parts[curpart].name = "linux"; 138 parts[curpart].offset = parts[0].size; 139 parts[curpart].size = master->size - parts[0].size - parts[3].size; 140 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)); 146 147 printk(KERN_INFO PFX "Spare partition is %x offset and length %x\n", 148 spareaddr, sparelen); 149 *pparts = parts; 150 vfree(buf); 151 152 return nrparts; 153}; 154 155static int bcm963xx_detect_cfe(struct mtd_info *master) 156{ 157 int idoffset = 0x4e0; 158 static char idstring[8] = "CFE1CFE1"; 159 char buf[9]; 160 int ret; 161 size_t retlen; 162 163 ret = master->read(master, idoffset, 8, &retlen, (void *)buf); 164 buf[retlen] = 0; 165 printk(KERN_INFO PFX "Read Signature value of %s\n", buf); 166 167 return strncmp(idstring, buf, 8); 168} 169 170static int bcm963xx_probe(struct platform_device *pdev) 171{ 172 int err = 0; 173 int parsed_nr_parts = 0; 174 char *part_type; 175 struct resource *r; 176 177 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 178 if (!r) { 179 dev_err(&pdev->dev, "no resource supplied\n"); 180 return -ENODEV; 181 } 182 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"); 188 return -EIO; 189 } 190 191 dev_info(&pdev->dev, "0x%08lx at 0x%08x\n", 192 bcm963xx_map.size, bcm963xx_map.phys); 193 194 simple_map_init(&bcm963xx_map); 195 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) 201 goto probe_ok; 202 dev_err(&pdev->dev, "failed to probe using JEDEC\n"); 203 err = -EIO; 204 goto err_probe; 205 } 206 207probe_ok: 208 bcm963xx_mtd_info->owner = THIS_MODULE; 209 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, 215 &parsed_parts); 216 if (ret > 0) { 217 part_type = "CFE"; 218 parsed_nr_parts = ret; 219 } 220 } 221 } else { 222 dev_info(&pdev->dev, "unsupported bootloader\n"); 223 err = -ENODEV; 224 goto err_probe; 225 } 226 227 return mtd_device_register(bcm963xx_mtd_info, parsed_parts, 228 parsed_nr_parts); 229 230err_probe: 231 iounmap(bcm963xx_map.virt); 232 return err; 233} 234 235static int bcm963xx_remove(struct platform_device *pdev) 236{ 237 if (bcm963xx_mtd_info) { 238 mtd_device_unregister(bcm963xx_mtd_info); 239 map_destroy(bcm963xx_mtd_info); 240 } 241 242 if (bcm963xx_map.virt) { 243 iounmap(bcm963xx_map.virt); 244 bcm963xx_map.virt = 0; 245 } 246 247 return 0; 248} 249 250static struct platform_driver bcm63xx_mtd_dev = { 251 .probe = bcm963xx_probe, 252 .remove = bcm963xx_remove, 253 .driver = { 254 .name = "bcm963xx-flash", 255 .owner = THIS_MODULE, 256 }, 257}; 258 259static int __init bcm963xx_mtd_init(void) 260{ 261 return platform_driver_register(&bcm63xx_mtd_dev); 262} 263 264static void __exit bcm963xx_mtd_exit(void) 265{ 266 platform_driver_unregister(&bcm63xx_mtd_dev); 267} 268 269module_init(bcm963xx_mtd_init); 270module_exit(bcm963xx_mtd_exit); 271 272MODULE_LICENSE("GPL"); 273MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot"); 274MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>"); 275MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); 276MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");