1/* 2 * Broadcom SiliconBackplane chipcommon serial flash interface 3 * 4 * Copyright (C) 2013, 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$ 19 */ 20 21#include <linux/version.h> 22 23#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) 24#include <linux/config.h> 25#endif 26#include <linux/module.h> 27#include <linux/slab.h> 28#include <linux/ioport.h> 29#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) 30#include <linux/mtd/compatmac.h> 31#endif 32#include <linux/mtd/mtd.h> 33#include <linux/mtd/partitions.h> 34#include <linux/errno.h> 35#include <linux/pci.h> 36#include <linux/delay.h> 37#include <asm/io.h> 38 39#include <typedefs.h> 40#include <osl.h> 41#include <bcmutils.h> 42#include <bcmdevs.h> 43#include <bcmnvram.h> 44#include <siutils.h> 45#include <hndpci.h> 46#include <pcicfg.h> 47#include <hndsoc.h> 48#include <hndsflash.h> 49 50 51#ifdef CONFIG_MTD_PARTITIONS 52extern struct mtd_partition * 53init_mtd_partitions(hndsflash_t *sfl, struct mtd_info *mtd, size_t size); 54#endif 55 56extern void *partitions_lock_init(void); 57#define BCMSFLASH_LOCK(lock) if (lock) spin_lock(lock) 58#define BCMSFLASH_UNLOCK(lock) if (lock) spin_unlock(lock) 59 60struct bcmsflash_mtd { 61 si_t *sih; 62 hndsflash_t *sfl; 63 struct mtd_info mtd; 64 struct mtd_erase_region_info region; 65}; 66 67/* Private global state */ 68static struct bcmsflash_mtd bcmsflash; 69 70static int 71bcmsflash_mtd_poll(hndsflash_t *sfl, unsigned int offset, int timeout) 72{ 73 int now = jiffies; 74 int ret = 0; 75 76 for (;;) { 77 if (!hndsflash_poll(sfl, offset)) 78 break; 79 80 if (time_after((unsigned long)jiffies, (unsigned long)now + timeout)) { 81 if (!hndsflash_poll(sfl, offset)) 82 break; 83 84 printk(KERN_ERR "sflash: timeout\n"); 85 ret = -ETIMEDOUT; 86 break; 87 } 88 udelay(1); 89 } 90 91 return ret; 92} 93 94static int 95bcmsflash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 96{ 97 hndsflash_t *sfl = ((struct bcmsflash_mtd *)mtd->priv)->sfl; 98 int bytes, ret = 0; 99 100 /* Check address range */ 101 if (!len) 102 return 0; 103 if ((from + len) > mtd->size) 104 return -EINVAL; 105 106 BCMSFLASH_LOCK(mtd->mlock); 107 108 *retlen = 0; 109 while (len) { 110 if ((bytes = hndsflash_read(sfl, (uint)from, len, buf)) 111 < 0) { 112 ret = bytes; 113 break; 114 } 115 from += (loff_t) bytes; 116 len -= bytes; 117 buf += bytes; 118 *retlen += bytes; 119 } 120 121 BCMSFLASH_UNLOCK(mtd->mlock); 122 return ret; 123} 124 125static int 126bcmsflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) 127{ 128 hndsflash_t *sfl = ((struct bcmsflash_mtd *)mtd->priv)->sfl; 129 int bytes, ret = 0; 130 131 /* Check address range */ 132 if (!len) 133 return 0; 134 if ((to + len) > mtd->size) 135 return -EINVAL; 136 137 BCMSFLASH_LOCK(mtd->mlock); 138 *retlen = 0; 139 while (len) { 140 if ((bytes = hndsflash_write(sfl, (uint)to, len, (u_char *)buf)) 141 < 0) { 142 ret = bytes; 143 break; 144 } 145 if ((ret = bcmsflash_mtd_poll(sfl, (unsigned int)to, HZ))) 146 break; 147 to += (loff_t) bytes; 148 len -= bytes; 149 buf += bytes; 150 *retlen += bytes; 151 } 152 153 BCMSFLASH_UNLOCK(mtd->mlock); 154 return ret; 155} 156 157static int 158bcmsflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) 159{ 160 hndsflash_t *sfl = ((struct bcmsflash_mtd *)mtd->priv)->sfl; 161 int i, j, ret = 0; 162 unsigned int addr, len; 163 164 /* Check address range */ 165 if (!erase->len) 166 return 0; 167 if ((erase->addr + erase->len) > mtd->size) 168 return -EINVAL; 169 170 BCMSFLASH_LOCK(mtd->mlock); 171 addr = erase->addr; 172 len = erase->len; 173 174 /* Ensure that requested region is aligned */ 175 for (i = 0; i < mtd->numeraseregions; i++) { 176 for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { 177 if (addr == mtd->eraseregions[i].offset + 178 mtd->eraseregions[i].erasesize * j && 179 len >= mtd->eraseregions[i].erasesize) { 180 if ((ret = hndsflash_erase(sfl, addr)) < 0) 181 break; 182 if ((ret = bcmsflash_mtd_poll(sfl, addr, 10 * HZ))) 183 break; 184 addr += mtd->eraseregions[i].erasesize; 185 len -= mtd->eraseregions[i].erasesize; 186 } 187 } 188 if (ret) 189 break; 190 } 191 192 /* Set erase status */ 193 if (ret < 0) 194 erase->state = MTD_ERASE_FAILED; 195 else { 196 erase->state = MTD_ERASE_DONE; 197 ret = 0; 198 } 199 200 BCMSFLASH_UNLOCK(mtd->mlock); 201 202 /* Call erase callback */ 203 if (erase->callback) 204 erase->callback(erase); 205 206 return ret; 207} 208 209static int __init 210bcmsflash_mtd_init(void) 211{ 212 int ret = 0; 213 hndsflash_t *info; 214#ifdef CONFIG_MTD_PARTITIONS 215 struct mtd_partition *parts; 216 int i; 217#endif 218 219 memset(&bcmsflash, 0, sizeof(struct bcmsflash_mtd)); 220 221 /* attach to the backplane */ 222 if (!(bcmsflash.sih = si_kattach(SI_OSH))) { 223 printk(KERN_ERR "bcmsflash: error attaching to backplane\n"); 224 ret = -EIO; 225 goto fail; 226 } 227 228 /* Initialize serial flash access */ 229 if (!(info = hndsflash_init(bcmsflash.sih))) { 230 printk(KERN_ERR "bcmsflash: found no supported devices\n"); 231 ret = -ENODEV; 232 goto fail; 233 } 234 bcmsflash.sfl = info; 235 236 /* Setup region info */ 237 bcmsflash.region.offset = 0; 238 bcmsflash.region.erasesize = info->blocksize; 239 bcmsflash.region.numblocks = info->numblocks; 240 if (bcmsflash.region.erasesize > bcmsflash.mtd.erasesize) 241 bcmsflash.mtd.erasesize = bcmsflash.region.erasesize; 242 bcmsflash.mtd.size = info->size; 243 bcmsflash.mtd.numeraseregions = 1; 244 245 /* Register with MTD */ 246 bcmsflash.mtd.name = "bcmsflash"; 247 bcmsflash.mtd.type = MTD_NORFLASH; 248 bcmsflash.mtd.flags = MTD_CAP_NORFLASH; 249 bcmsflash.mtd.eraseregions = &bcmsflash.region; 250 bcmsflash.mtd.erase = bcmsflash_mtd_erase; 251 bcmsflash.mtd.read = bcmsflash_mtd_read; 252 bcmsflash.mtd.write = bcmsflash_mtd_write; 253 bcmsflash.mtd.writesize = 1; 254 bcmsflash.mtd.priv = &bcmsflash; 255 bcmsflash.mtd.owner = THIS_MODULE; 256 bcmsflash.mtd.mlock = partitions_lock_init(); 257 if (!bcmsflash.mtd.mlock) 258 return -ENOMEM; 259 260#ifdef CONFIG_MTD_PARTITIONS 261 parts = init_mtd_partitions(info, &bcmsflash.mtd, bcmsflash.mtd.size); 262 if (parts) { 263 for (i = 0; parts[i].name; i++); 264 ret = add_mtd_partitions(&bcmsflash.mtd, parts, i); 265 if (ret) { 266 printk(KERN_ERR "bcmsflash: add_mtd failed\n"); 267 goto fail; 268 } 269 } 270#endif 271 272 return 0; 273 274fail: 275 return ret; 276} 277 278static void __exit 279bcmsflash_mtd_exit(void) 280{ 281#ifdef CONFIG_MTD_PARTITIONS 282 del_mtd_partitions(&bcmsflash.mtd); 283#else 284 del_mtd_device(&bcmsflash.mtd); 285#endif 286} 287 288module_init(bcmsflash_mtd_init); 289module_exit(bcmsflash_mtd_exit); 290