Commit 45a3fd55 authored by Ben Hutchings's avatar Ben Hutchings

sfc: Move MTD operations into efx_nic_type

Merge the per-NIC-type MTD probe selection and struct efx_mtd_ops into
struct efx_nic_type.  Move the implementations into the appropriate
source files.

Several NVRAM functions are now only called from MTD operations which
are now implemented in the same file (falcon.c or mcdi.c).  There is no
need for them to be extern, or to be defined at all if CONFIG_SFC_MTD
is not enabled, so move them into the #ifdef CONFIG_SFC_MTD sections
in those files.

Most of the SPI-related definitions are also only used in falcon.c,
so move them there.  Put the remainder of spi.h into nic.h (which
previously included it).
Signed-off-by: default avatarBen Hutchings <bhutchings@solarflare.com>
parent 141d748e
......@@ -204,7 +204,12 @@ extern void efx_port_dummy_op_void(struct efx_nic *efx);
/* MTD */
#ifdef CONFIG_SFC_MTD
extern int efx_mtd_probe(struct efx_nic *efx);
extern int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
size_t n_parts, size_t sizeof_part);
static inline int efx_mtd_probe(struct efx_nic *efx)
{
return efx->type->mtd_probe(efx);
}
extern void efx_mtd_rename(struct efx_nic *efx);
extern void efx_mtd_remove(struct efx_nic *efx);
#else
......
......@@ -19,7 +19,6 @@
#include "net_driver.h"
#include "bitfield.h"
#include "efx.h"
#include "spi.h"
#include "nic.h"
#include "farch_regs.h"
#include "io.h"
......@@ -159,11 +158,49 @@
/**************************************************************************
*
* Non-volatile configuration
* Basic SPI command set and bit definitions
*
*************************************************************************/
#define SPI_WRSR 0x01 /* Write status register */
#define SPI_WRITE 0x02 /* Write data to memory array */
#define SPI_READ 0x03 /* Read data from memory array */
#define SPI_WRDI 0x04 /* Reset write enable latch */
#define SPI_RDSR 0x05 /* Read status register */
#define SPI_WREN 0x06 /* Set write enable latch */
#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */
#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */
#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */
#define SPI_STATUS_BP1 0x08 /* Block protection bit 1 */
#define SPI_STATUS_BP0 0x04 /* Block protection bit 0 */
#define SPI_STATUS_WEN 0x02 /* State of the write enable latch */
#define SPI_STATUS_NRDY 0x01 /* Device busy flag */
/**************************************************************************
*
* Non-volatile memory layout
*
**************************************************************************
*/
/* SFC4000 flash is partitioned into:
* 0-0x400 chip and board config (see struct falcon_nvconfig)
* 0x400-0x8000 unused (or may contain VPD if EEPROM not present)
* 0x8000-end boot code (mapped to PCI expansion ROM)
* SFC4000 small EEPROM (size < 0x400) is used for VPD only.
* SFC4000 large EEPROM (size >= 0x400) is partitioned into:
* 0-0x400 chip and board config
* configurable VPD
* 0x800-0x1800 boot config
* Aside from the chip and board config, all of these are optional and may
* be absent or truncated depending on the devices used.
*/
#define FALCON_NVCONFIG_END 0x400U
#define FALCON_FLASH_BOOTCODE_START 0x8000U
#define FALCON_EEPROM_BOOTCONFIG_START 0x800U
#define FALCON_EEPROM_BOOTCONFIG_END 0x1800U
/* Board configuration v2 (v1 is obsolete; later versions are compatible) */
struct falcon_nvconfig_board_v2 {
__le16 nports;
......@@ -434,7 +471,8 @@ static int falcon_spi_wait(struct efx_nic *efx)
}
}
int falcon_spi_cmd(struct efx_nic *efx, const struct falcon_spi_device *spi,
static int
falcon_spi_cmd(struct efx_nic *efx, const struct falcon_spi_device *spi,
unsigned int command, int address,
const void *in, void *out, size_t len)
{
......@@ -490,13 +528,6 @@ int falcon_spi_cmd(struct efx_nic *efx, const struct falcon_spi_device *spi,
return 0;
}
static size_t
falcon_spi_write_limit(const struct falcon_spi_device *spi, size_t start)
{
return min(FALCON_SPI_MAX_LEN,
(spi->block_size - (start & (spi->block_size - 1))));
}
static inline u8
falcon_spi_munge_command(const struct falcon_spi_device *spi,
const u8 command, const unsigned int address)
......@@ -504,33 +535,8 @@ falcon_spi_munge_command(const struct falcon_spi_device *spi,
return command | (((address >> 8) & spi->munge_address) << 3);
}
/* Wait up to 10 ms for buffered write completion */
int
falcon_spi_wait_write(struct efx_nic *efx, const struct falcon_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)) {
netif_err(efx, hw, efx->net_dev,
"SPI write timeout on device %d"
" last status=0x%02x\n",
spi->device_id, status);
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
}
}
int falcon_spi_read(struct efx_nic *efx, const struct falcon_spi_device *spi,
static int
falcon_spi_read(struct efx_nic *efx, const struct falcon_spi_device *spi,
loff_t start, size_t len, size_t *retlen, u8 *buffer)
{
size_t block_len, pos = 0;
......@@ -560,7 +566,51 @@ int falcon_spi_read(struct efx_nic *efx, const struct falcon_spi_device *spi,
return rc;
}
int
#ifdef CONFIG_SFC_MTD
struct falcon_mtd_partition {
struct efx_mtd_partition common;
const struct falcon_spi_device *spi;
size_t offset;
};
#define to_falcon_mtd_partition(mtd) \
container_of(mtd, struct falcon_mtd_partition, common.mtd)
static size_t
falcon_spi_write_limit(const struct falcon_spi_device *spi, size_t start)
{
return min(FALCON_SPI_MAX_LEN,
(spi->block_size - (start & (spi->block_size - 1))));
}
/* Wait up to 10 ms for buffered write completion */
static int
falcon_spi_wait_write(struct efx_nic *efx, const struct falcon_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)) {
netif_err(efx, hw, efx->net_dev,
"SPI write timeout on device %d"
" last status=0x%02x\n",
spi->device_id, status);
return -ETIMEDOUT;
}
schedule_timeout_uninterruptible(1);
}
}
static int
falcon_spi_write(struct efx_nic *efx, const struct falcon_spi_device *spi,
loff_t start, size_t len, size_t *retlen, const u8 *buffer)
{
......@@ -609,6 +659,238 @@ falcon_spi_write(struct efx_nic *efx, const struct falcon_spi_device *spi,
return rc;
}
static int
falcon_spi_slow_wait(struct falcon_mtd_partition *part, bool uninterruptible)
{
const struct falcon_spi_device *spi = part->spi;
struct efx_nic *efx = part->common.mtd.priv;
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->common.name, part->common.dev_type_name);
return -ETIMEDOUT;
}
static int
falcon_spi_unlock(struct efx_nic *efx, const struct falcon_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;
}
#define FALCON_SPI_VERIFY_BUF_LEN 16
static int
falcon_spi_erase(struct falcon_mtd_partition *part, loff_t start, size_t len)
{
const struct falcon_spi_device *spi = part->spi;
struct efx_nic *efx = part->common.mtd.priv;
unsigned pos, block_len;
u8 empty[FALCON_SPI_VERIFY_BUF_LEN];
u8 buffer[FALCON_SPI_VERIFY_BUF_LEN];
int rc;
if (len != spi->erase_size)
return -EINVAL;
if (spi->erase_command == 0)
return -EOPNOTSUPP;
rc = falcon_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 = falcon_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;
}
static void falcon_mtd_rename(struct efx_mtd_partition *part)
{
struct efx_nic *efx = part->mtd.priv;
snprintf(part->name, sizeof(part->name), "%s %s",
efx->name, part->type_name);
}
static int falcon_mtd_read(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, u8 *buffer)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
rc = mutex_lock_interruptible(&nic_data->spi_lock);
if (rc)
return rc;
rc = falcon_spi_read(efx, part->spi, part->offset + start,
len, retlen, buffer);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
rc = mutex_lock_interruptible(&nic_data->spi_lock);
if (rc)
return rc;
rc = falcon_spi_erase(part, part->offset + start, len);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static int falcon_mtd_write(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, const u8 *buffer)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
rc = mutex_lock_interruptible(&nic_data->spi_lock);
if (rc)
return rc;
rc = falcon_spi_write(efx, part->spi, part->offset + start,
len, retlen, buffer);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static int falcon_mtd_sync(struct mtd_info *mtd)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
mutex_lock(&nic_data->spi_lock);
rc = falcon_spi_slow_wait(part, true);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static int falcon_mtd_probe(struct efx_nic *efx)
{
struct falcon_nic_data *nic_data = efx->nic_data;
struct falcon_mtd_partition *parts;
struct falcon_spi_device *spi;
size_t n_parts;
int rc = -ENODEV;
ASSERT_RTNL();
/* Allocate space for maximum number of partitions */
parts = kcalloc(2, sizeof(*parts), GFP_KERNEL);
n_parts = 0;
spi = &nic_data->spi_flash;
if (falcon_spi_present(spi) && spi->size > FALCON_FLASH_BOOTCODE_START) {
parts[n_parts].spi = spi;
parts[n_parts].offset = FALCON_FLASH_BOOTCODE_START;
parts[n_parts].common.dev_type_name = "flash";
parts[n_parts].common.type_name = "sfc_flash_bootrom";
parts[n_parts].common.mtd.type = MTD_NORFLASH;
parts[n_parts].common.mtd.flags = MTD_CAP_NORFLASH;
parts[n_parts].common.mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
parts[n_parts].common.mtd.erasesize = spi->erase_size;
n_parts++;
}
spi = &nic_data->spi_eeprom;
if (falcon_spi_present(spi) && spi->size > FALCON_EEPROM_BOOTCONFIG_START) {
parts[n_parts].spi = spi;
parts[n_parts].offset = FALCON_EEPROM_BOOTCONFIG_START;
parts[n_parts].common.dev_type_name = "EEPROM";
parts[n_parts].common.type_name = "sfc_bootconfig";
parts[n_parts].common.mtd.type = MTD_RAM;
parts[n_parts].common.mtd.flags = MTD_CAP_RAM;
parts[n_parts].common.mtd.size =
min(spi->size, FALCON_EEPROM_BOOTCONFIG_END) -
FALCON_EEPROM_BOOTCONFIG_START;
parts[n_parts].common.mtd.erasesize = spi->erase_size;
n_parts++;
}
rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
if (rc)
kfree(parts);
return rc;
}
#endif /* CONFIG_SFC_MTD */
/**************************************************************************
*
* XMAC operations
......@@ -2417,6 +2699,15 @@ const struct efx_nic_type falcon_a1_nic_type = {
.filter_get_rx_id_limit = efx_farch_filter_get_rx_id_limit,
.filter_get_rx_ids = efx_farch_filter_get_rx_ids,
#ifdef CONFIG_SFC_MTD
.mtd_probe = falcon_mtd_probe,
.mtd_rename = falcon_mtd_rename,
.mtd_read = falcon_mtd_read,
.mtd_erase = falcon_mtd_erase,
.mtd_write = falcon_mtd_write,
.mtd_sync = falcon_mtd_sync,
#endif
.revision = EFX_REV_FALCON_A1,
.txd_ptr_tbl_base = FR_AA_TX_DESC_PTR_TBL_KER,
.rxd_ptr_tbl_base = FR_AA_RX_DESC_PTR_TBL_KER,
......@@ -2500,6 +2791,14 @@ const struct efx_nic_type falcon_b0_nic_type = {
.filter_rfs_insert = efx_farch_filter_rfs_insert,
.filter_rfs_expire_one = efx_farch_filter_rfs_expire_one,
#endif
#ifdef CONFIG_SFC_MTD
.mtd_probe = falcon_mtd_probe,
.mtd_rename = falcon_mtd_rename,
.mtd_read = falcon_mtd_read,
.mtd_erase = falcon_mtd_erase,
.mtd_write = falcon_mtd_write,
.mtd_sync = falcon_mtd_sync,
#endif
.revision = EFX_REV_FALCON_B0,
.txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
......
......@@ -811,125 +811,6 @@ int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
return rc;
}
int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_IN_LEN);
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type);
BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
loff_t offset, u8 *buffer, size_t length)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_LEN);
MCDI_DECLARE_BUF(outbuf,
MC_CMD_NVRAM_READ_OUT_LEN(EFX_MCDI_NVRAM_LEN_MAX));
size_t outlen;
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type);
MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset);
MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (rc)
goto fail;
memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length);
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
loff_t offset, const u8 *buffer, size_t length)
{
MCDI_DECLARE_BUF(inbuf,
MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length);
BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
loff_t offset, size_t length)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type);
MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset);
MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length);
BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN);
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
static int efx_mcdi_nvram_test(struct efx_nic *efx, unsigned int type)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_TEST_IN_LEN);
......@@ -1272,3 +1153,236 @@ int efx_mcdi_wol_filter_reset(struct efx_nic *efx)
return rc;
}
#ifdef CONFIG_SFC_MTD
#define EFX_MCDI_NVRAM_LEN_MAX 128
static int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_IN_LEN);
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type);
BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
static int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
loff_t offset, u8 *buffer, size_t length)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_LEN);
MCDI_DECLARE_BUF(outbuf,
MC_CMD_NVRAM_READ_OUT_LEN(EFX_MCDI_NVRAM_LEN_MAX));
size_t outlen;
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type);
MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset);
MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf),
outbuf, sizeof(outbuf), &outlen);
if (rc)
goto fail;
memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length);
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
static int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
loff_t offset, const u8 *buffer, size_t length)
{
MCDI_DECLARE_BUF(inbuf,
MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length);
BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
static int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
loff_t offset, size_t length)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type);
MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset);
MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length);
BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
static int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN);
int rc;
MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0);
rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
NULL, 0, NULL);
if (rc)
goto fail;
return 0;
fail:
netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
return rc;
}
int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, u8 *buffer)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
loff_t offset = start;
loff_t end = min_t(loff_t, start + len, mtd->size);
size_t chunk;
int rc = 0;
while (offset < end) {
chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
rc = efx_mcdi_nvram_read(efx, part->nvram_type, offset,
buffer, chunk);
if (rc)
goto out;
offset += chunk;
buffer += chunk;
}
out:
*retlen = offset - start;
return rc;
}
int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
loff_t offset = start & ~((loff_t)(mtd->erasesize - 1));
loff_t end = min_t(loff_t, start + len, mtd->size);
size_t chunk = part->common.mtd.erasesize;
int rc = 0;
if (!part->updating) {
rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
if (rc)
goto out;
part->updating = true;
}
/* The MCDI interface can in fact do multiple erase blocks at once;
* but erasing may be slow, so we make multiple calls here to avoid
* tripping the MCDI RPC timeout. */
while (offset < end) {
rc = efx_mcdi_nvram_erase(efx, part->nvram_type, offset,
chunk);
if (rc)
goto out;
offset += chunk;
}
out:
return rc;
}
int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, const u8 *buffer)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
loff_t offset = start;
loff_t end = min_t(loff_t, start + len, mtd->size);
size_t chunk;
int rc = 0;
if (!part->updating) {
rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
if (rc)
goto out;
part->updating = true;
}
while (offset < end) {
chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
rc = efx_mcdi_nvram_write(efx, part->nvram_type, offset,
buffer, chunk);
if (rc)
goto out;
offset += chunk;
buffer += chunk;
}
out:
*retlen = offset - start;
return rc;
}
int efx_mcdi_mtd_sync(struct mtd_info *mtd)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
int rc = 0;
if (part->updating) {
part->updating = false;
rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
}
return rc;
}
void efx_mcdi_mtd_rename(struct efx_mtd_partition *part)
{
struct efx_mcdi_mtd_partition *mcdi_part =
container_of(part, struct efx_mcdi_mtd_partition, common);
struct efx_nic *efx = part->mtd.priv;
snprintf(part->name, sizeof(part->name), "%s %s:%02x",
efx->name, part->type_name, mcdi_part->fw_subtype);
}
#endif /* CONFIG_SFC_MTD */
......@@ -65,6 +65,16 @@ struct efx_mcdi_mon {
unsigned int n_attrs;
};
struct efx_mcdi_mtd_partition {
struct efx_mtd_partition common;
bool updating;
u8 nvram_type;
u16 fw_subtype;
};
#define to_efx_mcdi_mtd_partition(mtd) \
container_of(mtd, struct efx_mcdi_mtd_partition, common.mtd)
/**
* struct efx_mcdi_data - extra state for NICs that implement MCDI
* @iface: Interface/protocol state
......@@ -250,18 +260,6 @@ extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out);
extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
size_t *size_out, size_t *erase_size_out,
bool *protected_out);
extern int efx_mcdi_nvram_update_start(struct efx_nic *efx,
unsigned int type);
extern int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
loff_t offset, u8 *buffer, size_t length);
extern int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
loff_t offset, const u8 *buffer,
size_t length);
#define EFX_MCDI_NVRAM_LEN_MAX 128
extern int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
loff_t offset, size_t length);
extern int efx_mcdi_nvram_update_finish(struct efx_nic *efx,
unsigned int type);
extern int efx_mcdi_nvram_test_all(struct efx_nic *efx);
extern int efx_mcdi_handle_assertion(struct efx_nic *efx);
extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode);
......@@ -291,4 +289,14 @@ static inline int efx_mcdi_mon_probe(struct efx_nic *efx) { return 0; }
static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {}
#endif
#ifdef CONFIG_SFC_MTD
extern int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, u8 *buffer);
extern int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len);
extern int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, const u8 *buffer);
extern int efx_mcdi_mtd_sync(struct mtd_info *mtd);
extern void efx_mcdi_mtd_rename(struct efx_mtd_partition *part);
#endif
#endif /* EFX_MCDI_H */
......@@ -8,167 +8,17 @@
* by the Free Software Foundation, incorporated herein by reference.
*/
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include "net_driver.h"
#include "spi.h"
#include "efx.h"
#include "nic.h"
#include "mcdi.h"
#include "mcdi_pcol.h"
#define FALCON_SPI_VERIFY_BUF_LEN 16
struct efx_mtd_partition {
struct list_head node;
struct mtd_info mtd;
const char *dev_type_name;
const char *type_name;
char name[IFNAMSIZ + 20];
};
struct falcon_mtd_partition {
struct efx_mtd_partition common;
const struct falcon_spi_device *spi;
size_t offset;
};
struct efx_mtd_ops {
void (*rename)(struct efx_mtd_partition *part);
int (*read)(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, u8 *buffer);
int (*erase)(struct mtd_info *mtd, loff_t start, size_t len);
int (*write)(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, const u8 *buffer);
int (*sync)(struct mtd_info *mtd);
};
#define to_efx_mtd_partition(mtd) \
container_of(mtd, struct efx_mtd_partition, mtd)
#define to_falcon_mtd_partition(mtd) \
container_of(mtd, struct falcon_mtd_partition, common.mtd)
static int falcon_mtd_probe(struct efx_nic *efx);
static int siena_mtd_probe(struct efx_nic *efx);
/* SPI utilities */
static int
falcon_spi_slow_wait(struct falcon_mtd_partition *part, bool uninterruptible)
{
const struct falcon_spi_device *spi = part->spi;
struct efx_nic *efx = part->common.mtd.priv;
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->common.name, part->common.dev_type_name);
return -ETIMEDOUT;
}
static int
falcon_spi_unlock(struct efx_nic *efx, const struct falcon_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
falcon_spi_erase(struct falcon_mtd_partition *part, loff_t start, size_t len)
{
const struct falcon_spi_device *spi = part->spi;
struct efx_nic *efx = part->common.mtd.priv;
unsigned pos, block_len;
u8 empty[FALCON_SPI_VERIFY_BUF_LEN];
u8 buffer[FALCON_SPI_VERIFY_BUF_LEN];
int rc;
if (len != spi->erase_size)
return -EINVAL;
if (spi->erase_command == 0)
return -EOPNOTSUPP;
rc = falcon_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 = falcon_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;
}
/* MTD interface */
static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
......@@ -176,7 +26,7 @@ static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
struct efx_nic *efx = mtd->priv;
int rc;
rc = efx->mtd_ops->erase(mtd, erase->addr, erase->len);
rc = efx->type->mtd_erase(mtd, erase->addr, erase->len);
if (rc == 0) {
erase->state = MTD_ERASE_DONE;
} else {
......@@ -193,7 +43,7 @@ static void efx_mtd_sync(struct mtd_info *mtd)
struct efx_nic *efx = mtd->priv;
int rc;
rc = efx->mtd_ops->sync(mtd);
rc = efx->type->mtd_sync(mtd);
if (rc)
pr_err("%s: %s sync failed (%d)\n",
part->name, part->dev_type_name, rc);
......@@ -213,14 +63,7 @@ static void efx_mtd_remove_partition(struct efx_mtd_partition *part)
list_del(&part->node);
}
static void efx_mtd_rename_partition(struct efx_mtd_partition *part)
{
struct efx_nic *efx = part->mtd.priv;
efx->mtd_ops->rename(part);
}
static int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
size_t n_parts, size_t sizeof_part)
{
struct efx_mtd_partition *part;
......@@ -236,11 +79,11 @@ static int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts,
part->mtd.priv = efx;
part->mtd.name = part->name;
part->mtd._erase = efx_mtd_erase;
part->mtd._read = efx->mtd_ops->read;
part->mtd._write = efx->mtd_ops->write;
part->mtd._read = efx->type->mtd_read;
part->mtd._write = efx->type->mtd_write;
part->mtd._sync = efx_mtd_sync;
efx_mtd_rename_partition(part);
efx->type->mtd_rename(part);
if (mtd_device_register(&part->mtd, NULL, 0))
goto fail;
......@@ -286,396 +129,5 @@ void efx_mtd_rename(struct efx_nic *efx)
ASSERT_RTNL();
list_for_each_entry(part, &efx->mtd_list, node)
efx_mtd_rename_partition(part);
}
int efx_mtd_probe(struct efx_nic *efx)
{
if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
return siena_mtd_probe(efx);
else
return falcon_mtd_probe(efx);
}
/* Implementation of MTD operations for Falcon */
static void falcon_mtd_rename(struct efx_mtd_partition *part)
{
struct efx_nic *efx = part->mtd.priv;
snprintf(part->name, sizeof(part->name), "%s %s",
efx->name, part->type_name);
}
static int falcon_mtd_read(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, u8 *buffer)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
rc = mutex_lock_interruptible(&nic_data->spi_lock);
if (rc)
return rc;
rc = falcon_spi_read(efx, part->spi, part->offset + start,
len, retlen, buffer);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static int falcon_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
rc = mutex_lock_interruptible(&nic_data->spi_lock);
if (rc)
return rc;
rc = falcon_spi_erase(part, part->offset + start, len);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static int falcon_mtd_write(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, const u8 *buffer)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
rc = mutex_lock_interruptible(&nic_data->spi_lock);
if (rc)
return rc;
rc = falcon_spi_write(efx, part->spi, part->offset + start,
len, retlen, buffer);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static int falcon_mtd_sync(struct mtd_info *mtd)
{
struct falcon_mtd_partition *part = to_falcon_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
struct falcon_nic_data *nic_data = efx->nic_data;
int rc;
mutex_lock(&nic_data->spi_lock);
rc = falcon_spi_slow_wait(part, true);
mutex_unlock(&nic_data->spi_lock);
return rc;
}
static const struct efx_mtd_ops falcon_mtd_ops = {
.rename = falcon_mtd_rename,
.read = falcon_mtd_read,
.erase = falcon_mtd_erase,
.write = falcon_mtd_write,
.sync = falcon_mtd_sync,
};
static int falcon_mtd_probe(struct efx_nic *efx)
{
struct falcon_nic_data *nic_data = efx->nic_data;
struct falcon_mtd_partition *parts;
struct falcon_spi_device *spi;
size_t n_parts;
int rc = -ENODEV;
ASSERT_RTNL();
efx->mtd_ops = &falcon_mtd_ops;
/* Allocate space for maximum number of partitions */
parts = kcalloc(2, sizeof(*parts), GFP_KERNEL);
n_parts = 0;
spi = &nic_data->spi_flash;
if (falcon_spi_present(spi) && spi->size > FALCON_FLASH_BOOTCODE_START) {
parts[n_parts].spi = spi;
parts[n_parts].offset = FALCON_FLASH_BOOTCODE_START;
parts[n_parts].common.dev_type_name = "flash";
parts[n_parts].common.type_name = "sfc_flash_bootrom";
parts[n_parts].common.mtd.type = MTD_NORFLASH;
parts[n_parts].common.mtd.flags = MTD_CAP_NORFLASH;
parts[n_parts].common.mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
parts[n_parts].common.mtd.erasesize = spi->erase_size;
n_parts++;
}
spi = &nic_data->spi_eeprom;
if (falcon_spi_present(spi) && spi->size > FALCON_EEPROM_BOOTCONFIG_START) {
parts[n_parts].spi = spi;
parts[n_parts].offset = FALCON_EEPROM_BOOTCONFIG_START;
parts[n_parts].common.dev_type_name = "EEPROM";
parts[n_parts].common.type_name = "sfc_bootconfig";
parts[n_parts].common.mtd.type = MTD_RAM;
parts[n_parts].common.mtd.flags = MTD_CAP_RAM;
parts[n_parts].common.mtd.size =
min(spi->size, FALCON_EEPROM_BOOTCONFIG_END) -
FALCON_EEPROM_BOOTCONFIG_START;
parts[n_parts].common.mtd.erasesize = spi->erase_size;
n_parts++;
}
rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
if (rc)
kfree(parts);
return rc;
}
/* Implementation of MTD operations for Siena */
struct efx_mcdi_mtd_partition {
struct efx_mtd_partition common;
bool updating;
u8 nvram_type;
u16 fw_subtype;
};
#define to_efx_mcdi_mtd_partition(mtd) \
container_of(mtd, struct efx_mcdi_mtd_partition, common.mtd)
static void siena_mtd_rename(struct efx_mtd_partition *part)
{
struct efx_mcdi_mtd_partition *mcdi_part =
container_of(part, struct efx_mcdi_mtd_partition, common);
struct efx_nic *efx = part->mtd.priv;
snprintf(part->name, sizeof(part->name), "%s %s:%02x",
efx->name, part->type_name, mcdi_part->fw_subtype);
}
static int siena_mtd_read(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, u8 *buffer)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
loff_t offset = start;
loff_t end = min_t(loff_t, start + len, mtd->size);
size_t chunk;
int rc = 0;
while (offset < end) {
chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
rc = efx_mcdi_nvram_read(efx, part->nvram_type, offset,
buffer, chunk);
if (rc)
goto out;
offset += chunk;
buffer += chunk;
}
out:
*retlen = offset - start;
return rc;
}
static int siena_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
loff_t offset = start & ~((loff_t)(mtd->erasesize - 1));
loff_t end = min_t(loff_t, start + len, mtd->size);
size_t chunk = part->common.mtd.erasesize;
int rc = 0;
if (!part->updating) {
rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
if (rc)
goto out;
part->updating = true;
}
/* The MCDI interface can in fact do multiple erase blocks at once;
* but erasing may be slow, so we make multiple calls here to avoid
* tripping the MCDI RPC timeout. */
while (offset < end) {
rc = efx_mcdi_nvram_erase(efx, part->nvram_type, offset,
chunk);
if (rc)
goto out;
offset += chunk;
}
out:
return rc;
}
static int siena_mtd_write(struct mtd_info *mtd, loff_t start,
size_t len, size_t *retlen, const u8 *buffer)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
loff_t offset = start;
loff_t end = min_t(loff_t, start + len, mtd->size);
size_t chunk;
int rc = 0;
if (!part->updating) {
rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
if (rc)
goto out;
part->updating = true;
}
while (offset < end) {
chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
rc = efx_mcdi_nvram_write(efx, part->nvram_type, offset,
buffer, chunk);
if (rc)
goto out;
offset += chunk;
buffer += chunk;
}
out:
*retlen = offset - start;
return rc;
}
static int siena_mtd_sync(struct mtd_info *mtd)
{
struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
struct efx_nic *efx = mtd->priv;
int rc = 0;
if (part->updating) {
part->updating = false;
rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
}
return rc;
efx->type->mtd_rename(part);
}
static const struct efx_mtd_ops siena_mtd_ops = {
.rename = siena_mtd_rename,
.read = siena_mtd_read,
.erase = siena_mtd_erase,
.write = siena_mtd_write,
.sync = siena_mtd_sync,
};
struct siena_nvram_type_info {
int port;
const char *name;
};
static const struct siena_nvram_type_info siena_nvram_types[] = {
[MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO] = { 0, "sfc_dummy_phy" },
[MC_CMD_NVRAM_TYPE_MC_FW] = { 0, "sfc_mcfw" },
[MC_CMD_NVRAM_TYPE_MC_FW_BACKUP] = { 0, "sfc_mcfw_backup" },
[MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0] = { 0, "sfc_static_cfg" },
[MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1] = { 1, "sfc_static_cfg" },
[MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0] = { 0, "sfc_dynamic_cfg" },
[MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1] = { 1, "sfc_dynamic_cfg" },
[MC_CMD_NVRAM_TYPE_EXP_ROM] = { 0, "sfc_exp_rom" },
[MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0] = { 0, "sfc_exp_rom_cfg" },
[MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1] = { 1, "sfc_exp_rom_cfg" },
[MC_CMD_NVRAM_TYPE_PHY_PORT0] = { 0, "sfc_phy_fw" },
[MC_CMD_NVRAM_TYPE_PHY_PORT1] = { 1, "sfc_phy_fw" },
[MC_CMD_NVRAM_TYPE_FPGA] = { 0, "sfc_fpga" },
};
static int siena_mtd_probe_partition(struct efx_nic *efx,
struct efx_mcdi_mtd_partition *part,
unsigned int type)
{
const struct siena_nvram_type_info *info;
size_t size, erase_size;
bool protected;
int rc;
if (type >= ARRAY_SIZE(siena_nvram_types) ||
siena_nvram_types[type].name == NULL)
return -ENODEV;
info = &siena_nvram_types[type];
if (info->port != efx_port_num(efx))
return -ENODEV;
rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected);
if (rc)
return rc;
if (protected)
return -ENODEV; /* hide it */
part->nvram_type = type;
part->common.dev_type_name = "Siena NVRAM manager";
part->common.type_name = info->name;
part->common.mtd.type = MTD_NORFLASH;
part->common.mtd.flags = MTD_CAP_NORFLASH;
part->common.mtd.size = size;
part->common.mtd.erasesize = erase_size;
return 0;
}
static int siena_mtd_get_fw_subtypes(struct efx_nic *efx,
struct efx_mcdi_mtd_partition *parts,
size_t n_parts)
{
uint16_t fw_subtype_list[
MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_MAXNUM];
size_t i;
int rc;
rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list, NULL);
if (rc)
return rc;
for (i = 0; i < n_parts; i++)
parts[i].fw_subtype = fw_subtype_list[parts[i].nvram_type];
return 0;
}
static int siena_mtd_probe(struct efx_nic *efx)
{
struct efx_mcdi_mtd_partition *parts;
u32 nvram_types;
unsigned int type;
size_t n_parts;
int rc;
ASSERT_RTNL();
efx->mtd_ops = &siena_mtd_ops;
rc = efx_mcdi_nvram_types(efx, &nvram_types);
if (rc)
return rc;
parts = kcalloc(hweight32(nvram_types), sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
type = 0;
n_parts = 0;
while (nvram_types != 0) {
if (nvram_types & 1) {
rc = siena_mtd_probe_partition(efx, &parts[n_parts],
type);
if (rc == 0)
n_parts++;
else if (rc != -ENODEV)
goto fail;
}
type++;
nvram_types >>= 1;
}
rc = siena_mtd_get_fw_subtypes(efx, parts, n_parts);
if (rc)
goto fail;
rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
fail:
if (rc)
kfree(parts);
return rc;
}
......@@ -27,6 +27,7 @@
#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include <linux/i2c.h>
#include <linux/mtd/mtd.h>
#include "enum.h"
#include "bitfield.h"
......@@ -868,7 +869,6 @@ struct efx_nic {
struct delayed_work selftest_work;
#ifdef CONFIG_SFC_MTD
const struct efx_mtd_ops *mtd_ops;
struct list_head mtd_list;
#endif
......@@ -954,6 +954,14 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
return efx->port_num;
}
struct efx_mtd_partition {
struct list_head node;
struct mtd_info mtd;
const char *dev_type_name;
const char *type_name;
char name[IFNAMSIZ + 20];
};
/**
* struct efx_nic_type - Efx device type definition
* @mem_map_size: Get memory BAR mapped size
......@@ -1047,6 +1055,15 @@ static inline unsigned int efx_port_num(struct efx_nic *efx)
* @filter_rfs_expire_one: Consider expiring a filter inserted for RFS.
* This must check whether the specified table entry is used by RFS
* and that rps_may_expire_flow() returns true for it.
* @mtd_probe: Probe and add MTD partitions associated with this net device,
* using efx_mtd_add()
* @mtd_rename: Set an MTD partition name using the net device name
* @mtd_read: Read from an MTD partition
* @mtd_erase: Erase part of an MTD partition
* @mtd_write: Write to an MTD partition
* @mtd_sync: Wait for write-back to complete on MTD partition. This
* also notifies the driver that a writer has finished using this
* partition.
* @revision: Hardware architecture revision
* @txd_ptr_tbl_base: TX descriptor ring base address
* @rxd_ptr_tbl_base: RX descriptor ring base address
......@@ -1150,6 +1167,16 @@ struct efx_nic_type {
bool (*filter_rfs_expire_one)(struct efx_nic *efx, u32 flow_id,
unsigned int index);
#endif
#ifdef CONFIG_SFC_MTD
int (*mtd_probe)(struct efx_nic *efx);
void (*mtd_rename)(struct efx_mtd_partition *part);
int (*mtd_read)(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, u8 *buffer);
int (*mtd_erase)(struct mtd_info *mtd, loff_t start, size_t len);
int (*mtd_write)(struct mtd_info *mtd, loff_t start, size_t len,
size_t *retlen, const u8 *buffer);
int (*mtd_sync)(struct mtd_info *mtd);
#endif
int revision;
unsigned int txd_ptr_tbl_base;
......
......@@ -16,7 +16,6 @@
#include "net_driver.h"
#include "efx.h"
#include "mcdi.h"
#include "spi.h"
/*
* Falcon hardware control
......@@ -163,6 +162,38 @@ struct falcon_board {
struct i2c_client *hwmon_client, *ioexp_client;
};
/**
* struct falcon_spi_device - a Falcon SPI (Serial Peripheral Interface) device
* @device_id: Controller's id for the device
* @size: Size (in bytes)
* @addr_len: Number of address bytes in read/write commands
* @munge_address: Flag whether addresses should be munged.
* Some devices with 9-bit addresses (e.g. AT25040A EEPROM)
* use bit 3 of the command byte as address bit A8, rather
* than having a two-byte address. If this flag is set, then
* commands should be munged in this way.
* @erase_command: Erase command (or 0 if sector erase not needed).
* @erase_size: Erase sector size (in bytes)
* Erase commands affect sectors with this size and alignment.
* This must be a power of two.
* @block_size: Write block size (in bytes).
* Write commands are limited to blocks with this size and alignment.
*/
struct falcon_spi_device {
int device_id;
unsigned int size;
unsigned int addr_len;
unsigned int munge_address:1;
u8 erase_command;
unsigned int erase_size;
unsigned int block_size;
};
static inline bool falcon_spi_present(const struct falcon_spi_device *spi)
{
return spi->size != 0;
}
/**
* struct falcon_nic_data - Falcon NIC state
* @pci_dev2: Secondary function of Falcon A
......
......@@ -18,7 +18,6 @@
#include "bitfield.h"
#include "efx.h"
#include "nic.h"
#include "spi.h"
#include "farch_regs.h"
#include "io.h"
#include "phy.h"
......@@ -672,6 +671,138 @@ static int siena_mcdi_poll_reboot(struct efx_nic *efx)
return -EIO;
}
/**************************************************************************
*
* MTD
*
**************************************************************************
*/
#ifdef CONFIG_SFC_MTD
struct siena_nvram_type_info {
int port;
const char *name;
};
static const struct siena_nvram_type_info siena_nvram_types[] = {
[MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO] = { 0, "sfc_dummy_phy" },
[MC_CMD_NVRAM_TYPE_MC_FW] = { 0, "sfc_mcfw" },
[MC_CMD_NVRAM_TYPE_MC_FW_BACKUP] = { 0, "sfc_mcfw_backup" },
[MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0] = { 0, "sfc_static_cfg" },
[MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1] = { 1, "sfc_static_cfg" },
[MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0] = { 0, "sfc_dynamic_cfg" },
[MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1] = { 1, "sfc_dynamic_cfg" },
[MC_CMD_NVRAM_TYPE_EXP_ROM] = { 0, "sfc_exp_rom" },
[MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0] = { 0, "sfc_exp_rom_cfg" },
[MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1] = { 1, "sfc_exp_rom_cfg" },
[MC_CMD_NVRAM_TYPE_PHY_PORT0] = { 0, "sfc_phy_fw" },
[MC_CMD_NVRAM_TYPE_PHY_PORT1] = { 1, "sfc_phy_fw" },
[MC_CMD_NVRAM_TYPE_FPGA] = { 0, "sfc_fpga" },
};
static int siena_mtd_probe_partition(struct efx_nic *efx,
struct efx_mcdi_mtd_partition *part,
unsigned int type)
{
const struct siena_nvram_type_info *info;
size_t size, erase_size;
bool protected;
int rc;
if (type >= ARRAY_SIZE(siena_nvram_types) ||
siena_nvram_types[type].name == NULL)
return -ENODEV;
info = &siena_nvram_types[type];
if (info->port != efx_port_num(efx))
return -ENODEV;
rc = efx_mcdi_nvram_info(efx, type, &size, &erase_size, &protected);
if (rc)
return rc;
if (protected)
return -ENODEV; /* hide it */
part->nvram_type = type;
part->common.dev_type_name = "Siena NVRAM manager";
part->common.type_name = info->name;
part->common.mtd.type = MTD_NORFLASH;
part->common.mtd.flags = MTD_CAP_NORFLASH;
part->common.mtd.size = size;
part->common.mtd.erasesize = erase_size;
return 0;
}
static int siena_mtd_get_fw_subtypes(struct efx_nic *efx,
struct efx_mcdi_mtd_partition *parts,
size_t n_parts)
{
uint16_t fw_subtype_list[
MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_MAXNUM];
size_t i;
int rc;
rc = efx_mcdi_get_board_cfg(efx, NULL, fw_subtype_list, NULL);
if (rc)
return rc;
for (i = 0; i < n_parts; i++)
parts[i].fw_subtype = fw_subtype_list[parts[i].nvram_type];
return 0;
}
static int siena_mtd_probe(struct efx_nic *efx)
{
struct efx_mcdi_mtd_partition *parts;
u32 nvram_types;
unsigned int type;
size_t n_parts;
int rc;
ASSERT_RTNL();
rc = efx_mcdi_nvram_types(efx, &nvram_types);
if (rc)
return rc;
parts = kcalloc(hweight32(nvram_types), sizeof(*parts), GFP_KERNEL);
if (!parts)
return -ENOMEM;
type = 0;
n_parts = 0;
while (nvram_types != 0) {
if (nvram_types & 1) {
rc = siena_mtd_probe_partition(efx, &parts[n_parts],
type);
if (rc == 0)
n_parts++;
else if (rc != -ENODEV)
goto fail;
}
type++;
nvram_types >>= 1;
}
rc = siena_mtd_get_fw_subtypes(efx, parts, n_parts);
if (rc)
goto fail;
rc = efx_mtd_add(efx, &parts[0].common, n_parts, sizeof(*parts));
fail:
if (rc)
kfree(parts);
return rc;
}
#endif /* CONFIG_SFC_MTD */
/**************************************************************************
*
* Revision-dependent attributes used by efx.c and nic.c
......@@ -753,6 +884,14 @@ const struct efx_nic_type siena_a0_nic_type = {
.filter_rfs_insert = efx_farch_filter_rfs_insert,
.filter_rfs_expire_one = efx_farch_filter_rfs_expire_one,
#endif
#ifdef CONFIG_SFC_MTD
.mtd_probe = siena_mtd_probe,
.mtd_rename = efx_mcdi_mtd_rename,
.mtd_read = efx_mcdi_mtd_read,
.mtd_erase = efx_mcdi_mtd_erase,
.mtd_write = efx_mcdi_mtd_write,
.mtd_sync = efx_mcdi_mtd_sync,
#endif
.revision = EFX_REV_SIENA_A0,
.txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL,
......
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005 Fen Systems Ltd.
* Copyright 2006-2010 Solarflare Communications Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, incorporated herein by reference.
*/
#ifndef EFX_SPI_H
#define EFX_SPI_H
#include "net_driver.h"
/**************************************************************************
*
* Basic SPI command set and bit definitions
*
*************************************************************************/
#define SPI_WRSR 0x01 /* Write status register */
#define SPI_WRITE 0x02 /* Write data to memory array */
#define SPI_READ 0x03 /* Read data from memory array */
#define SPI_WRDI 0x04 /* Reset write enable latch */
#define SPI_RDSR 0x05 /* Read status register */
#define SPI_WREN 0x06 /* Set write enable latch */
#define SPI_SST_EWSR 0x50 /* SST: Enable write to status register */
#define SPI_STATUS_WPEN 0x80 /* Write-protect pin enabled */
#define SPI_STATUS_BP2 0x10 /* Block protection bit 2 */
#define SPI_STATUS_BP1 0x08 /* Block protection bit 1 */
#define SPI_STATUS_BP0 0x04 /* Block protection bit 0 */
#define SPI_STATUS_WEN 0x02 /* State of the write enable latch */
#define SPI_STATUS_NRDY 0x01 /* Device busy flag */
/**
* struct falcon_spi_device - a Falcon SPI (Serial Peripheral Interface) device
* @device_id: Controller's id for the device
* @size: Size (in bytes)
* @addr_len: Number of address bytes in read/write commands
* @munge_address: Flag whether addresses should be munged.
* Some devices with 9-bit addresses (e.g. AT25040A EEPROM)
* use bit 3 of the command byte as address bit A8, rather
* than having a two-byte address. If this flag is set, then
* commands should be munged in this way.
* @erase_command: Erase command (or 0 if sector erase not needed).
* @erase_size: Erase sector size (in bytes)
* Erase commands affect sectors with this size and alignment.
* This must be a power of two.
* @block_size: Write block size (in bytes).
* Write commands are limited to blocks with this size and alignment.
*/
struct falcon_spi_device {
int device_id;
unsigned int size;
unsigned int addr_len;
unsigned int munge_address:1;
u8 erase_command;
unsigned int erase_size;
unsigned int block_size;
};
static inline bool falcon_spi_present(const struct falcon_spi_device *spi)
{
return spi->size != 0;
}
int falcon_spi_cmd(struct efx_nic *efx,
const struct falcon_spi_device *spi, unsigned int command,
int address, const void *in, void *out, size_t len);
int falcon_spi_wait_write(struct efx_nic *efx,
const struct falcon_spi_device *spi);
int falcon_spi_read(struct efx_nic *efx,
const struct falcon_spi_device *spi, loff_t start,
size_t len, size_t *retlen, u8 *buffer);
int falcon_spi_write(struct efx_nic *efx,
const struct falcon_spi_device *spi, loff_t start,
size_t len, size_t *retlen, const u8 *buffer);
/*
* SFC4000 flash is partitioned into:
* 0-0x400 chip and board config (see falcon_hwdefs.h)
* 0x400-0x8000 unused (or may contain VPD if EEPROM not present)
* 0x8000-end boot code (mapped to PCI expansion ROM)
* SFC4000 small EEPROM (size < 0x400) is used for VPD only.
* SFC4000 large EEPROM (size >= 0x400) is partitioned into:
* 0-0x400 chip and board config
* configurable VPD
* 0x800-0x1800 boot config
* Aside from the chip and board config, all of these are optional and may
* be absent or truncated depending on the devices used.
*/
#define FALCON_NVCONFIG_END 0x400U
#define FALCON_FLASH_BOOTCODE_START 0x8000U
#define FALCON_EEPROM_BOOTCONFIG_START 0x800U
#define FALCON_EEPROM_BOOTCONFIG_END 0x1800U
#endif /* EFX_SPI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment