|
@@ -55,6 +55,12 @@ FILE_LICENCE ( GPL2_ONLY );
|
55
|
55
|
* myri10ge_net_poll() polls for these receive notifications, posts
|
56
|
56
|
* replacement receive buffers to the NIC, and passes received frames
|
57
|
57
|
* to netdev_rx().
|
|
58
|
+ *
|
|
59
|
+ * NonVolatile Storage
|
|
60
|
+ *
|
|
61
|
+ * This driver supports NonVolatile Storage (nvs) in the NIC EEPROM.
|
|
62
|
+ * If the last EEPROM block is not otherwise filled, we tell
|
|
63
|
+ * iPXE it may store NonVolatile Options (nvo) there.
|
58
|
64
|
*/
|
59
|
65
|
|
60
|
66
|
/*
|
|
@@ -75,6 +81,8 @@ FILE_LICENCE ( GPL2_ONLY );
|
75
|
81
|
#include <ipxe/iobuf.h>
|
76
|
82
|
#include <ipxe/malloc.h>
|
77
|
83
|
#include <ipxe/netdevice.h>
|
|
84
|
+#include <ipxe/nvo.h>
|
|
85
|
+#include <ipxe/nvs.h>
|
78
|
86
|
#include <ipxe/pci.h>
|
79
|
87
|
#include <ipxe/timer.h>
|
80
|
88
|
|
|
@@ -169,6 +177,18 @@ struct myri10ge_private
|
169
|
177
|
BEWARE: the value must be written 32 bits at a time. */
|
170
|
178
|
|
171
|
179
|
mcp_cmd_t *command;
|
|
180
|
+
|
|
181
|
+ /*
|
|
182
|
+ * Nonvolatile Storage for configuration options.
|
|
183
|
+ */
|
|
184
|
+
|
|
185
|
+ struct nvs_device nvs;
|
|
186
|
+ struct nvo_fragment nvo_fragment[2];
|
|
187
|
+ struct nvo_block nvo;
|
|
188
|
+
|
|
189
|
+ /* Cached PCI capability locations. */
|
|
190
|
+
|
|
191
|
+ uint8 pci_cap_vs;
|
172
|
192
|
};
|
173
|
193
|
|
174
|
194
|
/****************************************************************
|
|
@@ -198,6 +218,28 @@ static inline struct myri10ge_private *myri10ge_priv ( struct net_device *nd )
|
198
|
218
|
return ( struct myri10ge_private * ) ( nd + 1 );
|
199
|
219
|
}
|
200
|
220
|
|
|
221
|
+/*
|
|
222
|
+ * Convert a Myri10ge driver private data pointer to a netdev pointer.
|
|
223
|
+ *
|
|
224
|
+ * @v p Myri10ge device private data.
|
|
225
|
+ * @ret r The corresponding network device.
|
|
226
|
+ */
|
|
227
|
+static inline struct net_device *myri10ge_netdev ( struct myri10ge_private *p )
|
|
228
|
+{
|
|
229
|
+ return ( ( struct net_device * ) p ) - 1;
|
|
230
|
+}
|
|
231
|
+
|
|
232
|
+/*
|
|
233
|
+ * Convert a network device pointer to a PCI device pointer.
|
|
234
|
+ *
|
|
235
|
+ * @v netdev A Network Device.
|
|
236
|
+ * @ret r The corresponding PCI device.
|
|
237
|
+ */
|
|
238
|
+static inline struct pci_device *myri10ge_pcidev ( struct net_device *netdev )
|
|
239
|
+{
|
|
240
|
+ return container_of (netdev->dev, struct pci_device, dev);
|
|
241
|
+}
|
|
242
|
+
|
201
|
243
|
/*
|
202
|
244
|
* Pass a receive buffer to the NIC to be filled.
|
203
|
245
|
*
|
|
@@ -381,12 +423,16 @@ static void myri10ge_interrupt_handler ( struct net_device *netdev )
|
381
|
423
|
/* Constants for reading the STRING_SPECS via the Myricom
|
382
|
424
|
Vendor Specific PCI configuration space capability. */
|
383
|
425
|
|
|
426
|
+#define VS_EEPROM_READ_ADDR ( vs + 0x04 )
|
|
427
|
+#define VS_EEPROM_READ_DATA ( vs + 0x08 )
|
|
428
|
+#define VS_EEPROM_WRITE ( vs + 0x0C )
|
384
|
429
|
#define VS_ADDR ( vs + 0x18 )
|
385
|
430
|
#define VS_DATA ( vs + 0x14 )
|
386
|
431
|
#define VS_MODE ( vs + 0x10 )
|
387
|
432
|
#define VS_MODE_READ32 0x3
|
388
|
433
|
#define VS_MODE_LOCATE 0x8
|
389
|
434
|
#define VS_LOCATE_STRING_SPECS 0x3
|
|
435
|
+#define VS_MODE_EEPROM_STREAM_WRITE 0xB
|
390
|
436
|
|
391
|
437
|
/*
|
392
|
438
|
* Read MAC address from its 'string specs' via the vendor-specific
|
|
@@ -394,28 +440,21 @@ static void myri10ge_interrupt_handler ( struct net_device *netdev )
|
394
|
440
|
* before it is mapped.)
|
395
|
441
|
*
|
396
|
442
|
* @v pci The device.
|
|
443
|
+ * @v vs Offset of the PCI Vendor-Specific Capability.
|
397
|
444
|
* @v mac Buffer to store the MAC address.
|
398
|
445
|
* @ret rc Returns 0 on success, else an error code.
|
399
|
446
|
*/
|
400
|
447
|
static int mac_address_from_string_specs ( struct pci_device *pci,
|
401
|
|
- uint8 mac[ETH_ALEN] )
|
|
448
|
+ unsigned int vs,
|
|
449
|
+ uint8 mac[ETH_ALEN] )
|
402
|
450
|
{
|
403
|
451
|
char string_specs[256];
|
404
|
452
|
char *ptr, *limit;
|
405
|
453
|
char *to = string_specs;
|
406
|
454
|
uint32 addr;
|
407
|
455
|
uint32 len;
|
408
|
|
- unsigned int vs;
|
409
|
456
|
int mac_set = 0;
|
410
|
457
|
|
411
|
|
- /* Find the "vendor specific" capability. */
|
412
|
|
-
|
413
|
|
- vs = pci_find_capability ( pci, 9 );
|
414
|
|
- if ( vs == 0 ) {
|
415
|
|
- DBG ( "no VS\n" );
|
416
|
|
- return -ENOTSUP;
|
417
|
|
- }
|
418
|
|
-
|
419
|
458
|
/* Locate the String specs in LANai SRAM. */
|
420
|
459
|
|
421
|
460
|
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,
|
427
|
466
|
/* Copy in the string specs. Use 32-bit reads for performance. */
|
428
|
467
|
|
429
|
468
|
if ( len > sizeof ( string_specs ) || ( len & 3 ) ) {
|
|
469
|
+ pci_write_config_byte ( pci, VS_MODE, 0 );
|
430
|
470
|
DBG ( "SS too big\n" );
|
431
|
471
|
return -ENOTSUP;
|
432
|
472
|
}
|
|
@@ -483,6 +523,247 @@ static int mac_address_from_string_specs ( struct pci_device *pci,
|
483
|
523
|
return 0;
|
484
|
524
|
}
|
485
|
525
|
|
|
526
|
+/****************************************************************
|
|
527
|
+ * NonVolatile Storage support
|
|
528
|
+ ****************************************************************/
|
|
529
|
+
|
|
530
|
+/*
|
|
531
|
+ * Fill a buffer with data read from nonvolatile storage.
|
|
532
|
+ *
|
|
533
|
+ * @v nvs The NonVolatile Storage device to be read.
|
|
534
|
+ * @v addr The first NonVolatile Storage address to be read.
|
|
535
|
+ * @v _buf Pointer to the data buffer to be filled.
|
|
536
|
+ * @v len The number of bytes to copy.
|
|
537
|
+ * @ret rc 0 on success, else nonzero.
|
|
538
|
+ */
|
|
539
|
+static int myri10ge_nvs_read ( struct nvs_device *nvs,
|
|
540
|
+ unsigned int addr,
|
|
541
|
+ void *_buf,
|
|
542
|
+ size_t len )
|
|
543
|
+{
|
|
544
|
+ struct myri10ge_private *priv =
|
|
545
|
+ container_of (nvs, struct myri10ge_private, nvs);
|
|
546
|
+ struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
|
|
547
|
+ unsigned int vs = priv->pci_cap_vs;
|
|
548
|
+ unsigned char *buf = (unsigned char *) _buf;
|
|
549
|
+ unsigned int data;
|
|
550
|
+ unsigned int i, j;
|
|
551
|
+
|
|
552
|
+ DBGP ( "myri10ge_nvs_read\n" );
|
|
553
|
+
|
|
554
|
+ /* Issue the first read address. */
|
|
555
|
+
|
|
556
|
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 3, addr>>16 );
|
|
557
|
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 2, addr>>8 );
|
|
558
|
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
|
|
559
|
+ addr++;
|
|
560
|
+
|
|
561
|
+ /* Issue all the reads, and harvest the results every 4th issue. */
|
|
562
|
+
|
|
563
|
+ for ( i=0; i<len; ++i,addr++ ) {
|
|
564
|
+
|
|
565
|
+ /* Issue the next read address, updating only the
|
|
566
|
+ bytes that need updating. We always update the
|
|
567
|
+ LSB, which triggers the read. */
|
|
568
|
+
|
|
569
|
+ if ( ( addr & 0xff ) == 0 ) {
|
|
570
|
+ if ( ( addr & 0xffff ) == 0 ) {
|
|
571
|
+ pci_write_config_byte ( pci,
|
|
572
|
+ VS_EEPROM_READ_ADDR + 3,
|
|
573
|
+ addr >> 16 );
|
|
574
|
+ }
|
|
575
|
+ pci_write_config_byte ( pci,
|
|
576
|
+ VS_EEPROM_READ_ADDR + 2,
|
|
577
|
+ addr >> 8 );
|
|
578
|
+ }
|
|
579
|
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
|
|
580
|
+
|
|
581
|
+ /* If 4 data bytes are available, read them with a single read. */
|
|
582
|
+
|
|
583
|
+ if ( ( i & 3 ) == 3 ) {
|
|
584
|
+ pci_read_config_dword ( pci,
|
|
585
|
+ VS_EEPROM_READ_DATA,
|
|
586
|
+ &data );
|
|
587
|
+ for ( j=0; j<4; j++ ) {
|
|
588
|
+ buf[i-j] = data;
|
|
589
|
+ data >>= 8;
|
|
590
|
+ }
|
|
591
|
+ }
|
|
592
|
+ }
|
|
593
|
+
|
|
594
|
+ /* Harvest any remaining results. */
|
|
595
|
+
|
|
596
|
+ if ( ( i & 3 ) != 0 ) {
|
|
597
|
+ pci_read_config_dword ( pci, VS_EEPROM_READ_DATA, &data );
|
|
598
|
+ for ( j=1; j<=(i&3); j++ ) {
|
|
599
|
+ buf[i-j] = data;
|
|
600
|
+ data >>= 8;
|
|
601
|
+ }
|
|
602
|
+ }
|
|
603
|
+
|
|
604
|
+ DBGP_HDA ( addr - len, _buf, len );
|
|
605
|
+ return 0;
|
|
606
|
+}
|
|
607
|
+
|
|
608
|
+/*
|
|
609
|
+ * Write a buffer into nonvolatile storage.
|
|
610
|
+ *
|
|
611
|
+ * @v nvs The NonVolatile Storage device to be written.
|
|
612
|
+ * @v address The NonVolatile Storage address to be written.
|
|
613
|
+ * @v _buf Pointer to the data to be written.
|
|
614
|
+ * @v len Length of the buffer to be written.
|
|
615
|
+ * @ret rc 0 on success, else nonzero.
|
|
616
|
+ */
|
|
617
|
+static int myri10ge_nvs_write ( struct nvs_device *nvs,
|
|
618
|
+ unsigned int addr,
|
|
619
|
+ const void *_buf,
|
|
620
|
+ size_t len )
|
|
621
|
+{
|
|
622
|
+ struct myri10ge_private *priv =
|
|
623
|
+ container_of (nvs, struct myri10ge_private, nvs);
|
|
624
|
+ struct pci_device *pci = myri10ge_pcidev ( myri10ge_netdev ( priv ) );
|
|
625
|
+ unsigned int vs = priv->pci_cap_vs;
|
|
626
|
+ const unsigned char *buf = (const unsigned char *)_buf;
|
|
627
|
+ unsigned int i;
|
|
628
|
+ uint8 verify;
|
|
629
|
+
|
|
630
|
+ DBGP ( "nvs_write " );
|
|
631
|
+ DBGP_HDA ( addr, _buf, len );
|
|
632
|
+
|
|
633
|
+ /* Start erase of the NonVolatile Options block. */
|
|
634
|
+
|
|
635
|
+ DBGP ( "erasing " );
|
|
636
|
+ pci_write_config_dword ( pci, VS_EEPROM_WRITE, ( addr << 8 ) | 0xff );
|
|
637
|
+
|
|
638
|
+ /* Wait for erase to complete. */
|
|
639
|
+
|
|
640
|
+ DBGP ( "waiting " );
|
|
641
|
+ pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
|
|
642
|
+ while ( verify != 0xff ) {
|
|
643
|
+ pci_write_config_byte ( pci, VS_EEPROM_READ_ADDR + 1, addr );
|
|
644
|
+ pci_read_config_byte ( pci, VS_EEPROM_READ_DATA, &verify );
|
|
645
|
+ }
|
|
646
|
+
|
|
647
|
+ /* Write the data one byte at a time. */
|
|
648
|
+
|
|
649
|
+ DBGP ( "writing " );
|
|
650
|
+ pci_write_config_byte ( pci, VS_MODE, VS_MODE_EEPROM_STREAM_WRITE );
|
|
651
|
+ pci_write_config_dword ( pci, VS_ADDR, addr );
|
|
652
|
+ for (i=0; i<len; i++, addr++)
|
|
653
|
+ pci_write_config_byte ( pci, VS_DATA, buf[i] );
|
|
654
|
+ pci_write_config_dword ( pci, VS_ADDR, 0xffffffff );
|
|
655
|
+ pci_write_config_byte ( pci, VS_MODE, 0 );
|
|
656
|
+
|
|
657
|
+ DBGP ( "done\n" );
|
|
658
|
+ return 0;
|
|
659
|
+}
|
|
660
|
+
|
|
661
|
+/*
|
|
662
|
+ * Initialize NonVolatile storage support for a device.
|
|
663
|
+ *
|
|
664
|
+ * @v priv Device private data for the device.
|
|
665
|
+ * @ret rc 0 on success, else an error code.
|
|
666
|
+ */
|
|
667
|
+
|
|
668
|
+static int myri10ge_nv_init ( struct myri10ge_private *priv )
|
|
669
|
+{
|
|
670
|
+ int rc;
|
|
671
|
+ struct myri10ge_eeprom_header
|
|
672
|
+ {
|
|
673
|
+ uint8 __jump[8];
|
|
674
|
+ uint32 eeprom_len;
|
|
675
|
+ uint32 eeprom_segment_len;
|
|
676
|
+ uint32 mcp1_offset;
|
|
677
|
+ uint32 mcp2_offset;
|
|
678
|
+ uint32 version;
|
|
679
|
+ } hdr;
|
|
680
|
+ uint32 mcp2_len;
|
|
681
|
+ unsigned int nvo_fragment_pos;
|
|
682
|
+
|
|
683
|
+ DBGP ( "myri10ge_nv_init\n" );
|
|
684
|
+
|
|
685
|
+ /* Read the EEPROM header, and byteswap the fields we will use.
|
|
686
|
+ This is safe even though priv->nvs is not yet initialized. */
|
|
687
|
+
|
|
688
|
+ rc = myri10ge_nvs_read ( &priv->nvs, 0, &hdr, sizeof ( hdr ) );
|
|
689
|
+ if ( rc ) {
|
|
690
|
+ DBG ( "EEPROM header unreadable\n" );
|
|
691
|
+ return rc;
|
|
692
|
+ }
|
|
693
|
+ hdr.eeprom_len = ntohl ( hdr.eeprom_len );
|
|
694
|
+ hdr.eeprom_segment_len = ntohl ( hdr.eeprom_segment_len );
|
|
695
|
+ hdr.mcp2_offset = ntohl ( hdr.mcp2_offset );
|
|
696
|
+ hdr.version = ntohl ( hdr.version );
|
|
697
|
+ DBG2 ( "eelen:%xh seglen:%xh mcp2@%xh ver%d\n", hdr.eeprom_len,
|
|
698
|
+ hdr.eeprom_segment_len, hdr.mcp2_offset, hdr.version );
|
|
699
|
+
|
|
700
|
+ /* If the firmware does not support EEPROM writes, simply return. */
|
|
701
|
+
|
|
702
|
+ if ( hdr.version < 1 ) {
|
|
703
|
+ DBG ( "No EEPROM write support\n" );
|
|
704
|
+ return 0;
|
|
705
|
+ }
|
|
706
|
+
|
|
707
|
+ /* Read the length of MCP2. */
|
|
708
|
+
|
|
709
|
+ rc = myri10ge_nvs_read ( &priv->nvs, hdr.mcp2_offset, &mcp2_len, 4 );
|
|
710
|
+ mcp2_len = ntohl ( mcp2_len );
|
|
711
|
+ DBG2 ( "mcp2len:%xh\n", mcp2_len );
|
|
712
|
+
|
|
713
|
+ /* Determine the position of the NonVolatile Options fragment and
|
|
714
|
+ simply return if it overlaps other data. */
|
|
715
|
+
|
|
716
|
+ nvo_fragment_pos = hdr.eeprom_len - hdr.eeprom_segment_len;
|
|
717
|
+ if ( hdr.mcp2_offset + mcp2_len > nvo_fragment_pos ) {
|
|
718
|
+ DBG ( "EEPROM full\n" );
|
|
719
|
+ return 0;
|
|
720
|
+ }
|
|
721
|
+
|
|
722
|
+ /* Initilize NonVolatile Storage state. */
|
|
723
|
+
|
|
724
|
+ priv->nvs.word_len_log2 = 0;
|
|
725
|
+ priv->nvs.size = hdr.eeprom_len;
|
|
726
|
+ priv->nvs.block_size = hdr.eeprom_segment_len;
|
|
727
|
+ priv->nvs.read = myri10ge_nvs_read;
|
|
728
|
+ priv->nvs.write = myri10ge_nvs_write;
|
|
729
|
+
|
|
730
|
+ /* Build the NonVolatile storage fragment list. We would like
|
|
731
|
+ to use the whole last EEPROM block for this, but we must
|
|
732
|
+ reduce the block size lest malloc fail in
|
|
733
|
+ src/core/nvo.o. */
|
|
734
|
+
|
|
735
|
+ priv->nvo_fragment[0].address = nvo_fragment_pos;
|
|
736
|
+ priv->nvo_fragment[0].len = 0x200;
|
|
737
|
+
|
|
738
|
+ /* Register the NonVolatile Options storage. */
|
|
739
|
+
|
|
740
|
+ nvo_init ( &priv->nvo,
|
|
741
|
+ &priv->nvs,
|
|
742
|
+ priv->nvo_fragment,
|
|
743
|
+ & myri10ge_netdev (priv) -> refcnt );
|
|
744
|
+ rc = register_nvo ( &priv->nvo,
|
|
745
|
+ netdev_settings ( myri10ge_netdev ( priv ) ) );
|
|
746
|
+ if ( rc ) {
|
|
747
|
+ DBG ("register_nvo failed");
|
|
748
|
+ priv->nvo_fragment[0].len = 0;
|
|
749
|
+ return rc;
|
|
750
|
+ }
|
|
751
|
+
|
|
752
|
+ DBG2 ( "NVO supported\n" );
|
|
753
|
+ return 0;
|
|
754
|
+}
|
|
755
|
+
|
|
756
|
+void
|
|
757
|
+myri10ge_nv_fini ( struct myri10ge_private *priv )
|
|
758
|
+{
|
|
759
|
+ /* Simply return if nonvolatile access is not supported. */
|
|
760
|
+
|
|
761
|
+ if ( 0 == priv->nvo_fragment[0].len )
|
|
762
|
+ return;
|
|
763
|
+
|
|
764
|
+ unregister_nvo ( &priv->nvo );
|
|
765
|
+}
|
|
766
|
+
|
486
|
767
|
/****************************************************************
|
487
|
768
|
* iPXE PCI Device Driver API functions
|
488
|
769
|
****************************************************************/
|
|
@@ -532,9 +813,20 @@ static int myri10ge_pci_probe ( struct pci_device *pci,
|
532
|
813
|
|
533
|
814
|
myri10ge_net_irq ( netdev, 0 );
|
534
|
815
|
|
|
816
|
+ /* Find the PCI Vendor-Specific capability. */
|
|
817
|
+
|
|
818
|
+ priv->pci_cap_vs = pci_find_capability ( pci , PCI_CAP_ID_VNDR );
|
|
819
|
+ if ( 0 == priv->pci_cap_vs ) {
|
|
820
|
+ rc = -ENOTSUP;
|
|
821
|
+ dbg = "no_vs";
|
|
822
|
+ goto abort_with_netdev_init;
|
|
823
|
+ }
|
|
824
|
+
|
535
|
825
|
/* Read the NIC HW address. */
|
536
|
826
|
|
537
|
|
- rc = mac_address_from_string_specs ( pci, netdev->hw_addr );
|
|
827
|
+ rc = mac_address_from_string_specs ( pci,
|
|
828
|
+ priv->pci_cap_vs,
|
|
829
|
+ netdev->hw_addr );
|
538
|
830
|
if ( rc ) {
|
539
|
831
|
dbg = "mac_from_ss";
|
540
|
832
|
goto abort_with_netdev_init;
|
|
@@ -554,10 +846,20 @@ static int myri10ge_pci_probe ( struct pci_device *pci,
|
554
|
846
|
goto abort_with_netdev_init;
|
555
|
847
|
}
|
556
|
848
|
|
|
849
|
+ /* Initialize NonVolatile Storage support. */
|
|
850
|
+
|
|
851
|
+ rc = myri10ge_nv_init ( priv );
|
|
852
|
+ if ( rc ) {
|
|
853
|
+ dbg = "myri10ge_nv_init";
|
|
854
|
+ goto abort_with_registered_netdev;
|
|
855
|
+ }
|
|
856
|
+
|
557
|
857
|
DBGP ( "done\n" );
|
558
|
858
|
|
559
|
859
|
return 0;
|
560
|
860
|
|
|
861
|
+abort_with_registered_netdev:
|
|
862
|
+ unregister_netdev ( netdev );
|
561
|
863
|
abort_with_netdev_init:
|
562
|
864
|
netdev_nullify ( netdev );
|
563
|
865
|
netdev_put ( netdev );
|
|
@@ -580,6 +882,7 @@ static void myri10ge_pci_remove ( struct pci_device *pci )
|
580
|
882
|
DBGP ( "myri10ge_pci_remove\n" );
|
581
|
883
|
netdev = pci_get_drvdata ( pci );
|
582
|
884
|
|
|
885
|
+ myri10ge_nv_fini ( myri10ge_priv ( netdev ) );
|
583
|
886
|
unregister_netdev ( netdev );
|
584
|
887
|
netdev_nullify ( netdev );
|
585
|
888
|
netdev_put ( netdev );
|