1/* 2 * Broadcom SiliconBackplane chipcommon serial flash interface 3 * 4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: sflash.c,v 1.6 2011-02-10 10:55:57 Exp $ 19 */ 20 21#include <linux/config.h> 22#include <linux/module.h> 23#include <linux/slab.h> 24#include <linux/ioport.h> 25#include <linux/mtd/compatmac.h> 26#include <linux/mtd/mtd.h> 27#include <linux/mtd/partitions.h> 28#include <linux/errno.h> 29#include <linux/pci.h> 30#include <linux/delay.h> 31#include <asm/io.h> 32 33#include <typedefs.h> 34#include <osl.h> 35#include <bcmutils.h> 36#include <bcmdevs.h> 37#include <bcmnvram.h> 38#include <siutils.h> 39#include <hndpci.h> 40#include <pcicfg.h> 41#include <hndsoc.h> 42#include <sbchipc.h> 43#include <sflash.h> 44 45#ifdef CONFIG_MTD_PARTITIONS 46extern struct mtd_partition * init_mtd_partitions(struct mtd_info *mtd, size_t size); 47#endif 48 49 50struct sflash_mtd { 51 si_t *sih; 52 chipcregs_t *cc; 53 struct mtd_info mtd; 54 struct mtd_erase_region_info region; 55}; 56 57/* Private global state */ 58static struct sflash_mtd sflash; 59 60static int 61sflash_mtd_poll(struct sflash_mtd *sflash, unsigned int offset, int timeout) 62{ 63 int now = jiffies; 64 int ret = 0; 65 66 for (;;) { 67 if (!sflash_poll(sflash->sih, sflash->cc, offset)) 68 break; 69 70 if (time_after((unsigned long)jiffies, (unsigned long)now + timeout)) { 71 if (!sflash_poll(sflash->sih, sflash->cc, offset)) 72 break; 73 74 printk(KERN_ERR "sflash: timeout\n"); 75 ret = -ETIMEDOUT; 76 break; 77 } 78 udelay(1); 79 } 80 81 return ret; 82} 83 84static int 85sflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 86{ 87 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; 88 int bytes, ret = 0; 89 90 /* Check address range */ 91 if (!len) 92 return 0; 93 if ((from + len) > mtd->size) 94 return -EINVAL; 95 96 mutex_lock(mtd->mutex); 97 98 *retlen = 0; 99 while (len) { 100 if ((bytes = sflash_read(sflash->sih, sflash->cc, (uint) from, len, buf)) < 0) { 101 ret = bytes; 102 break; 103 } 104 from += (loff_t) bytes; 105 len -= bytes; 106 buf += bytes; 107 *retlen += bytes; 108 } 109 110 mutex_unlock(mtd->mutex); 111 return ret; 112} 113 114static int 115sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) 116{ 117 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; 118 int bytes, ret = 0; 119 120 /* Check address range */ 121 if (!len) 122 return 0; 123 if ((to + len) > mtd->size) 124 return -EINVAL; 125 126 mutex_lock(mtd->mutex); 127 *retlen = 0; 128 while (len) { 129 if ((bytes = sflash_write(sflash->sih, sflash->cc, (uint) to, len, buf)) < 0) { 130 ret = bytes; 131 break; 132 } 133 if ((ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ))) 134 break; 135 to += (loff_t) bytes; 136 len -= bytes; 137 buf += bytes; 138 *retlen += bytes; 139 } 140 141 mutex_unlock(mtd->mutex); 142 return ret; 143} 144 145static int 146sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) 147{ 148 struct sflash_mtd *sflash = (struct sflash_mtd *) mtd->priv; 149 int i, j, ret = 0; 150 unsigned int addr, len; 151 152 /* Check address range */ 153 if (!erase->len) 154 return 0; 155 if ((erase->addr + erase->len) > mtd->size) 156 return -EINVAL; 157 158 mutex_lock(mtd->mutex); 159 addr = erase->addr; 160 len = erase->len; 161 162 /* Ensure that requested region is aligned */ 163 for (i = 0; i < mtd->numeraseregions; i++) { 164 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { 165 if (addr == mtd->eraseregions[i].offset + 166 mtd->eraseregions[i].erasesize * j && 167 len >= mtd->eraseregions[i].erasesize) { 168 if ((ret = sflash_erase(sflash->sih, sflash->cc, addr)) < 0) 169 break; 170 if ((ret = sflash_mtd_poll(sflash, addr, 10 * HZ))) 171 break; 172 addr += mtd->eraseregions[i].erasesize; 173 len -= mtd->eraseregions[i].erasesize; 174 } 175 } 176 if (ret) 177 break; 178 } 179 180 /* Set erase status */ 181 if (ret) 182 erase->state = MTD_ERASE_FAILED; 183 else 184 erase->state = MTD_ERASE_DONE; 185 186 mutex_unlock(mtd->mutex); 187 188 /* Call erase callback */ 189 if (erase->callback) 190 erase->callback(erase); 191 192 return ret; 193} 194 195#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) 196#define sflash_mtd_init init_module 197#define sflash_mtd_exit cleanup_module 198#endif 199 200static int __init 201sflash_mtd_init(void) 202{ 203 int ret = 0; 204 struct sflash *info; 205 struct pci_dev *dev = NULL; 206#ifdef CONFIG_MTD_PARTITIONS 207 struct mtd_partition *parts; 208 int i; 209#endif 210 211 list_for_each_entry(dev, &((pci_find_bus(0, 0))->devices), bus_list) { 212 if ((dev != NULL) && (dev->device == CC_CORE_ID)) 213 break; 214 } 215 216 if (dev == NULL) { 217 printk(KERN_ERR "sflash: chipcommon not found\n"); 218 return -ENODEV; 219 } 220 221 memset(&sflash, 0, sizeof(struct sflash_mtd)); 222 223 /* attach to the backplane */ 224 if (!(sflash.sih = si_kattach(SI_OSH))) { 225 printk(KERN_ERR "sflash: error attaching to backplane\n"); 226 ret = -EIO; 227 goto fail; 228 } 229 230 /* Map registers and flash base */ 231 if (!(sflash.cc = ioremap_nocache( 232 pci_resource_start(dev, 0), 233 pci_resource_len(dev, 0)))) { 234 printk(KERN_ERR "sflash: error mapping registers\n"); 235 ret = -EIO; 236 goto fail; 237 } 238 239 /* Initialize serial flash access */ 240 if (!(info = sflash_init(sflash.sih, sflash.cc))) { 241 printk(KERN_ERR "sflash: found no supported devices\n"); 242 ret = -ENODEV; 243 goto fail; 244 } 245 246 /* Setup region info */ 247 sflash.region.offset = 0; 248 sflash.region.erasesize = info->blocksize; 249 sflash.region.numblocks = info->numblocks; 250 if (sflash.region.erasesize > sflash.mtd.erasesize) 251 sflash.mtd.erasesize = sflash.region.erasesize; 252 sflash.mtd.size = info->size; 253 sflash.mtd.numeraseregions = 1; 254 255 /* Register with MTD */ 256 sflash.mtd.name = "sflash"; 257 sflash.mtd.type = MTD_NORFLASH; 258 sflash.mtd.flags = MTD_CAP_NORFLASH; 259 sflash.mtd.eraseregions = &sflash.region; 260 sflash.mtd.erase = sflash_mtd_erase; 261 sflash.mtd.read = sflash_mtd_read; 262 sflash.mtd.write = sflash_mtd_write; 263 sflash.mtd.writesize = 1; 264 sflash.mtd.priv = &sflash; 265 sflash.mtd.owner = THIS_MODULE; 266 sflash.mtd.mutex = partitions_mutex_init(); 267 if (!sflash.mtd.mutex) 268 return -ENOMEM; 269 270#ifdef CONFIG_MTD_PARTITIONS 271 parts = init_mtd_partitions(&sflash.mtd, sflash.mtd.size); 272 for (i = 0; parts[i].name; i++); 273 ret = add_mtd_partitions(&sflash.mtd, parts, i); 274 if (ret) { 275 printk(KERN_ERR "sflash: add_mtd failed\n"); 276 goto fail; 277 } 278#endif 279 280 return 0; 281 282fail: 283 if (sflash.cc) 284 iounmap((void *) sflash.cc); 285 if (sflash.sih) 286 si_detach(sflash.sih); 287 return ret; 288} 289 290static void __exit 291sflash_mtd_exit(void) 292{ 293#ifdef CONFIG_MTD_PARTITIONS 294 del_mtd_partitions(&sflash.mtd); 295#else 296 del_mtd_device(&sflash.mtd); 297#endif 298 iounmap((void *) sflash.cc); 299 si_detach(sflash.sih); 300} 301 302module_init(sflash_mtd_init); 303module_exit(sflash_mtd_exit); 304