static int efx_spi_unlock(struct efx_nic *efx, const struct efx_spi_device *spi) { const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 | SPI_STATUS_BP0); u8 status; int rc; rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; if (!(status & unlock_mask)) return 0; /* already unlocked */ rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; rc = falcon_spi_cmd(efx, spi, SPI_SST_EWSR, -1, NULL, NULL, 0); if (rc) return rc; status &= ~unlock_mask; rc = falcon_spi_cmd(efx, spi, SPI_WRSR, -1, &status, NULL, sizeof(status)); if (rc) return rc; rc = falcon_spi_wait_write(efx, spi); if (rc) return rc; return 0; }
static int efx_spi_erase(struct efx_mtd_partition *part, loff_t start, size_t len) { struct efx_mtd *efx_mtd = part->mtd.priv; const struct efx_spi_device *spi = efx_mtd->spi; struct efx_nic *efx = efx_mtd->efx; unsigned pos, block_len; u8 empty[EFX_SPI_VERIFY_BUF_LEN]; u8 buffer[EFX_SPI_VERIFY_BUF_LEN]; int rc; if (len != spi->erase_size) return -EINVAL; if (spi->erase_command == 0) return -EOPNOTSUPP; rc = efx_spi_unlock(efx, spi); if (rc) return rc; rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) return rc; rc = falcon_spi_cmd(efx, spi, spi->erase_command, start, NULL, NULL, 0); if (rc) return rc; rc = efx_spi_slow_wait(part, false); /* Verify the entire region has been wiped */ memset(empty, 0xff, sizeof(empty)); for (pos = 0; pos < len; pos += block_len) { block_len = min(len - pos, sizeof(buffer)); rc = falcon_spi_read(efx, spi, start + pos, block_len, NULL, buffer); if (rc) return rc; if (memcmp(empty, buffer, block_len)) return -EIO; /* Avoid locking up the system */ cond_resched(); if (signal_pending(current)) return -EINTR; } return rc; }
int falcon_spi_write(struct efx_nic *efx, const struct efx_spi_device *spi, loff_t start, size_t len, size_t *retlen, const u8 *buffer) { u8 verify_buffer[FALCON_SPI_MAX_LEN]; size_t block_len, pos = 0; unsigned int command; int rc = 0; while (pos < len) { rc = falcon_spi_cmd(efx, spi, SPI_WREN, -1, NULL, NULL, 0); if (rc) break; block_len = min(len - pos, falcon_spi_write_limit(spi, start + pos)); command = efx_spi_munge_command(spi, SPI_WRITE, start + pos); rc = falcon_spi_cmd(efx, spi, command, start + pos, buffer + pos, NULL, block_len); if (rc) break; rc = falcon_spi_wait_write(efx, spi); if (rc) break; command = efx_spi_munge_command(spi, SPI_READ, start + pos); rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, verify_buffer, block_len); if (memcmp(verify_buffer, buffer + pos, block_len)) { rc = -EIO; break; } pos += block_len; cond_resched(); if (signal_pending(current)) { rc = -EINTR; break; } } if (retlen) *retlen = pos; return rc; }
static int efx_spi_slow_wait(struct efx_mtd_partition *part, bool uninterruptible) { struct efx_mtd *efx_mtd = part->mtd.priv; const struct efx_spi_device *spi = efx_mtd->spi; struct efx_nic *efx = efx_mtd->efx; u8 status; int rc, i; /* Wait up to 4s for flash/EEPROM to finish a slow operation. */ for (i = 0; i < 40; i++) { __set_current_state(uninterruptible ? TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE); schedule_timeout(HZ / 10); rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; if (!(status & SPI_STATUS_NRDY)) return 0; if (signal_pending(current)) return -EINTR; } pr_err("%s: timed out waiting for %s\n", part->name, efx_mtd->name); return -ETIMEDOUT; }
int falcon_spi_read(struct efx_nic *efx, const struct efx_spi_device *spi, loff_t start, size_t len, size_t *retlen, u8 *buffer) { size_t block_len, pos = 0; unsigned int command; int rc = 0; while (pos < len) { block_len = min(len - pos, FALCON_SPI_MAX_LEN); command = efx_spi_munge_command(spi, SPI_READ, start + pos); rc = falcon_spi_cmd(efx, spi, command, start + pos, NULL, buffer + pos, block_len); if (rc) break; pos += block_len; cond_resched(); if (signal_pending(current)) { rc = -EINTR; break; } } if (retlen) *retlen = pos; return rc; }
/* Wait up to 10 ms for buffered write completion */ int falcon_spi_wait_write(struct efx_nic *efx, const struct efx_spi_device *spi) { unsigned long timeout = jiffies + 1 + DIV_ROUND_UP(HZ, 100); u8 status; int rc; for (;;) { rc = falcon_spi_cmd(efx, spi, SPI_RDSR, -1, NULL, &status, sizeof(status)); if (rc) return rc; if (!(status & SPI_STATUS_NRDY)) return 0; if (time_after_eq(jiffies, timeout)) { EFX_ERR(efx, "SPI write timeout on device %d" " last status=0x%02x\n", spi->device_id, status); return -ETIMEDOUT; } schedule_timeout_uninterruptible(1); } }