[PATCH 4/4] [myri10ge] Add NonVolatile Option (nvo) support
Glenn Brown
glenn at myri.com
Mon Jun 21 01:57:41 EDT 2010
Add NonVolatile Option (nvo) and NonVolatile Storage (nvs) support to
the myri10ge driver using the EEPROM read/write mechanism provided by
the NIC's Vendor Specific PCI capability.
The myri10ge NIC is capabile of storing 64KB or more of nonvolatile
options, but this patch advertises only 512 bytes of nvo storage because
gPXE malloc's a buffer matching the total size we advertise. 512 is
plenty without wasting malloc'd memory. (The 2 other drivers currently
supporting nvo advertise 256 bytes or less.)
Signed-off-by: Glenn Brown <glenn at myri.com>
---
src/drivers/net/myri10ge.c | 325 ++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 314 insertions(+), 11 deletions(-)
diff --git a/src/drivers/net/myri10ge.c b/src/drivers/net/myri10ge.c
index 353158b..99bc272 100644
--- a/src/drivers/net/myri10ge.c
+++ b/src/drivers/net/myri10ge.c
@@ -55,6 +55,12 @@ FILE_LICENCE ( GPL2_ONLY );
* myri10ge_net_poll() polls for these receive notifications, posts
* replacement receive buffers to the NIC, and passes received frames
* to netdev_rx().
+ *
+ * NonVolatile Storage
+ *
+ * This driver supports NonVolatile Storage (nvs) in the NIC EEPROM.
+ * If the last EEPROM block is not otherwise filled, we tell
+ * gPXE it may store NonVolatile Options (nvo) there.
*/
/*
@@ -75,6 +81,8 @@ FILE_LICENCE ( GPL2_ONLY );
#include <gpxe/iobuf.h>
#include <gpxe/malloc.h>
#include <gpxe/netdevice.h>
+#include <gpxe/nvo.h>
+#include <gpxe/nvs.h>
#include <gpxe/pci.h>
#include <gpxe/timer.h>
@@ -169,6 +177,18 @@ struct myri10ge_private
BEWARE: the value must be written 32 bits at a time. */
mcp_cmd_t *command;
+
+ /*
+ * Nonvolatile Storage for configuration options.
+ */
+
+ struct nvs_device nvs;
+ struct nvo_fragment nvo_fragment[2];
+ struct nvo_block nvo;
+
+ /* Cached PCI capability locations. */
+
+ uint8 pci_cap_vs;
};
/****************************************************************
@@ -199,6 +219,28 @@ static inline struct myri10ge_private *myri10ge_priv ( struct net_device *nd )
}
/*
+ * Convert a Myri10ge driver private data pointer to a netdev pointer.
+ *
+ * @v p Myri10ge device private data.
+ * @ret r The corresponding network device.
+ */
+static inline struct net_device *myri10ge_netdev ( struct myri10ge_private *p )
+{
+ return ( ( struct net_device * ) p ) - 1;
+}
+
+/*
+ * Convert a network device pointer to a PCI device pointer.
+ *
+ * @v netdev A Network Device.
+ * @ret r The corresponding PCI device.
+ */
+static inline struct pci_device *myri10ge_pcidev ( struct net_device *netdev )
+{
+ return container_of (netdev->dev, struct pci_device, dev);
+}
+
+/*
* Pass a receive buffer to the NIC to be filled.
*
* @v priv The network device to receive the buffer.
@@ -381,12 +423,16 @@ static void myri10ge_interrupt_handler ( struct net_device *netdev )
/* Constants for reading the STRING_SPECS via the Myricom
Vendor Specific PCI configuration space capability. */
+#define VS_EEPROM_READ_ADDR ( vs + 0x04 )
+#define VS_EEPROM_READ_DATA ( vs + 0x08 )
+#define VS_EEPROM_WRITE ( vs + 0x0C )
#define VS_ADDR ( vs + 0x18 )
#define VS_DATA ( vs + 0x14 )
#define VS_MODE ( vs + 0x10 )
#define VS_MODE_READ32 0x3
#define VS_MODE_LOCATE 0x8
#define VS_LOCATE_STRING_SPECS 0x3
+#define VS_MODE_EEPROM_STREAM_WRITE 0xB
/*
* Read MAC address from its 'string specs' via the vendor-specific
@@ -394,28 +440,21 @@ static void myri10ge_interrupt_handler ( struct net_device *netdev )
* before it is mapped.)
*
* @v pci The device.
+ * @v vs Offset of the PCI Vendor-Specific Capability.
* @v mac Buffer to store the MAC address.
* @ret rc Returns 0 on success, else an error code.
*/
static int mac_address_from_string_specs ( struct pci_device *pci,
- uint8 mac[ETH_ALEN] )
+ unsigned int vs,
+ uint8 mac[ETH_ALEN] )
{
char string_specs[256];
char *ptr, *limit;
char *to = string_specs;
uint32 addr;
uint32 len;
- unsigned int vs;
int mac_set = 0;
- /* Find the "vendor specific" capability. */
-
- vs = pci_find_capability ( pci, 9 );
- if ( vs == 0 ) {
- DBG ( "no VS\n" );
- return -ENOTSUP;
- }
-
/* Locate the String specs in LANai SRAM. */
pci_write_config_byte ( pci, VS_MODE, VS_MODE_LOCATE );
@@ -427,6 +466,7 @@ static int mac_address_from_string_specs ( struct pci_device *pci,
/* Copy in the string specs. Use 32-bit reads for performance. */
if ( len > sizeof ( string_specs ) || ( len & 3 ) ) {
+ pci_write_config_byte ( pci, VS_MODE, 0 );
DBG ( "SS too big\n" );
return -ENOTSUP;
}
@@ -484,6 +524,247 @@ static int mac_address_from_string_specs ( struct pci_device *pci,
}
/****************************************************************
+ * NonVolatile Storage support
+ ****************************************************************/
+
+/*
+ * Fill a buffer with data read from nonvolatile storage.
+ *
+ * @v nvs The NonVolatile Storage device to be read.
+ * @v addr The first NonVolatile Storage address to be read.
+ * @v _buf Pointer to the data buffer to be filled.
+ * @v len The number of bytes to copy.
+ * @ret rc 0 on success, else nonzero.
+ */
+static int myri10ge_nvs_read ( struct nvs_device *nvs,
+ unsigned int addr,
+ void *_buf,
+ size_t len )
+{
+ struct myri10ge_private *priv =
+ container_of (nvs, struct myri10ge_private, nvs);
+ struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
+ unsigned int vs = priv->pci_cap_vs;
+ unsigned char *buf = (unsigned char *) _buf;
+ unsigned int data;
+ unsigned int i, j;
+
+ DBGP ( "myri10ge_nvs_read\n" );
+
+ /* Issue the first read address. */
+
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 3, addr>>16 );
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 2, addr>>8 );
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
+ addr++;
+
+ /* Issue all the reads, and harvest the results every 4th issue. */
+
+ for ( i=0; i<len; ++i,addr++ ) {
+
+ /* Issue the next read address, updating only the
+ bytes that need updating. We always update the
+ LSB, which triggers the read. */
+
+ if ( ( addr & 0xff ) == 0 ) {
+ if ( ( addr & 0xffff ) == 0 ) {
+ pci_write_config_byte ( pci,
+ VS_EEPROM_READ_ADDR + 3,
+ addr >> 16 );
+ }
+ pci_write_config_byte ( pci,
+ VS_EEPROM_READ_ADDR + 2,
+ addr >> 8 );
+ }
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
+
+ /* If 4 data bytes are available, read them with a single read. */
+
+ if ( ( i & 3 ) == 3 ) {
+ pci_read_config_dword ( pci,
+ VS_EEPROM_READ_DATA,
+ &data );
+ for ( j=0; j<4; j++ ) {
+ buf[i-j] = data;
+ data >>= 8;
+ }
+ }
+ }
+
+ /* Harvest any remaining results. */
+
+ if ( ( i & 3 ) != 0 ) {
+ pci_read_config_dword ( pci, VS_EEPROM_READ_DATA, &data );
+ for ( j=1; j<=(i&3); j++ ) {
+ buf[i-j] = data;
+ data >>= 8;
+ }
+ }
+
+ DBGP_HDA ( addr - len, _buf, len );
+ return 0;
+}
+
+/*
+ * Write a buffer into nonvolatile storage.
+ *
+ * @v nvs The NonVolatile Storage device to be written.
+ * @v address The NonVolatile Storage address to be written.
+ * @v _buf Pointer to the data to be written.
+ * @v len Length of the buffer to be written.
+ * @ret rc 0 on success, else nonzero.
+ */
+static int myri10ge_nvs_write ( struct nvs_device *nvs,
+ unsigned int addr,
+ const void *_buf,
+ size_t len )
+{
+ struct myri10ge_private *priv =
+ container_of (nvs, struct myri10ge_private, nvs);
+ struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
+ unsigned int vs = priv->pci_cap_vs;
+ const unsigned char *buf = (const unsigned char *)_buf;
+ unsigned int i;
+ uint8 verify;
+
+ DBGP ( "nvs_write " );
+ DBGP_HDA ( addr, _buf, len );
+
+ /* Start erase of the NonVolatile Options block. */
+
+ DBGP ( "erasing " );
+ pci_write_config_dword ( pci, VS_EEPROM_WRITE, ( addr << 8 ) | 0xff );
+
+ /* Wait for erase to complete. */
+
+ DBGP ( "waiting " );
+ pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
+ while ( verify != 0xff ) {
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
+ pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
+ }
+
+ /* Write the data one byte at a time. */
+
+ DBGP ( "writing " );
+ pci_write_config_byte ( pci, VS_MODE, VS_MODE_EEPROM_STREAM_WRITE );
+ pci_write_config_dword ( pci, VS_ADDR, addr );
+ for (i=0; i<len; i++, addr++)
+ pci_write_config_byte ( pci, VS_DATA, buf[i] );
+ pci_write_config_dword ( pci, VS_ADDR, 0xffffffff );
+ pci_write_config_byte ( pci, VS_MODE, 0 );
+
+ DBGP ( "done\n" );
+ return 0;
+}
+
+/*
+ * Initialize NonVolatile storage support for a device.
+ *
+ * @v priv Device private data for the device.
+ * @ret rc 0 on success, else an error code.
+ */
+
+static int myri10ge_nv_init ( struct myri10ge_private *priv )
+{
+ int rc;
+ struct myri10ge_eeprom_header
+ {
+ uint8 __jump[8];
+ uint32 eeprom_len;
+ uint32 eeprom_segment_len;
+ uint32 mcp1_offset;
+ uint32 mcp2_offset;
+ uint32 version;
+ } hdr;
+ uint32 mcp2_len;
+ unsigned int nvo_fragment_pos;
+
+ DBGP ( "myri10ge_nv_init\n" );
+
+ /* Read the EEPROM header, and byteswap the fields we will use.
+ This is safe even though priv->nvs is not yet initialized. */
+
+ rc = myri10ge_nvs_read ( &priv->nvs, 0, &hdr, sizeof ( hdr ) );
+ if ( rc ) {
+ DBG ( "EEPROM header unreadable\n" );
+ return rc;
+ }
+ hdr.eeprom_len = bswap_32 ( hdr.eeprom_len );
+ hdr.eeprom_segment_len = bswap_32 ( hdr.eeprom_segment_len );
+ hdr.mcp2_offset = bswap_32 ( hdr.mcp2_offset );
+ hdr.version = bswap_32 ( hdr.version );
+ DBG2 ( "eelen:%xh seglen:%xh mcp2@%xh ver%d\n", hdr.eeprom_len,
+ hdr.eeprom_segment_len, hdr.mcp2_offset, hdr.version );
+
+ /* If the firmware does not support EEPROM writes, simply return. */
+
+ if ( hdr.version < 1 ) {
+ DBG ( "No EEPROM write support\n" );
+ return 0;
+ }
+
+ /* Read the length of MCP2. */
+
+ rc = myri10ge_nvs_read ( &priv->nvs, hdr.mcp2_offset, &mcp2_len, 4 );
+ mcp2_len = bswap_32 ( mcp2_len );
+ DBG2 ( "mcp2len:%xh\n", mcp2_len );
+
+ /* Determine the position of the NonVolatile Options fragment and
+ simply return if it overlaps other data. */
+
+ nvo_fragment_pos = hdr.eeprom_len - hdr.eeprom_segment_len;
+ if ( hdr.mcp2_offset + mcp2_len > nvo_fragment_pos ) {
+ DBG ( "EEPROM full\n" );
+ return 0;
+ }
+
+ /* Initilize NonVolatile Storage state. */
+
+ priv->nvs.word_len_log2 = 0;
+ priv->nvs.size = hdr.eeprom_len;
+ priv->nvs.block_size = hdr.eeprom_segment_len;
+ priv->nvs.read = myri10ge_nvs_read;
+ priv->nvs.write = myri10ge_nvs_write;
+
+ /* Build the NonVolatile storage fragment list. We would like
+ to use the whole last EEPROM block for this, but we must
+ reduce the block size lest malloc fail in
+ src/core/nvo.o. */
+
+ priv->nvo_fragment[0].address = nvo_fragment_pos;
+ priv->nvo_fragment[0].len = 0x200;
+
+ /* Register the NonVolatile Options storage. */
+
+ nvo_init ( &priv->nvo,
+ &priv->nvs,
+ priv->nvo_fragment,
+ & myri10ge_netdev (priv) -> refcnt );
+ rc = register_nvo ( &priv->nvo,
+ netdev_settings ( myri10ge_netdev ( priv ) ) );
+ if ( rc ) {
+ DBG ("register_nvo failed");
+ priv->nvo_fragment[0].len = 0;
+ return rc;
+ }
+
+ DBG2 ( "NVO supported\n" );
+ return 0;
+}
+
+void
+myri10ge_nv_fini ( struct myri10ge_private *priv )
+{
+ /* Simply return if nonvolatile access is not supported. */
+
+ if ( 0 == priv->nvo_fragment[0].len )
+ return;
+
+ unregister_nvo ( &priv->nvo );
+}
+
+/****************************************************************
* gPXE PCI Device Driver API functions
****************************************************************/
@@ -532,9 +813,20 @@ static int myri10ge_pci_probe ( struct pci_device *pci,
myri10ge_net_irq ( netdev, 0 );
+ /* Find the PCI Vendor-Specific capability. */
+
+ priv->pci_cap_vs = pci_find_capability ( pci , PCI_CAP_ID_VS );
+ if ( 0 == priv->pci_cap_vs ) {
+ rc = -ENOTSUP;
+ dbg = "no_vs";
+ goto abort_with_netdev_init;
+ }
+
/* Read the NIC HW address. */
- rc = mac_address_from_string_specs ( pci, netdev->hw_addr );
+ rc = mac_address_from_string_specs ( pci,
+ priv->pci_cap_vs,
+ netdev->hw_addr );
if ( rc ) {
dbg = "mac_from_ss";
goto abort_with_netdev_init;
@@ -554,10 +846,20 @@ static int myri10ge_pci_probe ( struct pci_device *pci,
goto abort_with_netdev_init;
}
+ /* Initialize NonVolatile Storage support. */
+
+ rc = myri10ge_nv_init ( priv );
+ if ( rc ) {
+ dbg = "myri10ge_nv_init";
+ goto abort_with_registered_netdev;
+ }
+
DBGP ( "done\n" );
return 0;
+abort_with_registered_netdev:
+ unregister_netdev ( netdev );
abort_with_netdev_init:
netdev_nullify ( netdev );
netdev_put ( netdev );
@@ -580,6 +882,7 @@ static void myri10ge_pci_remove ( struct pci_device *pci )
DBGP ( "myri10ge_pci_remove\n" );
netdev = pci_get_drvdata ( pci );
+ myri10ge_nv_fini ( myri10ge_priv ( netdev ) );
unregister_netdev ( netdev );
netdev_nullify ( netdev );
netdev_put ( netdev );
--
1.7.0.4
--------------000002080307040702050807
Content-Type: image/gif;
name="gpxe_config.gif"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="gpxe_config.gif"
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
CAz3uMhNrnKXy9zm/zr3udCNrnSnS93lFhcCyB1AcLUrWlNVV7afAm54Wzte0EqQuwKw1mrV
O9ryrhe95n1Wd+WrWvbOt730Ta57ebtf+6Y3v/etL4D9+93Z7rfAsCUwag+sW/d6l7XXfcBx
GZzbA1MYwae98G01jF/4lne8H4ZvaUM8XxEv2MHoBXGKTYzf0KLYwytWLodp++D4upi7JC6x
gG9s4x5jOLYz/vGIY/xe8Yo4yKCNsAMmzOIKmxjJQnZuf2tMWgZD+b83RjKAW4xlHxc5vijm
cYC5TOYxMznMPc4xmJs8ZhWLOcpwzu6R2SxjOo9WyQ3IsHap7F/7SnDHfA70gPds5x9T+f+/
gn7xlFlsZTZbuNBl7jJrL+xmRJP3yTB+s5c1zelNW1rHbj70iduMYyJjecYqLnWnqzzoGnsX
1eFVc6gXPen8qlnPi86xops8ZVbPOdEwPrSwRV1aPDPgxIT2caUlzezULpvTj052nFlNamWr
etNXpvCjL73tS/s6xlvW85A7fGXcovnckG72n9dcaVgjesXEJvOyUx1ucj9Y1rGe85e7fGtn
m7rd/w52upmtayL3u9czNvYCRs1vg5u62e8ueJZ7zeVyU5fiZs64xhkeaYjv29Mcp/a7PS7y
kkva4rVFd4C17GhGl9rP7JXvvO3caDHHmuQ8lja+sZ3ubUsb2Wj/XnmmQ070bps80s8uNgQ+
K255X5vgAwc4z0WOculGe+NgHre3O151jH+84knXuthPPnBzD13lX9+40XGd5oeP/c2hxrmN
dR7wpr830z83bdIDzfOW19rXWP800p9uWoUrgOhkt3bgqX5tr7d92keHO80x/XZPO961l0e8
zd0eeTMf3OWV9zjaH5/2Va9d7w5ftdpTn/dx013ydv/73OXecLGHneSZ1zbnua5v2hueAIiX
uJfzvePaq/vnewd952ve+dgL3/jHD/3pP1/t1Y/9+V6nPuytv3zlk/77INe99AuN/d4rPvGn
jn7Ood9v7p//vGWe+ebPz37zez7133f8/+1F+3tcYxrYgCdggnZ88DZ13Td+oYd6u1ZiC8iA
d8d3DDhs9haB9hdx8NaA9wdztlZvRoaB0Nd3rjZsjQZrADh4JYh+moZv9FVv4ZZreEd5oHaB
wUZ2J+hvHthnW9ZqCbd0xlVgVQd5QBiEQghdPziECFaERpiEEMaD2OWDZaeEUBiFUphyTziF
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
eK4b7nBbQzzisp44xV0dgAQAADs=
--------------000002080307040702050807--
More information about the gPXE
mailing list