at v2.6.29 268 lines 6.5 kB view raw
1/**************************************************************************** 2 * Driver for Solarflare Solarstorm network controllers and boards 3 * Copyright 2005-2006 Fen Systems Ltd. 4 * Copyright 2006-2008 Solarflare Communications Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published 8 * by the Free Software Foundation, incorporated herein by reference. 9 */ 10 11#include <linux/module.h> 12#include <linux/mtd/mtd.h> 13#include <linux/delay.h> 14 15#define EFX_DRIVER_NAME "sfc_mtd" 16#include "net_driver.h" 17#include "spi.h" 18 19#define EFX_SPI_VERIFY_BUF_LEN 16 20 21struct efx_mtd { 22 const struct efx_spi_device *spi; 23 struct mtd_info mtd; 24 char name[IFNAMSIZ + 20]; 25}; 26 27/* SPI utilities */ 28 29static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible) 30{ 31 const struct efx_spi_device *spi = efx_mtd->spi; 32 struct efx_nic *efx = spi->efx; 33 u8 status; 34 int rc, i; 35 36 /* Wait up to 4s for flash/EEPROM to finish a slow operation. */ 37 for (i = 0; i < 40; i++) { 38 __set_current_state(uninterruptible ? 39 TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); 40 schedule_timeout(HZ / 10); 41 rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, 42 &status, sizeof(status)); 43 if (rc) 44 return rc; 45 if (!(status & SPI_STATUS_NRDY)) 46 return 0; 47 if (signal_pending(current)) 48 return -EINTR; 49 } 50 EFX_ERR(efx, "timed out waiting for %s\n", efx_mtd->name); 51 return -ETIMEDOUT; 52} 53 54static int efx_spi_unlock(const struct efx_spi_device *spi) 55{ 56 const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | 57 SPI_STATUS_BP0); 58 u8 status; 59 int rc; 60 61 rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); 62 if (rc) 63 return rc; 64 65 if (!(status & unlock_mask)) 66 return 0; /* already unlocked */ 67 68 rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); 69 if (rc) 70 return rc; 71 rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0); 72 if (rc) 73 return rc; 74 75 status &= ~unlock_mask; 76 rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status)); 77 if (rc) 78 return rc; 79 rc = falcon_spi_wait_write(spi); 80 if (rc) 81 return rc; 82 83 return 0; 84} 85 86static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len) 87{ 88 const struct efx_spi_device *spi = efx_mtd->spi; 89 unsigned pos, block_len; 90 u8 empty[EFX_SPI_VERIFY_BUF_LEN]; 91 u8 buffer[EFX_SPI_VERIFY_BUF_LEN]; 92 int rc; 93 94 if (len != spi->erase_size) 95 return -EINVAL; 96 97 if (spi->erase_command == 0) 98 return -EOPNOTSUPP; 99 100 rc = efx_spi_unlock(spi); 101 if (rc) 102 return rc; 103 rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0); 104 if (rc) 105 return rc; 106 rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0); 107 if (rc) 108 return rc; 109 rc = efx_spi_slow_wait(efx_mtd, false); 110 111 /* Verify the entire region has been wiped */ 112 memset(empty, 0xff, sizeof(empty)); 113 for (pos = 0; pos < len; pos += block_len) { 114 block_len = min(len - pos, sizeof(buffer)); 115 rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer); 116 if (rc) 117 return rc; 118 if (memcmp(empty, buffer, block_len)) 119 return -EIO; 120 121 /* Avoid locking up the system */ 122 cond_resched(); 123 if (signal_pending(current)) 124 return -EINTR; 125 } 126 127 return rc; 128} 129 130/* MTD interface */ 131 132static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len, 133 size_t *retlen, u8 *buffer) 134{ 135 struct efx_mtd *efx_mtd = mtd->priv; 136 const struct efx_spi_device *spi = efx_mtd->spi; 137 struct efx_nic *efx = spi->efx; 138 int rc; 139 140 rc = mutex_lock_interruptible(&efx->spi_lock); 141 if (rc) 142 return rc; 143 rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start, 144 len, retlen, buffer); 145 mutex_unlock(&efx->spi_lock); 146 return rc; 147} 148 149static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) 150{ 151 struct efx_mtd *efx_mtd = mtd->priv; 152 struct efx_nic *efx = efx_mtd->spi->efx; 153 int rc; 154 155 rc = mutex_lock_interruptible(&efx->spi_lock); 156 if (rc) 157 return rc; 158 rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr, 159 erase->len); 160 mutex_unlock(&efx->spi_lock); 161 162 if (rc == 0) { 163 erase->state = MTD_ERASE_DONE; 164 } else { 165 erase->state = MTD_ERASE_FAILED; 166 erase->fail_addr = 0xffffffff; 167 } 168 mtd_erase_callback(erase); 169 return rc; 170} 171 172static int efx_mtd_write(struct mtd_info *mtd, loff_t start, 173 size_t len, size_t *retlen, const u8 *buffer) 174{ 175 struct efx_mtd *efx_mtd = mtd->priv; 176 const struct efx_spi_device *spi = efx_mtd->spi; 177 struct efx_nic *efx = spi->efx; 178 int rc; 179 180 rc = mutex_lock_interruptible(&efx->spi_lock); 181 if (rc) 182 return rc; 183 rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start, 184 len, retlen, buffer); 185 mutex_unlock(&efx->spi_lock); 186 return rc; 187} 188 189static void efx_mtd_sync(struct mtd_info *mtd) 190{ 191 struct efx_mtd *efx_mtd = mtd->priv; 192 struct efx_nic *efx = efx_mtd->spi->efx; 193 int rc; 194 195 mutex_lock(&efx->spi_lock); 196 rc = efx_spi_slow_wait(efx_mtd, true); 197 mutex_unlock(&efx->spi_lock); 198 199 if (rc) 200 EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc); 201 return; 202} 203 204void efx_mtd_remove(struct efx_nic *efx) 205{ 206 if (efx->spi_flash && efx->spi_flash->mtd) { 207 struct efx_mtd *efx_mtd = efx->spi_flash->mtd; 208 int rc; 209 210 for (;;) { 211 rc = del_mtd_device(&efx_mtd->mtd); 212 if (rc != -EBUSY) 213 break; 214 ssleep(1); 215 } 216 WARN_ON(rc); 217 kfree(efx_mtd); 218 } 219} 220 221void efx_mtd_rename(struct efx_nic *efx) 222{ 223 if (efx->spi_flash && efx->spi_flash->mtd) { 224 struct efx_mtd *efx_mtd = efx->spi_flash->mtd; 225 snprintf(efx_mtd->name, sizeof(efx_mtd->name), 226 "%s sfc_flash_bootrom", efx->name); 227 } 228} 229 230int efx_mtd_probe(struct efx_nic *efx) 231{ 232 struct efx_spi_device *spi = efx->spi_flash; 233 struct efx_mtd *efx_mtd; 234 235 if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START) 236 return -ENODEV; 237 238 efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL); 239 if (!efx_mtd) 240 return -ENOMEM; 241 242 efx_mtd->spi = spi; 243 spi->mtd = efx_mtd; 244 245 efx_mtd->mtd.type = MTD_NORFLASH; 246 efx_mtd->mtd.flags = MTD_CAP_NORFLASH; 247 efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START; 248 efx_mtd->mtd.erasesize = spi->erase_size; 249 efx_mtd->mtd.writesize = 1; 250 efx_mtd_rename(efx); 251 252 efx_mtd->mtd.owner = THIS_MODULE; 253 efx_mtd->mtd.priv = efx_mtd; 254 efx_mtd->mtd.name = efx_mtd->name; 255 efx_mtd->mtd.erase = efx_mtd_erase; 256 efx_mtd->mtd.read = efx_mtd_read; 257 efx_mtd->mtd.write = efx_mtd_write; 258 efx_mtd->mtd.sync = efx_mtd_sync; 259 260 if (add_mtd_device(&efx_mtd->mtd)) { 261 kfree(efx_mtd); 262 spi->mtd = NULL; 263 /* add_mtd_device() returns 1 if the MTD table is full */ 264 return -ENOMEM; 265 } 266 267 return 0; 268}