From 4cd56820ea8767d0a6e7444e1b453a69322f8f2e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 29 Nov 2006 02:41:08 +0000 Subject: [PATCH] Basic non-volatile storage support --- src/core/nvs.c | 106 ++++++++++++++++++++++++++++++ src/drivers/net/etherfabric.c | 118 ++++++++++++++++++++++++++++++---- src/include/gpxe/nvs.h | 30 +++++++++ 3 files changed, 243 insertions(+), 11 deletions(-) create mode 100644 src/core/nvs.c create mode 100644 src/include/gpxe/nvs.h diff --git a/src/core/nvs.c b/src/core/nvs.c new file mode 100644 index 00000000..9b4d1310 --- /dev/null +++ b/src/core/nvs.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +/** @file + * + * Non-volatile storage + * + */ + +static size_t nvs_options_len ( struct nvs_device *nvs ) { + struct dhcp_option *option; + uint8_t sum; + unsigned int i; + size_t len; + + for ( sum = 0, i = 0 ; i < nvs->len ; i++ ) { + sum += * ( ( uint8_t * ) ( nvs->options->data + i ) ); + } + if ( sum != 0 ) { + DBG ( "NVS %p has bad checksum %02x; assuming empty\n", + nvs, sum ); + return 0; + } + + option = nvs->options->data; + if ( option->tag == DHCP_PAD ) { + DBG ( "NVS %p has bad start; assuming empty\n", nvs ); + return 0; + } + + option = find_dhcp_option ( nvs->options, DHCP_END ); + if ( ! option ) { + DBG ( "NVS %p has no end tag; assuming empty\n", nvs ); + return 0; + } + + len = ( ( void * ) option - nvs->options->data + 1 ); + DBG ( "NVS %p contains %zd bytes of options (maximum %zd)\n", + nvs, len, nvs->len ); + + return len; +} + +int nvs_register ( struct nvs_device *nvs ) { + struct dhcp_option *option; + int rc; + + nvs->options = alloc_dhcp_options ( nvs->len ); + if ( ! nvs->options ) { + DBG ( "NVS %p could not allocate %zd bytes\n", nvs, nvs->len ); + rc = -ENOMEM; + goto err; + } + + if ( ( rc = nvs->op->read ( nvs, 0, nvs->options->data, + nvs->len ) ) != 0 ) { + DBG ( "NVS %p could not read [0,%zd)\n", nvs, nvs->len ); + goto err; + } + + nvs->options->len = nvs->options->max_len; + nvs->options->len = nvs_options_len ( nvs ); + if ( ! nvs->options->len ) { + option = nvs->options->data; + option->tag = DHCP_END; + nvs->options->len = 1; + } + + register_dhcp_options ( nvs->options ); + + return 0; + + err: + + free_dhcp_options ( nvs->options ); + nvs->options = NULL; + return rc; +} + +void nvs_unregister ( struct nvs_device *nvs ) { + if ( nvs->options ) { + unregister_dhcp_options ( nvs->options ); + free_dhcp_options ( nvs->options ); + nvs->options = NULL; + } +} diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 91bf2d6f..1b5f29a7 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -18,9 +18,11 @@ #include "etherboot.h" #include "nic.h" +#include #include #include #include +#include #include "timer.h" #define dma_addr_t unsigned long #include "etherfabric.h" @@ -210,6 +212,9 @@ struct efab_nic { struct i2c_bit_basher ef1002_i2c; unsigned long ef1002_i2c_outputs; struct i2c_device ef1002_eeprom; + + /** NVS access */ + struct nvs_device nvs; }; /************************************************************************** @@ -2206,10 +2211,13 @@ struct efab_spi_device { unsigned int device_id; /** Address length (in bytes) */ unsigned int addr_len; - /** Read command */ - unsigned int read_command; + /** Device size */ + unsigned int len; }; +#define SPI_WRITE_CMD 0x02 +#define SPI_READ_CMD 0x03 + /** * Wait for SPI command completion * @@ -2234,8 +2242,8 @@ static int falcon_spi_wait ( struct efab_nic *efab ) { * */ static int falcon_spi_read ( struct efab_nic *efab, - struct efab_spi_device *spi, - int address, void *data, unsigned int len ) { + struct efab_spi_device *spi, int address, + void *data, unsigned int len ) { efab_oword_t reg; /* Program address register */ @@ -2250,7 +2258,7 @@ static int falcon_spi_read ( struct efab_nic *efab, FCN_EE_SPI_HCMD_READ, FCN_EE_SPI_READ, FCN_EE_SPI_HCMD_DUBCNT, 0, FCN_EE_SPI_HCMD_ADBCNT, spi->addr_len, - FCN_EE_SPI_HCMD_ENC, spi->read_command ); + FCN_EE_SPI_HCMD_ENC, SPI_READ_CMD ); falcon_write ( efab, ®, FCN_EE_SPI_HCMD_REG_KER ); /* Wait for read to complete */ @@ -2264,24 +2272,61 @@ static int falcon_spi_read ( struct efab_nic *efab, return 1; } -#define SPI_READ_CMD 0x03 +/** + * Perform SPI write + * + */ +static int falcon_spi_write ( struct efab_nic *efab, + struct efab_spi_device *spi, int address, + const void *data, unsigned int len ) { + efab_oword_t reg; + + /* Program address register */ + EFAB_POPULATE_OWORD_1 ( reg, FCN_EE_SPI_HADR_ADR, address ); + falcon_write ( efab, ®, FCN_EE_SPI_HADR_REG_KER ); + + /* Program data register */ + memcpy ( ®, data, len ); + falcon_write ( efab, ®, FCN_EE_SPI_HDATA_REG_KER ); + + /* Issue write command */ + EFAB_POPULATE_OWORD_7 ( reg, + FCN_EE_SPI_HCMD_CMD_EN, 1, + FCN_EE_SPI_HCMD_SF_SEL, spi->device_id, + FCN_EE_SPI_HCMD_DABCNT, len, + FCN_EE_SPI_HCMD_READ, FCN_EE_SPI_WRITE, + FCN_EE_SPI_HCMD_DUBCNT, 0, + FCN_EE_SPI_HCMD_ADBCNT, spi->addr_len, + FCN_EE_SPI_HCMD_ENC, SPI_WRITE_CMD ); + falcon_write ( efab, ®, FCN_EE_SPI_HCMD_REG_KER ); + + /* Wait for read to complete */ + if ( ! falcon_spi_wait ( efab ) ) + return 0; + + return 1; +} + #define AT25F1024_ADDR_LEN 3 -#define AT25F1024_READ_CMD SPI_READ_CMD +#define AT25040_ADDR_LEN 1 #define MC25XX640_ADDR_LEN 2 -#define MC25XX640_READ_CMD SPI_READ_CMD /** Falcon Flash SPI device */ static struct efab_spi_device falcon_spi_flash = { .device_id = FCN_EE_SPI_FLASH, .addr_len = AT25F1024_ADDR_LEN, - .read_command = AT25F1024_READ_CMD, }; /** Falcon EEPROM SPI device */ static struct efab_spi_device falcon_spi_large_eeprom = { .device_id = FCN_EE_SPI_EEPROM, .addr_len = MC25XX640_ADDR_LEN, - .read_command = MC25XX640_READ_CMD, +}; + +/** Falcon EEPROM SPI device */ +static struct efab_spi_device falcon_spi_small_eeprom = { + .device_id = FCN_EE_SPI_EEPROM, + .addr_len = AT25040_ADDR_LEN, }; /** Offset of MAC address within EEPROM or Flash */ @@ -2301,6 +2346,49 @@ static int falcon_read_eeprom ( struct efab_nic *efab ) { efab->mac_addr, sizeof ( efab->mac_addr ) ); } +#define FALCON_NVS_OFFSET 0x000 + +static int falcon_read_nvs ( struct nvs_device *nvs, unsigned int offset, + void *data, size_t len ) { + struct efab_nic *efab = container_of ( nvs, struct efab_nic, nvs ); + struct efab_spi_device *spi = &falcon_spi_small_eeprom; + + while ( len ) { + if ( ! falcon_spi_read ( efab, spi, + ( offset + FALCON_NVS_OFFSET ), + data, 16 ) ) { + return -EIO; + } + data += 16; + offset += 16; + len -=16; + } + return 0; +} + +static int falcon_write_nvs ( struct nvs_device *nvs, unsigned int offset, + const void *data, size_t len ) { + struct efab_nic *efab = container_of ( nvs, struct efab_nic, nvs ); + struct efab_spi_device *spi = &falcon_spi_large_eeprom; + + while ( len ) { + if ( ! falcon_spi_write ( efab, spi, + ( offset + FALCON_NVS_OFFSET ), + data, 16 ) ) { + return -EIO; + } + data += 16; + offset += 16; + len -=16; + } + return 0; +} + +static struct nvs_operations falcon_nvs_operations = { + .read = falcon_read_nvs, + .write = falcon_write_nvs, +}; + /** RX descriptor */ typedef efab_qword_t falcon_rx_desc_t; @@ -2952,6 +3040,14 @@ static int falcon_init_nic ( struct efab_nic *efab ) { falcon_write ( efab, ®, FCN_INT_ADR_REG_KER ); udelay ( 1000 ); + /* Register non-volatile storage */ + if ( efab->has_eeprom ) { + efab->nvs.op = &falcon_nvs_operations; + efab->nvs.len = 0x100; + if ( nvs_register ( &efab->nvs ) != 0 ) + return 0; + } + return 1; } @@ -3303,7 +3399,7 @@ PROBE - Look for an adapter, this routine's visible to the outside ***************************************************************************/ static int etherfabric_probe ( struct nic *nic, struct pci_device *pci ) { static struct efab_nic efab; - static int nic_port = 1; + static int nic_port = 0; struct efab_buffers *buffers; int i; diff --git a/src/include/gpxe/nvs.h b/src/include/gpxe/nvs.h new file mode 100644 index 00000000..f8a80787 --- /dev/null +++ b/src/include/gpxe/nvs.h @@ -0,0 +1,30 @@ +#ifndef _GPXE_NVS_H +#define _GPXE_NVS_H + +/** @file + * + * Non-volatile storage + * + */ + +#include + +struct nvs_operations; + +struct nvs_device { + struct dhcp_option_block *options; + size_t len; + struct nvs_operations *op; +}; + +struct nvs_operations { + int ( * read ) ( struct nvs_device *nvs, unsigned int offset, + void *data, size_t len ); + int ( * write ) ( struct nvs_device *nvs, unsigned int offset, + const void *data, size_t len ); +}; + +extern int nvs_register ( struct nvs_device *nvs ); +extern void nvs_unregister ( struct nvs_device *nvs ); + +#endif /* _GPXE_NVS_H */