| /****************************************************************************** |
| * Copyright (c) 2004, 2008 IBM Corporation |
| * All rights reserved. |
| * This program and the accompanying materials |
| * are made available under the terms of the BSD License |
| * which accompanies this distribution, and is available at |
| * http://www.opensource.org/licenses/bsd-license.php |
| * |
| * Contributors: |
| * IBM Corporation - initial implementation |
| *****************************************************************************/ |
| |
| /* |
| * |
| ****************************************************************************** |
| * reference: |
| * Broadcom 57xx |
| * Host Programmer Interface Specification for the |
| * NetXtreme Family of Highly-Integrated Media Access Controlers |
| */ |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <byteorder.h> |
| #include <helpers.h> |
| #include <netdriver.h> |
| #include "bcm57xx.h" |
| |
| /* |
| * local defines |
| ****************************************************************************** |
| */ |
| |
| |
| // #define BCM_VLAN_TAG ( (uint32_t) 0x1 ) |
| |
| // number of tx/rx rings |
| // NOTE: 5714 only uses 1 rx/tx ring, but memory |
| // for the other rings is cleaned anyways for |
| // sanity & future use |
| #define BCM_MAX_TX_RING 16 |
| #define BCM_MAX_RXRET_RING 16 |
| #define BCM_MAX_RXPROD_RCB 3 |
| |
| // bd descriptions |
| #define BCM_RXPROD_RING_SIZE 512 // don't change |
| #define BCM_RXRET_RING_SIZE 512 // don't change |
| #define BCM_TX_RING_SIZE 512 // don't change |
| #define BCM_BUF_SIZE 1536 // don't change |
| #define BCM_MTU_MAX_LEN 1522 |
| #define BCM_MAX_RX_BUF 64 |
| #define BCM_MAX_TX_BUF 16 |
| |
| // number of MAC addresses in NIC |
| #define BCM_NUM_MAC_ADDR 4 |
| #define BCM_NUM_MAC5704_ADDR 12 |
| // offset of mac address field(s) in bcm register space |
| #define MAC5704_ADDR_OFFS ( (uint16_t) 0x0530 ) |
| |
| // offset of NIC memory start address from base address |
| #define BCM_MEMORY_OFFS ( (uint64_t) 0x8000 ) |
| |
| // offset of statistics block in NIC memory |
| #define BCM_STATISTIC_OFFS ( (uint64_t) 0x0300 ) |
| // size of statistic block in NIC memory |
| #define BCM_STATISTIC_SIZE 0x800 |
| |
| // offsets of NIC rx/tx rings in NIC memory |
| #define BCM_NIC_TX_OFFS ( (uint16_t) 0x4000 ) |
| #define BCM_NIC_RX_OFFS ( (uint16_t) 0x6000 ) |
| #define BCM_NIC_TX_SIZE ( (uint16_t) ( ( BCM_TX_RING_SIZE * BCM_RCB_SIZE_u16 ) / 4 ) ) |
| |
| // device mailboxes |
| #define BCM_FW_MBX ( (uint16_t) 0x0b50 ) |
| #define BCM_FW_MBX_CMD ( (uint16_t) 0x0b78 ) |
| #define BCM_FW_MBX_LEN ( (uint16_t) 0x0b7c ) |
| #define BCM_FW_MBX_DATA ( (uint16_t) 0x0b80 ) |
| #define BCM_NICDRV_STATE_MBX ( (uint16_t) 0x0c04 ) |
| |
| // device mailbox commands |
| #define BCM_NICDRV_ALIVE ( (uint32_t) 0x00000001 ) |
| #define BCM_NICDRV_PAUSE_FW ( (uint32_t) 0x00000002 ) |
| |
| // device values |
| #define BCM_MAGIC_NUMBER ( (uint32_t) 0x4b657654 ) |
| |
| // device states |
| #define NIC_FWDRV_STATE_START ( (uint32_t) 0x00000001 ) |
| #define NIC_FWDRV_STATE_START_DONE ( (uint32_t) 0x80000001 ) |
| #define NIC_FWDRV_STATE_UNLOAD ( (uint32_t) 0x00000002 ) |
| #define NIC_FWDRV_STATE_UNLOAD_DONE ( (uint32_t) 0x80000002 ) |
| #define NIC_FWDRV_STATE_SUSPEND ( (uint32_t) 0x00000004 ) |
| |
| // timer prescaler value |
| #define BCM_TMR_PRESCALE ( (uint32_t) 0x41 ) |
| |
| // offset of transmit rcb's in NIC memory |
| #define BCM_TX_RCB_OFFS ( (uint16_t) 0x0100 ) |
| // offset of receive return rcb's in NIC memory |
| #define BCM_RXRET_RCB_OFFS ( (uint16_t) 0x0200 ) |
| |
| // register offsets for ring indices |
| #define TX_PROD_IND ( (uint16_t) 0x0304 ) |
| #define TX_CONS_IND ( (uint16_t) 0x3cc0 ) |
| #define RXPROD_PROD_IND ( (uint16_t) 0x026c ) |
| #define RXPROD_CONS_IND ( (uint16_t) 0x3c54 ) |
| #define RXRET_PROD_IND ( (uint16_t) 0x3c80 ) |
| #define RXRET_CONS_IND ( (uint16_t) 0x0284 ) |
| // NIC producer index only needed for initialization |
| #define TX_NIC_PROD_IND ( (uint16_t) 0x0384 ) |
| |
| /* |
| * predefined register values used during initialization |
| * may be adapted by user |
| */ |
| #define DMA_RW_CTRL_VAL_5714 ( (uint32_t) 0x76144000 ) |
| #define DMA_RW_CTRL_VAL ( (uint32_t) 0x760F0000 ) |
| #define TX_MAC_LEN_VAL ( (uint32_t) 0x00002620 ) |
| |
| #define RX_LST_PLC_CFG_VAL ( (uint32_t) 0x00000109 ) |
| #define RX_LST_PLC_STAT_EN_VAL ( (uint32_t) 0x007e000f ) |
| #define NVM_ADDR_MSK ( (uint32_t) 0x000fffff ) |
| |
| // Number of Receive Rules /w or /wo SOL enabled |
| #define RX_RULE_CFG_VAL ( (uint32_t) 0x00000008 ) |
| #define NUM_RX_RULE ( (uint32_t) 16 ) |
| #define NUM_RX_RULE_ASF ( (uint32_t) ( NUM_RX_RULE - 4 ) ) |
| |
| // RCB register offsets |
| #define BCM_RXPROD_RCB_JUM ( (uint16_t) 0x2440 ) |
| #define BCM_RXPROD_RCB_STD ( (uint16_t) 0x2450 ) |
| #define BCM_RXPROD_RCB_MIN ( (uint16_t) 0x2460 ) |
| |
| // macros needed for new addressing method |
| #define BCM_RCB_HOSTADDR_HI_u16( rcb ) ( (uint16_t) rcb + 0x00 ) |
| #define BCM_RCB_HOSTADDR_LOW_u16( rcb ) ( (uint16_t) rcb + 0x04 ) |
| #define BCM_RCB_LENFLAG_u16( rcb ) ( (uint16_t) rcb + 0x08 ) |
| #define BCM_RCB_NICADDR_u16( rcb ) ( (uint16_t) rcb + 0x0c ) |
| #define BCM_RCB_SIZE_u16 ( (uint16_t) 0x0010 ) |
| |
| // RCB flags |
| #define RCB_FLAG_RING_DISABLED BIT32( 1 ) |
| |
| // BCM device ID masks |
| #define BCM_DEV_5714 ( (uint64_t) 0x1 ) |
| #define BCM_DEV_5704 ( (uint64_t) 0x2 ) |
| #define BCM_DEV_5703 ( (uint64_t) 0x4 ) |
| #define BCM_DEV_SERDES ( (uint64_t) 0x80000000 ) |
| #define BCM_DEV_COPPER ( (uint64_t) 0x40000000 ) |
| |
| #define IS_5714 ( ( bcm_device_u64 & BCM_DEV_5714 ) != 0 ) |
| #define IS_5704 ( ( bcm_device_u64 & BCM_DEV_5704 ) != 0 ) |
| #define IS_5703 ( ( bcm_device_u64 & BCM_DEV_5703 ) != 0 ) |
| #define IS_SERDES ( ( bcm_device_u64 & BCM_DEV_SERDES ) != 0 ) |
| #define IS_COPPER_PHY ( ( bcm_device_u64 & BCM_DEV_COPPER ) != 0 ) |
| |
| #define BUFFERED_FLASH_PAGE_POS 9 |
| #define BUFFERED_FLASH_BYTE_ADDR_MASK ((<<BUFFERED_FLASH_PAGE_POS) - 1) |
| #define BUFFERED_FLASH_PAGE_SIZE 264 |
| #define BUFFERED_FLASH_PHY_SIZE 512 |
| #define MANUFACTURING_INFO_SIZE 140 |
| #define CRC32_POLYNOMIAL 0xEDB88320 |
| |
| /* |
| * local types |
| ****************************************************************************** |
| */ |
| typedef struct { |
| uint32_t m_dev_u32; |
| uint64_t m_devmsk_u64; |
| } bcm_dev_t; |
| |
| /* |
| * BCM common data structures |
| * BCM57xx Programmer's Guide: Section 5 |
| */ |
| |
| /* |
| * 64bit host address in a way the NIC is able to understand it |
| */ |
| typedef struct { |
| uint32_t m_hi_u32; |
| uint32_t m_lo_u32; |
| } bcm_addr64_t; |
| /* |
| * ring control block |
| */ |
| typedef struct { |
| bcm_addr64_t m_hostaddr_st; |
| uint32_t m_lenflags_u32; // upper 16b: len, lower 16b: flags |
| uint32_t m_nicaddr_u32; |
| } bcm_rcb_t; |
| |
| /* |
| * tx buffer descriptor |
| */ |
| typedef struct { |
| bcm_addr64_t m_hostaddr_st; |
| uint32_t m_lenflags_u32; // upper 16b: len, lower 16b: flags |
| uint32_t m_VLANtag_u32; // lower 16b: vtag |
| } bcm_txbd_t; |
| |
| /* |
| * rx buffer descriptor |
| */ |
| typedef struct { |
| bcm_addr64_t m_hostaddr_st; |
| uint32_t m_idxlen_u32; // upper 16b: idx, lower 16b: len |
| uint32_t m_typeflags_u32; // upper 16b: type, lower 16b: flags |
| uint32_t m_chksum_u32; // upper 16b: ip, lower 16b: tcp/udp |
| uint32_t m_errvlan_u32; // upper 16b: err, lower 16b: vlan tag |
| uint32_t m_reserved_u32; |
| uint32_t m_opaque_u32; |
| } bcm_rxbd_t; |
| |
| /* |
| * bcm status block |
| * NOTE: in fact the status block is not used and configured |
| * so that it is not updated by the NIC. Still it has to be |
| * set up so the NIC is satisfied |
| */ |
| typedef struct { |
| uint32_t m_st_word_u32; |
| uint32_t m_st_tag_u32; |
| uint16_t m_rxprod_cons_u16; |
| uint16_t m_unused_u16; |
| uint32_t m_unused_u32; |
| uint16_t m_tx_cons_u16; |
| uint16_t m_rxret_prod_u16; |
| } bcm_status_t; |
| |
| /* |
| * local constants |
| ****************************************************************************** |
| */ |
| static const bcm_dev_t bcm_dev[] = { |
| { 0x166b, BCM_DEV_5714 }, |
| { 0x1668, BCM_DEV_5714 }, |
| { 0x1669, BCM_DEV_5714 }, |
| { 0x166a, BCM_DEV_5714 }, |
| { 0x1648, BCM_DEV_5704 }, |
| { 0x1649, BCM_DEV_5704 | BCM_DEV_SERDES }, |
| { 0x16a8, BCM_DEV_5704 | BCM_DEV_SERDES }, |
| { 0x16a7, BCM_DEV_5703 | BCM_DEV_SERDES }, |
| { 0x16c7, BCM_DEV_5703 | BCM_DEV_SERDES }, |
| { 0 , 0 } |
| }; |
| |
| /* |
| * local variables |
| ****************************************************************************** |
| */ |
| static uint64_t bcm_device_u64; |
| static uint32_t bcm_rxret_ring_sz; |
| static uint64_t bcm_baseaddr_u64; |
| static uint64_t bcm_memaddr_u64; |
| |
| /* |
| * rings & their buffers |
| */ |
| // the rings made of buffer descriptors |
| static bcm_txbd_t bcm_tx_ring[BCM_TX_RING_SIZE]; |
| static bcm_rxbd_t bcm_rxprod_ring[BCM_RXPROD_RING_SIZE]; |
| static bcm_rxbd_t bcm_rxret_ring[BCM_RXRET_RING_SIZE*2]; |
| |
| // the buffers used in the rings |
| static uint8_t bcm_tx_buffer_pu08[BCM_MAX_TX_BUF][BCM_BUF_SIZE]; |
| static uint8_t bcm_rx_buffer_pu08[BCM_MAX_RX_BUF][BCM_BUF_SIZE]; |
| |
| // tx ring index of first/last bd |
| static uint32_t bcm_tx_start_u32; |
| static uint32_t bcm_tx_stop_u32; |
| static uint32_t bcm_tx_bufavail_u32; |
| |
| /* |
| * status block |
| */ |
| static bcm_status_t bcm_status; |
| |
| /* |
| * implementation |
| ****************************************************************************** |
| */ |
| |
| |
| /* |
| * global functions |
| ****************************************************************************** |
| */ |
| |
| |
| /* |
| * local helper functions |
| ****************************************************************************** |
| */ |
| #if 0 |
| static char * |
| memcpy( char *dest, const char *src, size_t n ) |
| { |
| char *ret = dest; |
| while( n-- ) { |
| *dest++ = *src++; |
| } |
| |
| return( ret ); |
| } |
| #endif |
| |
| static char * |
| memset_ci( char *dest, int c, size_t n ) |
| { |
| char *ret = dest; |
| |
| while( n-- ) { |
| wr08( dest, c ); |
| dest++; |
| } |
| |
| return( ret ); |
| } |
| |
| #if 0 |
| static char * |
| memset( char *dest, int c, size_t n ) |
| { |
| char *ret = dest; |
| while( n-- ) { |
| *dest++ = (char) c; |
| } |
| |
| return( ret ); |
| } |
| #endif |
| |
| static uint32_t |
| bcm_nvram_logical_to_physical_address(uint32_t address) |
| { |
| uint32_t page_no = address / BUFFERED_FLASH_PAGE_SIZE; |
| uint32_t page_addr = address % BUFFERED_FLASH_PAGE_SIZE; |
| |
| return (page_no << BUFFERED_FLASH_PAGE_POS) + page_addr; |
| } |
| |
| /* |
| * read/write functions to access NIC registers & memory |
| * NOTE: all functions are executed with cache inhibitation (dead slow :-) ) |
| */ |
| static uint32_t |
| bcm_read_mem32( uint16_t f_offs_u16 ) |
| { // caution: shall only be used after initialization! |
| return rd32( bcm_memaddr_u64 + (uint64_t) f_offs_u16 ); |
| } |
| |
| /* not used so far |
| static uint16_t |
| bcm_read_mem16( uint16_t f_offs_u16 ) |
| { // caution: shall only be used after initialization! |
| return rd16( bcm_memaddr_u64 + (uint64_t) f_offs_u16 ); |
| }*/ |
| /* not used so far |
| static uint8_t |
| bcm_read_mem08( uint16_t f_offs_u16 ) |
| { // caution: shall only be used after initialization! |
| return rd08( bcm_memaddr_u64 + (uint64_t) f_offs_u16 ); |
| }*/ |
| |
| static uint32_t |
| bcm_read_reg32_indirect( uint16_t f_offs_u16 ) |
| { // caution: shall only be used after initialization! |
| SLOF_pci_config_write32(REG_BASE_ADDR_REG, f_offs_u16); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| REG_BASE_ADDR_REG, |
| f_offs_u16 );*/ |
| return bswap_32(SLOF_pci_config_read32(REG_DATA_REG)); |
| /*return (uint32_t) bswap_32( snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| REG_DATA_REG ) ) ;*/ |
| } |
| |
| static uint32_t |
| bcm_read_reg32( uint16_t f_offs_u16 ) |
| { // caution: shall only be used after initialization! |
| if(f_offs_u16 >= 0x200 && f_offs_u16 <0x400) |
| return bcm_read_reg32_indirect( f_offs_u16 + 0x5600 ); |
| return rd32( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); |
| } |
| |
| static uint16_t |
| bcm_read_reg16( uint16_t f_offs_u16 ) |
| { // caution: shall only be used after initialization! |
| return rd16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); |
| } |
| /* not used so far |
| static uint8_t |
| bcm_read_reg08( uint16_t f_offs_u16 ) |
| { // caution: shall only be used after initialization! |
| return rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); |
| }*/ |
| |
| static void |
| bcm_write_mem32_indirect( uint16_t f_offs_u16, uint32_t f_val_u32 ) |
| { // caution: shall only be used after initialization! |
| SLOF_pci_config_write32(MEM_BASE_ADDR_REG, f_offs_u16); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| MEM_BASE_ADDR_REG, |
| f_offs_u16 );*/ |
| SLOF_pci_config_write32(MEM_DATA_REG, bswap_32(f_val_u32)); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| MEM_DATA_REG, |
| bswap_32 ( f_val_u32 ) );*/ |
| } |
| |
| static void |
| bcm_write_mem32( uint16_t f_offs_u16, uint32_t f_val_u32 ) |
| { // caution: shall only be used after initialization! |
| if(f_offs_u16 >= BCM_RXRET_RCB_OFFS && |
| f_offs_u16 < BCM_RXRET_RCB_OFFS + (BCM_MAX_RXRET_RING*BCM_RCB_SIZE_u16)) |
| bcm_write_mem32_indirect( f_offs_u16, f_val_u32 ); |
| else if(f_offs_u16 >= BCM_TX_RCB_OFFS && |
| f_offs_u16 < BCM_TX_RCB_OFFS + (BCM_MAX_TX_RING*BCM_RCB_SIZE_u16)) |
| bcm_write_mem32_indirect( f_offs_u16, f_val_u32 ); |
| else |
| wr32( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u32 ); |
| } |
| /* not used so far |
| static void |
| bcm_write_mem16( uint16_t f_offs_u16, uint16_t f_val_u16 ) |
| { // caution: shall only be used after initialization! |
| wr16( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u16 ); |
| }*/ |
| /* not used so far |
| static void |
| bcm_write_mem08( uint16_t f_offs_u16, uint8_t f_val_u08 ) |
| { // caution: shall only be used after initialization! |
| wr08( bcm_memaddr_u64 + (uint64_t) f_offs_u16, f_val_u08 ); |
| }*/ |
| |
| static void |
| bcm_write_reg32_indirect( uint16_t f_offs_u16, uint32_t f_val_u32 ) |
| { // caution: shall only be used after initialization! |
| SLOF_pci_config_write32(REG_BASE_ADDR_REG, f_offs_u16); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| REG_BASE_ADDR_REG, |
| f_offs_u16 );*/ |
| SLOF_pci_config_write32(REG_DATA_REG, bswap_32(f_val_u32)); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| REG_DATA_REG, |
| bswap_32 ( f_val_u32 ) );*/ |
| } |
| |
| static void |
| bcm_write_reg32( uint16_t f_offs_u16, uint32_t f_val_u32 ) |
| { // caution: shall only be used after initialization! |
| if(f_offs_u16 >= 0x200 && f_offs_u16 <0x400) |
| bcm_write_reg32_indirect( f_offs_u16 + 0x5600, f_val_u32 ); |
| else |
| wr32( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u32 ); |
| } |
| |
| static void |
| bcm_write_reg16( uint16_t f_offs_u16, uint16_t f_val_u16 ) |
| { // caution: shall only be used after initialization! |
| wr16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u16 ); |
| } |
| /* not used so far |
| static void |
| bcm_write_reg08( uint16_t f_offs_u16, uint8_t f_val_u08 ) |
| { // caution: shall only be used after initialization! |
| wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, f_val_u08 ); |
| }*/ |
| |
| static void |
| bcm_setb_reg32( uint16_t f_offs_u16, uint32_t f_mask_u32 ) |
| { |
| uint32_t v; |
| |
| v = bcm_read_reg32( f_offs_u16 ); |
| v |= f_mask_u32; |
| bcm_write_reg32( f_offs_u16, v ); |
| } |
| /* not used so far |
| static void |
| bcm_setb_reg16( uint16_t f_offs_u16, uint16_t f_mask_u16 ) |
| { |
| uint16_t v; |
| v = rd16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); |
| v |= f_mask_u16; |
| wr16( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v ); |
| }*/ |
| /* not used so far |
| static void |
| bcm_setb_reg08( uint16_t f_offs_u16, uint8_t f_mask_u08 ) |
| { |
| uint8_t v; |
| v = rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); |
| v |= f_mask_u08; |
| wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v ); |
| }*/ |
| |
| static void |
| bcm_clrb_reg32( uint16_t f_offs_u16, uint32_t f_mask_u32 ) |
| { |
| uint32_t v; |
| |
| v = bcm_read_reg32( f_offs_u16 ); |
| v &= ~f_mask_u32; |
| bcm_write_reg32( f_offs_u16, v ); |
| } |
| |
| static void |
| bcm_clrb_reg16( uint16_t f_offs_u16, uint16_t f_mask_u16 ) |
| { |
| uint16_t v; |
| |
| v = bcm_read_reg16( f_offs_u16 ); |
| v &= ~f_mask_u16; |
| bcm_write_reg16( f_offs_u16, v ); |
| } |
| /* not used so far |
| static void |
| bcm_clrb_reg08( uint16_t f_offs_u16, uint8_t f_mask_u08 ) |
| { |
| uint8_t v; |
| v = rd08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16 ); |
| v &= ~f_mask_u32; |
| wr08( bcm_baseaddr_u64 + (uint64_t) f_offs_u16, v ); |
| }*/ |
| |
| static void |
| bcm_clr_wait_bit32( uint16_t r, uint32_t b ) |
| { |
| uint32_t i; |
| |
| bcm_clrb_reg32( r, b ); |
| |
| i = 1000; |
| while( --i ) { |
| |
| if( ( bcm_read_reg32( r ) & b ) == 0 ) { |
| break; |
| } |
| |
| SLOF_usleep( 10 ); |
| } |
| #ifdef BCM_DEBUG |
| if( ( bcm_read_reg32( r ) & b ) != 0 ) { |
| printf( "bcm57xx: bcm_clear_wait_bit32 failed (0x%04X)!\n", r ); |
| } |
| #endif |
| } |
| |
| /* |
| * (g)mii bus access |
| */ |
| #if 0 |
| // not used so far |
| static int32_t |
| bcm_mii_write16( uint32_t f_reg_u32, uint16_t f_value_u16 ) |
| { |
| static const uint32_t WR_VAL = ( ( ((uint32_t) 0x1) << 21 ) | BIT32( 29 ) | BIT32( 26 ) ); |
| int32_t l_autopoll_i32 = 0; |
| uint32_t l_wrval_u32; |
| uint32_t i; |
| |
| /* |
| * only 0x00-0x1f are valid registers |
| */ |
| if( f_reg_u32 > (uint32_t) 0x1f ) { |
| return -1; |
| } |
| |
| /* |
| * disable auto polling if enabled |
| */ |
| if( ( bcm_read_reg32( MI_MODE_R ) & BIT32( 4 ) ) != 0 ) { |
| l_autopoll_i32 = (int32_t) !0; |
| bcm_clrb_reg32( MI_MODE_R, BIT32( 4 ) ); |
| SLOF_usleep( 40 ); |
| } |
| |
| /* |
| * construct & write mi com register value |
| */ |
| l_wrval_u32 = ( WR_VAL | ( f_reg_u32 << 16 ) | (uint32_t) f_value_u16 ); |
| bcm_write_reg32( MI_COM_R, l_wrval_u32 ); |
| |
| /* |
| * wait for transaction to complete |
| */ |
| i = 25; |
| while( ( --i ) && |
| ( ( bcm_read_reg32( MI_COM_R ) & BIT32( 29 ) ) != 0 ) ) { |
| SLOF_usleep( 10 ); |
| } |
| |
| /* |
| * re-enable auto polling if necessary |
| */ |
| if( l_autopoll_i32 ) { |
| bcm_setb_reg32( MI_MODE_R, BIT32( 4 ) ); |
| } |
| |
| // return on error |
| if( i == 0 ) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int32_t |
| bcm_mii_read16( uint32_t f_reg_u32, uint16_t *f_value_pu16 ) |
| { |
| static const uint32_t RD_VAL = ( ( ((uint32_t) 0x1) << 21 ) | BIT32( 29 ) | BIT32( 27 ) ); |
| int32_t l_autopoll_i32 = 0; |
| uint32_t l_rdval_u32; |
| uint32_t i; |
| uint16_t first_not_busy; |
| |
| /* |
| * only 0x00-0x1f are valid registers |
| */ |
| if( f_reg_u32 > (uint32_t) 0x1f ) { |
| return -1; |
| } |
| |
| /* |
| * disable auto polling if enabled |
| */ |
| if( ( bcm_read_reg32( MI_MODE_R ) & BIT32( 4 ) ) != 0 ) { |
| l_autopoll_i32 = ( int32_t ) !0; |
| bcm_clrb_reg32( MI_MODE_R, BIT32( 4 ) ); |
| SLOF_usleep( 40 ); |
| } |
| |
| /* |
| * construct & write mi com register value |
| */ |
| l_rdval_u32 = ( RD_VAL | ( f_reg_u32 << 16 ) ); |
| bcm_write_reg32( MI_COM_R, l_rdval_u32 ); |
| |
| /* |
| * wait for transaction to complete |
| * ERRATA workaround: must read two "not busy" states to indicate transaction complete |
| */ |
| i = 25; |
| first_not_busy = 0; |
| l_rdval_u32 = bcm_read_reg32( MI_COM_R ); |
| while( ( --i ) && |
| ( (first_not_busy == 0) || ( ( l_rdval_u32 & BIT32( 29 ) ) != 0 ) ) ) { |
| /* Is this the first clear BUSY state? */ |
| if ( ( l_rdval_u32 & BIT32( 29 ) ) == 0 ) |
| first_not_busy++; |
| SLOF_usleep( 10 ); |
| l_rdval_u32 = bcm_read_reg32( MI_COM_R ); |
| } |
| |
| /* |
| * re-enable autopolling if necessary |
| */ |
| if( l_autopoll_i32 ) { |
| bcm_setb_reg32( MI_MODE_R, BIT32( 4 ) ); |
| } |
| |
| /* |
| * return on read transaction error |
| * (check read failed bit) |
| */ |
| if( ( i == 0 ) || |
| ( ( l_rdval_u32 & BIT32( 28 ) ) != 0 ) ) { |
| return -1; |
| } |
| |
| /* |
| * return read value |
| */ |
| *f_value_pu16 = (uint16_t) ( l_rdval_u32 & (uint32_t) 0xffff ); |
| |
| return 0; |
| } |
| |
| /* |
| * ht2000 dump (not complete) |
| */ |
| #if 0 |
| static void |
| bcm_dump( void ) |
| { |
| uint32_t i, j; |
| |
| printf( "*** DUMP ***********************************************************************\n\n" ); |
| |
| printf( "* PCI Configuration Registers:\n" ); |
| for( i = 0, j = 0; i < 0x40; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Private PCI Configuration Registers:\n" ); |
| for( i = 0x68, j = 0; i < 0x88; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* VPD Config:\n" ); |
| printf( "%04X: %08X \n", 0x94, bcm_read_reg32( 0x94 ) ); |
| |
| printf( "\n* Dual MAC Control Registers:\n" ); |
| for( i = 0xb8, j = 0; i < 0xd0; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Ethernet MAC Control Registers:\n" ); |
| for( i = 0x400, j = 0; i < 0x590; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Send Data Initiator Control:\n" ); |
| for( i = 0xc00, j = 0; i < 0xc10; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Send Data Completion Control:\n" ); |
| printf( "%04X: %08X ", 0x1000, bcm_read_reg32( 0x1000 ) ); |
| printf( "%04X: %08X \n", 0x1008, bcm_read_reg32( 0x1008 ) ); |
| |
| printf( "\n* Send BD Ring Selector Control:\n" ); |
| printf( "%04X: %08X ", 0x1400, bcm_read_reg32( 0x1400 ) ); |
| printf( "%04X: %08X ", 0x1404, bcm_read_reg32( 0x1404 ) ); |
| printf( "%04X: %08X \n", 0x1408, bcm_read_reg32( 0x1408 ) ); |
| |
| printf( "\n* Send BD Initiator Control:\n" ); |
| printf( "%04X: %08X ", 0x1800, bcm_read_reg32( 0x1800 ) ); |
| printf( "%04X: %08X \n", 0x1804, bcm_read_reg32( 0x1804 ) ); |
| |
| printf( "\n* Send BD Completion Control:\n" ); |
| printf( "%04X: %08X ", 0x1c00, bcm_read_reg32( 0x1c00 ) ); |
| |
| printf( "\n* Receive List Placement Control:\n" ); |
| for( i = 0x2000, j = 0; i < 0x2020; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Receive Data & Receive BD Initiator Control:\n" ); |
| printf( "%04X: %08X ", 0x2400, bcm_read_reg32( 0x2400 ) ); |
| printf( "%04X: %08X \n", 0x2404, bcm_read_reg32( 0x2404 ) ); |
| |
| printf( "\n* Jumbo Receive BD Ring RCB:\n" ); |
| for( i = 0x2440, j = 0; i < 0x2450; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Standard Receive BD Ring RCB:\n" ); |
| for( i = 0x2450, j = 0; i < 0x2460; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Mini Receive BD Ring RCB:\n" ); |
| for( i = 0x2460, j = 0; i < 0x2470; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\nRDI Timer Mode Register:\n" ); |
| printf( "%04X: %08X \n", 0x24f0, bcm_read_reg32( 0x24f0 ) ); |
| |
| printf( "\n* Receive BD Initiator Control:\n" ); |
| for( i = 0x2c00, j = 0; i < 0x2c20; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| |
| printf( "\n* Receive BD Completion Control:\n" ); |
| for( i = 0x3000, j = 0; i < 0x3014; i += 4 ) { |
| |
| printf( "%04X: %08X ", i, bcm_read_reg32( i ) ); |
| |
| if( ( ++j & 0x3 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| } |
| #endif |
| |
| |
| |
| /* |
| * NVRAM access |
| */ |
| |
| static int |
| bcm_nvram_lock( void ) |
| { |
| int i; |
| |
| /* |
| * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON) |
| */ |
| // bcm_setb_reg32( SW_ARB_R, BIT32( 0 ) ); |
| bcm_setb_reg32( SW_ARB_R, BIT32( 1 ) ); |
| |
| i = 2000; |
| while( ( --i ) && |
| // ( bcm_read_reg32( SW_ARB_R ) & BIT32( 8 ) ) == 0 ) { |
| ( bcm_read_reg32( SW_ARB_R ) & BIT32( 9 ) ) == 0 ) { |
| SLOF_msleep( 1 ); |
| } |
| |
| // return on error |
| if( i == 0 ) { |
| #ifdef BCM_DEBUG |
| printf("bcm57xx: failed to lock nvram"); |
| #endif |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| bcm_nvram_unlock( void ) |
| { |
| /* |
| * release NVRam lock (CLR0) |
| */ |
| // bcm_setb_reg32( SW_ARB_R, BIT32( 4 ) ); |
| bcm_setb_reg32( SW_ARB_R, BIT32( 5 ) ); |
| } |
| |
| static void |
| bcm_nvram_init( void ) |
| { |
| /* |
| * enable access to NVRAM registers |
| */ |
| if(IS_5714) { |
| bcm_setb_reg32( NVM_ACC_R, BIT32( 1 ) | BIT32( 0 ) ); |
| } |
| |
| /* |
| * disable bit-bang method 19& disable interface bypass |
| */ |
| bcm_clrb_reg32( NVM_CFG1_R, BIT32( 31 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 14 ) | BIT32( 16 ) ); |
| bcm_setb_reg32( NVM_CFG1_R, BIT32 ( 13 ) | BIT32 ( 17 )); |
| |
| /* |
| * enable Auto SEEPROM Access |
| */ |
| bcm_setb_reg32( MISC_LOCAL_CTRL_R, BIT32 ( 24 ) ); |
| |
| /* |
| * NVRAM write enable |
| */ |
| bcm_setb_reg32( MODE_CTRL_R, BIT32 ( 21 ) ); |
| } |
| |
| static int32_t |
| bcm_nvram_read( uint32_t f_addr_u32, uint32_t *f_val_pu32, uint32_t lock ) |
| { |
| uint32_t i; |
| |
| /* |
| * parameter check |
| */ |
| if( f_addr_u32 > NVM_ADDR_MSK ) { |
| return -1; |
| } |
| |
| /* |
| * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON) |
| */ |
| if( lock && (bcm_nvram_lock() == -1) ) { |
| return -1; |
| } |
| |
| /* |
| * setup address to read |
| */ |
| bcm_write_reg32( NVM_ADDR_R, |
| bcm_nvram_logical_to_physical_address(f_addr_u32) ); |
| // bcm_write_reg32( NVM_ADDR_R, f_addr_u32 ); |
| |
| /* |
| * get the command going |
| */ |
| bcm_write_reg32( NVM_COM_R, BIT32( 8 ) | BIT32( 7 ) | |
| BIT32( 4 ) | BIT32( 3 ) ); |
| |
| /* |
| * wait for command completion |
| */ |
| i = 2000; |
| while( ( --i ) && |
| ( ( bcm_read_reg32( NVM_COM_R ) & BIT32( 3 ) ) == 0 ) ) { |
| SLOF_msleep( 1 ); |
| } |
| |
| /* |
| * read back data if no error |
| */ |
| if( i != 0 ) { |
| /* |
| * read back data |
| */ |
| *f_val_pu32 = bcm_read_reg32( NVM_READ_R ); |
| } |
| |
| if(lock) |
| bcm_nvram_unlock(); |
| |
| // error |
| if( i == 0 ) { |
| #ifdef BCM_DEBUG |
| printf("bcm57xx: reading from NVRAM failed\n"); |
| #endif |
| return -1; |
| } |
| |
| // success |
| return 0; |
| } |
| |
| static int32_t |
| bcm_nvram_write( uint32_t f_addr_u32, uint32_t f_value_u32, uint32_t lock ) |
| { |
| uint32_t i; |
| |
| /* |
| * parameter check |
| */ |
| if( f_addr_u32 > NVM_ADDR_MSK ) { |
| return -1; |
| } |
| |
| /* |
| * Acquire NVRam lock (REQ0) & wait for arbitration won (ARB0_WON) |
| */ |
| if( lock && (bcm_nvram_lock() == -1) ) { |
| return -1; |
| } |
| |
| /* |
| * setup address to write |
| */ |
| bcm_write_reg32( NVM_ADDR_R, bcm_nvram_logical_to_physical_address( f_addr_u32 ) ); |
| |
| /* |
| * setup write data |
| */ |
| bcm_write_reg32( NVM_WRITE_R, f_value_u32 ); |
| |
| /* |
| * get the command going |
| */ |
| bcm_write_reg32( NVM_COM_R, BIT32( 8 ) | BIT32( 7 ) | |
| BIT32( 5 ) | BIT32( 4 ) | BIT32( 3 ) ); |
| |
| /* |
| * wait for command completion |
| */ |
| i = 2000; |
| while( ( --i ) && |
| ( ( bcm_read_reg32( NVM_COM_R ) & BIT32( 3 ) ) == 0 ) ) { |
| SLOF_msleep( 1 ); |
| } |
| |
| /* |
| * release NVRam lock (CLR0) |
| */ |
| if(lock) |
| bcm_nvram_unlock(); |
| |
| // error |
| if( i == 0 ) { |
| #ifdef BCM_DEBUG |
| printf("bcm57xx: writing to NVRAM failed\n"); |
| #endif |
| return -1; |
| } |
| |
| // success |
| return 0; |
| } |
| |
| /* |
| * PHY initialization |
| */ |
| static int32_t |
| bcm_mii_phy_init( void ) |
| { |
| static const uint32_t PHY_STAT_R = (uint32_t) 0x01; |
| static const uint32_t AUX_STAT_R = (uint32_t) 0x19; |
| static const uint32_t MODE_GMII = BIT32( 3 ); |
| static const uint32_t MODE_MII = BIT32( 2 ); |
| static const uint32_t NEG_POLARITY = BIT32( 10 ); |
| static const uint32_t MII_MSK = ( MODE_GMII | MODE_MII ); |
| static const uint16_t GIGA_ETH = ( BIT16( 10 ) | BIT16( 9 ) ); |
| int32_t i; |
| uint16_t v; |
| |
| /* |
| * enable MDI communication |
| */ |
| bcm_write_reg32( MDI_CTRL_R, (uint32_t) 0x0 ); |
| |
| /* |
| * check link up |
| */ |
| i = 2500; |
| do { |
| SLOF_msleep( 1 ); |
| // register needs to be read twice! |
| bcm_mii_read16( PHY_STAT_R, &v ); |
| bcm_mii_read16( PHY_STAT_R, &v ); |
| } while( ( --i ) && |
| ( ( v & BIT16( 2 ) ) == 0 ) ); |
| |
| if( i == 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: link is down\n" ); |
| #endif |
| return -1; |
| } |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: link is up\n" ); |
| #endif |
| if( !IS_COPPER_PHY ) { |
| return 0; |
| } |
| |
| /* |
| * setup GMII or MII interface |
| */ |
| i = bcm_read_reg32( ETH_MAC_MODE_R ); |
| /* |
| * read status register twice, since the first |
| * read fails once between here and the moon... |
| */ |
| bcm_mii_read16( AUX_STAT_R, &v ); |
| bcm_mii_read16( AUX_STAT_R, &v ); |
| |
| if( ( v & GIGA_ETH ) == GIGA_ETH ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: running PHY in GMII mode (1000BaseT)\n" ); |
| #endif |
| // GMII device |
| if( ( i & MII_MSK ) != MODE_GMII ) { |
| i &= ~MODE_MII; |
| i |= MODE_GMII; |
| } |
| |
| } else { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: running PHY in MII mode (10/100BaseT)\n" ); |
| #endif |
| // MII device |
| if( ( i & MII_MSK ) != MODE_MII ) { |
| i &= ~MODE_GMII; |
| i |= MODE_MII; |
| } |
| |
| } |
| |
| if( IS_5704 && !IS_SERDES ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: set the link ready signal for 5704C to negative polarity\n" ); |
| #endif |
| i |= NEG_POLARITY; // set the link ready signal for 5704C to negative polarity |
| } |
| |
| bcm_write_reg32( ETH_MAC_MODE_R, i ); |
| |
| return 0; |
| } |
| |
| static int32_t |
| bcm_tbi_phy_init( void ) |
| { |
| int32_t i; |
| #if 0 |
| /* |
| * set TBI mode full duplex |
| */ |
| bcm_clrb_reg32( ETH_MAC_MODE_R, BIT32( 1 ) ); |
| bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 2 ) | BIT32( 3 ) ); |
| |
| /* |
| * enable MDI communication |
| */ |
| bcm_write_reg32( MDI_CTRL_R, (uint32_t) 0x0 ); |
| |
| /* Disable link change interrupt. */ |
| bcm_write_reg32( ETH_MAC_EVT_EN_R, 0 ); |
| |
| /* |
| * set link polarity |
| */ |
| bcm_clrb_reg32( ETH_MAC_MODE_R, BIT32( 10 ) ); |
| |
| /* |
| * wait for sync/config changes |
| */ |
| for( i = 0; i < 100; i++ ) { |
| bcm_write_reg32( ETH_MAC_STAT_R, |
| BIT32( 3 ) | BIT32( 4 ) ); |
| |
| SLOF_usleep( 20 ); |
| |
| if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & |
| ( BIT32( 3 ) | BIT32( 4 ) ) ) == 0 ) { |
| break; |
| } |
| |
| } |
| #endif |
| /* |
| * wait for sync to come up |
| */ |
| for( i = 0; i < 100; i++ ) { |
| |
| if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) != 0 ) { |
| break; |
| } |
| |
| SLOF_usleep( 20 ); |
| } |
| |
| if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) == 0) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: link is down\n" ); |
| #endif |
| return -1; |
| } |
| #if 0 |
| /* |
| * clear all attentions |
| */ |
| bcm_write_reg32( ETH_MAC_STAT_R, (uint32_t) ~0 ); |
| #endif |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: link is up\n" ); |
| #endif |
| return 0; |
| } |
| |
| static int32_t |
| bcm_phy_init( void ) |
| { |
| static const uint16_t SRAM_HW_CFG = (uint16_t) 0x0b58; |
| uint32_t l_val_u32; |
| int32_t l_ret_i32 = 0; |
| |
| /* |
| * get HW configuration from SRAM |
| */ |
| l_val_u32 = bcm_read_mem32( SRAM_HW_CFG ); |
| l_val_u32 &= ( BIT32( 5 ) | BIT32( 4 ) ); |
| |
| switch( l_val_u32 ) { |
| case 0x10: { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: copper PHY detected\n" ); |
| #endif |
| |
| bcm_device_u64 |= BCM_DEV_COPPER; |
| l_ret_i32 = bcm_mii_phy_init(); |
| } break; |
| |
| case 0x20: { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: fiber PHY detected\n" ); |
| #endif |
| |
| if( !IS_SERDES ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: running PHY in gmii/mii mode\n" ); |
| #endif |
| l_ret_i32 = bcm_mii_phy_init(); |
| } else { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: running PHY in tbi mode\n" ); |
| #endif |
| l_ret_i32 = bcm_tbi_phy_init(); |
| } |
| |
| } break; |
| |
| default: { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: unknown PHY type detected, terminating\n" ); |
| #endif |
| l_ret_i32 = -1; |
| } |
| |
| } |
| |
| return l_ret_i32; |
| } |
| |
| /* |
| * ring initialization |
| */ |
| static void |
| bcm_init_rxprod_ring( void ) |
| { |
| uint32_t v; |
| uint32_t i; |
| |
| /* |
| * clear out the whole rx prod ring for sanity |
| */ |
| memset( (void *) &bcm_rxprod_ring, |
| 0, |
| BCM_RXPROD_RING_SIZE * sizeof( bcm_rxbd_t ) ); |
| mb(); |
| |
| /* |
| * assign buffers & indices to the ring members |
| */ |
| for( i = 0; i < BCM_MAX_RX_BUF; i++ ) { |
| bcm_rxprod_ring[i].m_hostaddr_st.m_hi_u32 = |
| (uint32_t) ( (uint64_t) &bcm_rx_buffer_pu08[i] >> 32 ); |
| bcm_rxprod_ring[i].m_hostaddr_st.m_lo_u32 = |
| (uint32_t) ( (uint64_t) &bcm_rx_buffer_pu08[i] & |
| (uint64_t) 0xffffffff ); |
| bcm_rxprod_ring[i].m_idxlen_u32 = ( i << 16 ); |
| bcm_rxprod_ring[i].m_idxlen_u32 += BCM_BUF_SIZE; |
| } |
| |
| /* |
| * clear rcb registers & disable rings |
| * NOTE: mini & jumbo rings are not supported, |
| * still rcb's are cleaned out for sanity |
| */ |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_JUM ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_JUM ), 0 ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_JUM ), 0 ); |
| bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_JUM ), 0 ); |
| |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), 0 ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), 0 ); |
| bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), 0 ); |
| |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_MIN ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_MIN ), 0 ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_MIN ), 0 ); |
| bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_MIN ), 0 ); |
| |
| /* |
| * clear rx producer index of std producer ring |
| */ |
| bcm_write_reg32( RXPROD_PROD_IND, 0 ); |
| |
| /* |
| * setup rx standard rcb using recommended NIC addr (hard coded) |
| */ |
| bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), |
| (uint32_t) ( (uint64_t) &bcm_rxprod_ring >> 32 ) ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), |
| (uint32_t) ( (uint64_t) &bcm_rxprod_ring & (uint64_t) 0xffffffff ) ); |
| bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), |
| (uint32_t) BCM_NIC_RX_OFFS ); |
| |
| if( IS_5704 || IS_5703 ) { |
| // 5704: length field = max buffer len |
| v = (uint32_t) BCM_BUF_SIZE << 16; |
| } else { |
| // 5714: length field = number of ring entries |
| v = (uint32_t) BCM_RXPROD_RING_SIZE << 16; |
| } |
| |
| v &= (uint32_t) ~RCB_FLAG_RING_DISABLED; |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), v ); |
| } |
| |
| static void |
| bcm_init_rxret_ring( void ) |
| { |
| uint32_t i; |
| uint16_t v; |
| |
| /* |
| * clear out the whole rx ret ring for sanity |
| */ |
| memset( (void *) &bcm_rxret_ring, |
| 0, |
| 2 * BCM_RXRET_RING_SIZE * sizeof( bcm_rxbd_t ) ); |
| mb(); |
| |
| /* |
| * setup return ring size dependent on installed device |
| */ |
| bcm_rxret_ring_sz = BCM_RXRET_RING_SIZE; |
| if( IS_5704 || IS_5703 ) { |
| bcm_rxret_ring_sz *= 2; |
| } |
| |
| /* |
| * clear rcb memory & disable rings |
| * NOTE: 5714 only supports one return ring, |
| * still all possible rcb's are cleaned out for sanity |
| */ |
| v = BCM_RXRET_RCB_OFFS; |
| for( i = 0; i < BCM_MAX_RXRET_RING; i++ ) { |
| bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); |
| |
| v += BCM_RCB_SIZE_u16; |
| } |
| |
| /* |
| * clear rx consumer index of return ring |
| */ |
| bcm_write_reg32( RXRET_CONS_IND, 0 ); |
| |
| /* |
| * setup rx ret rcb |
| * NOTE: NIC address not aplicable in return rings |
| */ |
| bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXRET_RCB_OFFS ), |
| (uint32_t) ( (uint64_t) &bcm_rxret_ring >> 32 ) ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXRET_RCB_OFFS ), |
| (uint32_t) ( (uint64_t) &bcm_rxret_ring & |
| (uint64_t) 0xffffffff ) ); |
| bcm_write_mem32( BCM_RCB_NICADDR_u16( BCM_RXRET_RCB_OFFS ), 0 ); |
| |
| i = bcm_rxret_ring_sz; |
| i <<= 16; |
| i &= (uint32_t) ~RCB_FLAG_RING_DISABLED; |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXRET_RCB_OFFS ), i ); |
| } |
| |
| static void |
| bcm_init_tx_ring( void ) |
| { |
| uint32_t i; |
| uint16_t v; |
| |
| /* |
| * clear out the whole tx ring for sanity |
| */ |
| memset( (void *) &bcm_tx_ring, |
| 0, |
| BCM_TX_RING_SIZE * sizeof( bcm_txbd_t ) ); |
| mb(); |
| |
| /* |
| * assign buffers to the ring members & setup invariant flags |
| */ |
| for( i = 0; i < BCM_MAX_TX_BUF; i++ ) { |
| bcm_tx_ring[i].m_hostaddr_st.m_hi_u32 = |
| (uint32_t) ( (uint64_t) &bcm_tx_buffer_pu08[i] >> 32 ); |
| bcm_tx_ring[i].m_hostaddr_st.m_lo_u32 = |
| (uint32_t) ( (uint64_t) &bcm_tx_buffer_pu08[i] & |
| (uint64_t) 0xffffffff ); |
| // flags: indicate last packet & coal now |
| // -last packet is always true (only one send packet supported) |
| // -coal now needed to always get the consumed bd's (since |
| // only a few bd's are set up which permanently are recycled) |
| bcm_tx_ring[i].m_lenflags_u32 = ( BIT32( 2 ) | BIT32( 7 ) ); |
| bcm_tx_ring[i].m_VLANtag_u32 = (uint32_t) 0; // not used |
| } |
| |
| /* |
| * clear rcb memory & disable rings |
| * NOTE: 5714 only supports one send ring, |
| * still all possible rcb's are cleaned out for sanity |
| */ |
| v = BCM_TX_RCB_OFFS; |
| for( i = 0; i < BCM_MAX_TX_RING; i++ ) { |
| bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); |
| |
| v += BCM_RCB_SIZE_u16; |
| } |
| |
| /* |
| * clear host/nic producer indices |
| */ |
| bcm_write_reg32( TX_NIC_PROD_IND, 0 ); |
| bcm_write_reg32( TX_PROD_IND, 0 ); |
| |
| /* |
| * setup tx rcb using recommended NIC addr (hard coded) |
| */ |
| bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( BCM_TX_RCB_OFFS ), |
| (uint32_t) ( (uint64_t) &bcm_tx_ring >> 32 ) ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( BCM_TX_RCB_OFFS ), |
| (uint32_t) ( (uint64_t) &bcm_tx_ring & |
| (uint64_t) 0xffffffff ) ); |
| bcm_write_mem32( BCM_RCB_NICADDR_u16( BCM_TX_RCB_OFFS ), |
| (uint32_t) BCM_NIC_TX_OFFS ); |
| |
| if( IS_5704 || IS_5703 ) { |
| // 5704: length field = max buffer len |
| i = (uint32_t) BCM_BUF_SIZE << 16; |
| } else { |
| // 5714: length field = number of ring entries |
| i = (uint32_t) BCM_TX_RING_SIZE << 16; |
| } |
| |
| i &= ( uint32_t ) ~RCB_FLAG_RING_DISABLED; |
| bcm_write_mem32( BCM_RCB_LENFLAG_u16( BCM_TX_RCB_OFFS ), i ); |
| |
| /* |
| * remember the next bd index to be used |
| * & number of available buffers |
| */ |
| bcm_tx_stop_u32 = BCM_MAX_TX_BUF; |
| bcm_tx_bufavail_u32 = BCM_MAX_TX_BUF; |
| } |
| |
| static int32_t |
| bcm_mac_init( uint8_t *f_mac_pu08 ) |
| { |
| static const uint16_t MEM_MAC_LO = (uint16_t) 0x0c18; |
| static const uint16_t MEM_MAC_HI = (uint16_t) 0x0c14; |
| |
| uint32_t NVR_MAC_LO = (uint16_t) 0x80; |
| uint32_t NVR_MAC_HI = (uint16_t) 0x7c; |
| |
| bcm_addr64_t l_mac_st; |
| uint32_t i; |
| uint32_t v; |
| |
| /* |
| * Use MAC address from device tree if possible |
| */ |
| for( i = 0, v = 0; i < 6; i++ ) { |
| v += (uint32_t) f_mac_pu08[i]; |
| } |
| |
| if( v != 0 ) { |
| l_mac_st.m_hi_u32 = ( ( (uint32_t) f_mac_pu08[0]) << 8 ); |
| l_mac_st.m_hi_u32 |= ( ( (uint32_t) f_mac_pu08[1]) << 0 ); |
| l_mac_st.m_lo_u32 = ( ( (uint32_t) f_mac_pu08[2]) << 24 ); |
| l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[3]) << 16 ); |
| l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[4]) << 8 ); |
| l_mac_st.m_lo_u32 |= ( ( (uint32_t) f_mac_pu08[5]) << 0 ); |
| } else { |
| /* |
| * try to read MAC address from MAC mailbox |
| */ |
| l_mac_st.m_hi_u32 = bcm_read_mem32( MEM_MAC_HI ); |
| |
| if( ( l_mac_st.m_hi_u32 >> 16 ) == (uint32_t) 0x484b ) { |
| l_mac_st.m_hi_u32 &= (uint32_t) 0xffff; |
| l_mac_st.m_lo_u32 = bcm_read_mem32( MEM_MAC_LO ); |
| } else { |
| int32_t l_err_i32; |
| |
| /* |
| * otherwise retrieve MAC address from NVRam |
| */ |
| if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) != 0 ) { |
| // secondary MAC is in use, address in NVRAM changes |
| NVR_MAC_LO += 0x50; |
| NVR_MAC_HI += 0x50; |
| } |
| |
| l_err_i32 = bcm_nvram_read( NVR_MAC_LO, &l_mac_st.m_lo_u32, 1 ); |
| l_err_i32 += bcm_nvram_read( NVR_MAC_HI, &l_mac_st.m_hi_u32, 1 ); |
| |
| // return on read error |
| if( l_err_i32 < 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: failed to retrieve MAC address\n" ); |
| #endif |
| return -1; |
| } |
| } |
| } |
| |
| /* |
| * write the mac addr into the NIC's register area |
| */ |
| bcm_write_reg32( MAC_ADDR_OFFS_HI(0), l_mac_st.m_hi_u32 ); |
| bcm_write_reg32( MAC_ADDR_OFFS_LO(0), l_mac_st.m_lo_u32 ); |
| for( i = 1; i < BCM_NUM_MAC_ADDR; i++ ) { |
| bcm_write_reg32( MAC_ADDR_OFFS_HI(i), 0 ); |
| bcm_write_reg32( MAC_ADDR_OFFS_LO(i), 0 ); |
| } |
| |
| /* |
| * WY 26.01.07 |
| * not needed anymore, s.a. |
| if( IS_5704 != 0 ) { |
| |
| v = MAC5704_ADDR_OFFS; |
| for( i = 0; i < BCM_NUM_MAC5704_ADDR; i++ ) { |
| bcm_write_reg32( v, l_mac_st.m_hi_u32 ); |
| v += sizeof( uint32_t ); |
| bcm_write_reg32( v, l_mac_st.m_lo_u32 ); |
| v += sizeof( uint32_t ); |
| } |
| |
| } |
| */ |
| |
| /* |
| * return MAC address as string |
| */ |
| f_mac_pu08[0] = (uint8_t) ( ( l_mac_st.m_hi_u32 >> 8 ) & (uint32_t) 0xff ); |
| f_mac_pu08[1] = (uint8_t) ( ( l_mac_st.m_hi_u32 ) & (uint32_t) 0xff ); |
| f_mac_pu08[2] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 24 ) & (uint32_t) 0xff ); |
| f_mac_pu08[3] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 16 ) & (uint32_t) 0xff ); |
| f_mac_pu08[4] = (uint8_t) ( ( l_mac_st.m_lo_u32 >> 8 ) & (uint32_t) 0xff ); |
| f_mac_pu08[5] = (uint8_t) ( ( l_mac_st.m_lo_u32 ) & (uint32_t) 0xff ); |
| |
| #ifdef BCM_DEBUG |
| do { |
| int32_t i; |
| printf( "bcm57xx: retrieved MAC address " ); |
| |
| for( i = 0; i < 6; i++ ) { |
| printf( "%02X", f_mac_pu08[i] ); |
| |
| if( i != 5 ) { |
| printf( ":" ); |
| } |
| |
| } |
| |
| printf( "\n" ); |
| } while( 0 ); |
| #endif |
| |
| return 0; |
| } |
| |
| |
| /* |
| ****************************************************************************** |
| * ASF Firmware |
| ****************************************************************************** |
| */ |
| |
| |
| #ifdef BCM_DEBUG |
| #ifdef BCM_SHOW_ASF_REGS |
| static void |
| bcm_asf_check_register( void ) |
| { |
| uint32_t i; |
| |
| i = bcm_read_reg32( ASF_CTRL_R ); |
| printf( "bcm57xx: ASF control : %x\n", i ); |
| |
| i = bcm_read_reg32( ASF_WATCHDOG_TIMER_R ); |
| printf( "bcm57xx: ASF Watchdog Timer : %x\n", i ); |
| |
| i = bcm_read_reg32( ASF_HEARTBEAT_TIMER_R ); |
| printf( "bcm57xx: ASF Heartbeat Timer : %x\n", i ); |
| |
| i = bcm_read_reg32( ASF_POLL_TIMER_R ); |
| printf( "bcm57xx: ASF Poll Timer : %x\n", i ); |
| |
| i = bcm_read_reg32( POLL_LEGACY_TIMER_R ); |
| printf( "bcm57xx: Poll Legacy Timer : %x\n", i ); |
| |
| i = bcm_read_reg32( RETRANSMISSION_TIMER_R ); |
| printf( "bcm57xx: Retransmission Timer : %x\n", i ); |
| |
| i = bcm_read_reg32( TIME_STAMP_COUNTER_R ); |
| printf( "bcm57xx: Time Stamp Counter : %x\n", i ); |
| |
| i = bcm_read_reg32( RX_CPU_MODE_R ); |
| printf( "bcm57xx: RX RISC Mode : %x\n", i ); |
| |
| i = bcm_read_reg32( RX_CPU_STATE_R ); |
| printf( "bcm57xx: RX RISC State : %x\n", i ); |
| |
| i = bcm_read_reg32( RX_CPU_PC_R ); |
| printf( "bcm57xx: RX RISC Prg. Counter : %x\n", i ); |
| } |
| #endif |
| #endif |
| |
| static int |
| bcm_fw_halt( void ) |
| { |
| int i; |
| |
| bcm_write_mem32( BCM_FW_MBX_CMD, BCM_NICDRV_PAUSE_FW ); |
| bcm_setb_reg32( RX_CPU_EVENT_R, BIT32( 14 ) ); |
| |
| /* Wait for RX cpu to ACK the event. */ |
| for (i = 0; i < 100; i++) { |
| if(bcm_read_reg32( RX_CPU_EVENT_R ) & BIT32( 14 )) |
| break; |
| SLOF_msleep(1); |
| } |
| if( i>= 100) |
| return -1; |
| return 0; |
| } |
| |
| |
| #ifdef BCM_SW_AUTONEG |
| static void |
| bcm_sw_autoneg( void ) { |
| uint32_t i, j, k; |
| uint32_t SerDesCfg; |
| uint32_t SgDigControl; |
| uint32_t SgDigStatus; |
| uint32_t ExpectedSgDigControl; |
| int AutoNegJustInitiated = 0; |
| |
| // step 1: init TX 1000BX Autoneg. Register to zero |
| bcm_write_reg32(TX_1000BX_AUTONEG_R, 0); |
| |
| // step 2&3: set TBI mode |
| bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 2 ) | BIT32( 3 ) ); |
| SLOF_usleep(10); |
| |
| // step 4: enable link attention |
| bcm_setb_reg32( ETH_MAC_EVT_EN_R, BIT32( 12 ) ); |
| |
| // step 5: preserve voltage regulator bits |
| SerDesCfg = bcm_read_reg32(SERDES_CTRL_R) & ( BIT32( 20 ) | BIT32( 21 ) |
| | BIT32( 22 ) | BIT32( 23 ) ); |
| |
| // step 6: preserve voltage regulator bits |
| SgDigControl = bcm_read_reg32(HW_AUTONEG_CTRL_R); |
| |
| // step 7: if device is NOT set-up for auto negotiation, then go to step 26 |
| // goto bcm_setup_phy_step26; |
| |
| // We want to use auto negotiation |
| |
| // step 8: we don't want to use flow control |
| ExpectedSgDigControl = 0x81388400; // no flow control |
| |
| // step 9: compare SgDigControl with 0x81388400 |
| if(SgDigControl == ExpectedSgDigControl) { |
| goto bcm_setup_phy_step17; |
| } |
| #ifdef BCM_DEBUG |
| printf("bcm57xx: SgDigControl = %08X\n", SgDigControl); |
| #endif |
| // step 10 |
| bcm_write_reg32(SERDES_CTRL_R, SerDesCfg | 0xC011880); |
| |
| // step 11: restart auto negotiation |
| bcm_write_reg32(HW_AUTONEG_CTRL_R, ExpectedSgDigControl | BIT32( 30 ) ); |
| |
| // step 12: read back HW_AUTONEG_CTRL_R |
| bcm_read_reg32(HW_AUTONEG_CTRL_R); |
| |
| // step 13 |
| SLOF_usleep( 5 ); |
| |
| // step 14,15,16: same as step 11, but don't restart auto neg. |
| bcm_write_reg32(HW_AUTONEG_CTRL_R, ExpectedSgDigControl); |
| AutoNegJustInitiated = 1; |
| goto bcm_setup_phy_step30; |
| |
| // step 17: |
| bcm_setup_phy_step17: |
| if( ( bcm_read_reg32(ETH_MAC_STAT_R) & ( BIT32( 1 ) | BIT32( 0 ) ) ) == 0 ) { |
| goto bcm_setup_phy_step30; |
| } |
| |
| // step 18: Get HW Autoneg. Status |
| SgDigStatus = bcm_read_reg32(HW_AUTONEG_STAT_R); |
| |
| // step 19: |
| if( ( SgDigStatus & BIT32(1) ) |
| && ( bcm_read_reg32(ETH_MAC_STAT_R) & BIT32(0) ) ) { |
| // resolve the current flow control? |
| AutoNegJustInitiated = 0; |
| goto bcm_setup_phy_step30; |
| } |
| |
| // step 20 |
| if( SgDigStatus & BIT32(1) ) { |
| goto bcm_setup_phy_step30; |
| } |
| if( AutoNegJustInitiated != 0) { |
| AutoNegJustInitiated = 0; |
| goto bcm_setup_phy_step29; |
| } |
| |
| // step 21, 22, 23, 24: fallback to 1000Mbps-FullDuplex forced mode |
| if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) == 0 ) { |
| // port 0 |
| bcm_write_reg32( SERDES_CTRL_R, 0xC010880 ); |
| } |
| else { // port 1 |
| bcm_write_reg32( SERDES_CTRL_R, 0x4010880 ); |
| } |
| // set to 1000Mbps-FullDuplex |
| bcm_write_reg32(HW_AUTONEG_CTRL_R, 0x1388400); |
| // read back |
| bcm_read_reg32(HW_AUTONEG_CTRL_R); |
| SLOF_usleep( 40 ); |
| |
| // step 25: a little bit reduces... |
| goto bcm_setup_phy_step30; |
| |
| // step 26: check if auto negotiation bit is NOT set |
| // bcm_setup_phy_step26: |
| if( ( SgDigControl & BIT32(31) )== 0 ) { |
| printf("No autoneg.\n"); |
| goto bcm_setup_phy_step29; |
| } |
| |
| // step 27: |
| if( ( bcm_read_reg32( MAC_FUNC_R ) & BIT32( 2 ) ) == 0 ) { |
| // port 0 |
| bcm_write_reg32( SERDES_CTRL_R, 0xC010880 ); |
| } |
| else { // port 1 |
| bcm_write_reg32( SERDES_CTRL_R, 0x4010880 ); |
| } |
| |
| // step 28: disable auto neg. and force 1000FD mode |
| bcm_write_reg32(HW_AUTONEG_CTRL_R, 0x1388400); |
| |
| // step 29-31: omitted for 5704S |
| bcm_setup_phy_step29: |
| bcm_setup_phy_step30: |
| |
| // step 32: clear link attentions |
| i = bcm_read_reg32( ETH_MAC_STAT_R ) | BIT32( 3 ) | BIT32( 4 ); |
| k = 100; |
| do { |
| bcm_write_reg32( ETH_MAC_STAT_R, i ); |
| j = bcm_read_reg32( ETH_MAC_STAT_R ); |
| if( ( j & BIT32( 3 ) ) != 0 ) |
| i = i & ~(BIT32( 3 )); |
| if( ( j & BIT32( 4 ) ) != 0 ) |
| i = i & ~(BIT32( 4 )); |
| --k; |
| } while( i & k); |
| |
| // step 33 |
| if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & BIT32( 0 ) ) == 0 ) { |
| goto bcm_setup_phy_step35; |
| } |
| |
| // step 34 |
| i = bcm_read_reg32( ETH_MAC_MODE_R ); |
| i|= BIT32( 17 ); |
| bcm_write_reg32( ETH_MAC_MODE_R, i ); |
| |
| SLOF_usleep( 1 ); |
| |
| i = bcm_read_reg32( ETH_MAC_STAT_R ); |
| i&= ~BIT32( 17 ); |
| bcm_write_reg32( ETH_MAC_STAT_R, i ); |
| |
| // step 35 & 36: done |
| bcm_setup_phy_step35: |
| #ifdef BCM_DEBUG |
| printf("bcm57xx: SetupPhy\n"); |
| #endif |
| return; |
| } |
| #endif |
| |
| static int |
| bcm_handle_events( void ) { |
| #ifdef BCM_DEBUG |
| #ifdef BCM_SHOW_ASF_REGS |
| // ASF REGISTER CHECK |
| // ------------------ |
| // check if watchdog timer expired |
| if( bcm_read_reg32( ASF_WATCHDOG_TIMER_R ) == 0 ) { |
| // Show ASF registers |
| bcm_asf_check_register(); |
| |
| // rearm watchdog timer |
| bcm_write_reg32( ASF_WATCHDOG_TIMER_R, 5 ); |
| } |
| #endif |
| #endif |
| |
| #ifdef BCM_SW_AUTONEG |
| // AUTO NEGOTIATION |
| // ---------------- |
| |
| // Check event for Auto Negotiation |
| if( ( bcm_read_reg32( ETH_MAC_STAT_R ) & |
| ( BIT32( 12 ) | BIT32( 3 ) | BIT32( 0 ) ) ) != 0 ) { |
| // link timer procedure |
| bcm_sw_autoneg(); |
| } |
| #endif |
| |
| // ASF FW HEARTBEAT |
| // ---------------- |
| |
| // check if heartsbeat timer expired |
| if( bcm_read_reg32( ASF_HEARTBEAT_TIMER_R ) <= 2) { |
| int i; |
| |
| // Send heartbeat event |
| bcm_write_mem32( BCM_FW_MBX_CMD, BCM_NICDRV_ALIVE ); |
| bcm_write_mem32( BCM_FW_MBX_LEN, 4 ); |
| bcm_write_mem32( BCM_FW_MBX_DATA, 5 ); |
| bcm_setb_reg32( RX_CPU_EVENT_R, BIT32( 14 ) ); |
| |
| // Wait for RX cpu to ACK the event. |
| for (i = 100; i > 0; i--) { |
| if(bcm_read_reg32( RX_CPU_EVENT_R ) & BIT32( 14 )) |
| break; |
| SLOF_msleep(1); |
| } |
| if( i == 0) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: RX cpu did not acknowledge heartbeat event\n" ); |
| #endif |
| return -1; |
| } |
| |
| // rearm heartbeat timer |
| bcm_write_reg32( ASF_HEARTBEAT_TIMER_R, 5 ); |
| } |
| return 0; |
| } |
| |
| /* |
| * interface |
| ****************************************************************************** |
| */ |
| |
| /* |
| * bcm_receive |
| */ |
| static int |
| bcm_receive( char *f_buffer_pc, int f_len_i ) |
| { |
| uint32_t l_rxret_prod_u32 = bcm_read_reg32( RXRET_PROD_IND ); |
| uint32_t l_rxret_cons_u32 = bcm_read_reg32( RXRET_CONS_IND ); |
| uint32_t l_rxprod_prod_u32 = bcm_read_reg32( RXPROD_PROD_IND ); |
| int l_ret_i; |
| #ifdef BCM_DEBUG |
| #ifdef BCM_SHOW_RCV_DATA |
| int i, j; |
| #endif |
| #endif |
| |
| /* |
| * NOTE: dummy read to ensure data has already been DMA'd is |
| * done by the indice reads |
| */ |
| |
| bcm_handle_events(); |
| |
| /* |
| * if producer index == consumer index then nothing was received |
| */ |
| if( l_rxret_prod_u32 == l_rxret_cons_u32 ) { |
| return 0; |
| } |
| |
| /* |
| * discard erroneous packets |
| */ |
| if( ( bcm_rxret_ring[l_rxret_cons_u32].m_typeflags_u32 & BIT32( 10 ) ) != 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: erroneous frame received\n" ); |
| printf( " : frame discarded\n" ); |
| #endif |
| l_ret_i = 0; |
| } else { |
| /* |
| * get packet length, throw away checksum (last 4 bytes) |
| */ |
| l_ret_i = (int) ( bcm_rxret_ring[l_rxret_cons_u32].m_idxlen_u32 & |
| (uint32_t) 0xffff ) - (int) 4; |
| |
| /* |
| * discard oversized packets |
| */ |
| if( l_ret_i > f_len_i ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: receive packet length error:\n" ); |
| printf( " : incoming 0x%X bytes, available buffer 0x%X bytes\n", l_ret_i, f_len_i ); |
| printf( " : frame discarded\n" ); |
| #endif |
| l_ret_i = 0; |
| } |
| |
| } |
| |
| /* |
| * copy & update data & indices |
| */ |
| if( l_ret_i != 0 ) { |
| uint64_t l_cpyaddr_u64; |
| |
| l_cpyaddr_u64 = |
| ( (uint64_t) bcm_rxret_ring[l_rxret_cons_u32].m_hostaddr_st.m_hi_u32 << 32 ); |
| l_cpyaddr_u64 += |
| ( (uint64_t) bcm_rxret_ring[l_rxret_cons_u32].m_hostaddr_st.m_lo_u32 ); |
| |
| // FIXME: |
| if(l_cpyaddr_u64 == 0) { |
| #ifdef BCM_DEBUG |
| printf("bcm57xx: NULL address\n"); |
| #endif |
| return 0; |
| } |
| // |
| memcpy( (void *) f_buffer_pc, |
| (void *) l_cpyaddr_u64, |
| (size_t) l_ret_i ); |
| |
| } |
| |
| /* |
| * replenish bd to producer ring |
| */ |
| bcm_rxprod_ring[l_rxprod_prod_u32] = |
| bcm_rxret_ring[l_rxret_cons_u32]; |
| bcm_rxprod_ring[l_rxprod_prod_u32].m_idxlen_u32 = |
| ( l_rxprod_prod_u32 << 16 ); |
| bcm_rxprod_ring[l_rxprod_prod_u32].m_idxlen_u32 += |
| (uint32_t) BCM_BUF_SIZE; |
| |
| /* |
| * update producer ring's producer index |
| */ |
| l_rxprod_prod_u32 = ( l_rxprod_prod_u32 + 1 ) & ( BCM_RXPROD_RING_SIZE - 1 ); |
| |
| /* |
| * move to the next bd in return ring |
| */ |
| l_rxret_cons_u32 = ( l_rxret_cons_u32 + 1 ) & ( bcm_rxret_ring_sz - 1 ); |
| |
| /* |
| * synchronize before new indices are send to NIC |
| */ |
| mb(); |
| |
| /* |
| * write back new indices |
| */ |
| bcm_write_reg32( RXRET_CONS_IND, l_rxret_cons_u32 ); |
| bcm_write_reg32( RXPROD_PROD_IND, l_rxprod_prod_u32 ); |
| |
| #ifdef BCM_DEBUG |
| #ifdef BCM_SHOW_RCV |
| if( l_ret_i != 0 ) { |
| printf( "bcm57xx: received bytes: %d\n", l_ret_i ); |
| } |
| #ifdef BCM_SHOW_RCV_DATA |
| for( i = 0, j = 0; i < l_ret_i; i++ ) { |
| printf( "%02X ", ( uint32_t ) f_buffer_pc[i] ); |
| |
| if( ( ++j % 0x18 ) == 0 ) { |
| printf( "\n" ); |
| } |
| } |
| |
| if( ( i % 0x18 ) != 0 ) { |
| printf( "\n" ); |
| } |
| #endif |
| #endif |
| #endif |
| |
| /* |
| * return packet length |
| */ |
| return l_ret_i; |
| } |
| |
| static int |
| bcm_xmit( char *f_buffer_pc, int f_len_i ) |
| { |
| uint32_t l_tx_cons_u32 = bcm_read_reg32( TX_CONS_IND ); |
| uint32_t l_tx_prod_u32 = bcm_read_reg32( TX_PROD_IND ); |
| uint64_t l_cpyaddr_u64; |
| |
| #ifdef BCM_DEBUG |
| #ifdef BCM_SHOW_XMIT_DATA |
| int i, j; |
| #endif |
| #ifdef BCM_SHOW_IDX |
| printf( "\n" ); |
| printf( "bcm57xx: TX_PROD_IND : 0x%03X\n", l_tx_prod_u32 ); |
| printf( "bcm57xx: TX_CONS_IND : 0x%03X\n", l_tx_cons_u32 ); |
| printf( "bcm57xx: RXPROD_PROD_IND: 0x%03X\n", bcm_read_reg32( RXPROD_PROD_IND ) ); |
| printf( "bcm57xx: RXPROD_CONS_IND: 0x%03X\n", bcm_read_reg32( RXPROD_CONS_IND ) ); |
| printf( "bcm57xx: RXRET_PROD_IND : 0x%03X\n", bcm_read_reg32( RXRET_PROD_IND ) ); |
| printf( "bcm57xx: RXRET_CONS_IND : 0x%03X\n", bcm_read_reg32( RXRET_CONS_IND ) ); |
| printf( "bcm57xx: available txb : 0x%03X\n", bcm_tx_bufavail_u32 ); |
| #endif |
| #ifdef BCM_SHOW_STATS |
| printf( "bcm57xx: bcm_status.m_st_word_u32: %08X\n", bcm_status.m_st_word_u32 ); |
| printf( "bcm57xx: bcm_status.m_st_tag_u32 : %08X\n", bcm_status.m_st_tag_u32 ); |
| printf( "bcm57xx: bcm_status.m_rxprod_cons_u16: %04X\n", ( uint32_t ) bcm_status.m_rxprod_cons_u16 ); |
| printf( "bcm57xx: bcm_status.m_unused_u16: %04X\n", ( uint32_t ) bcm_status.m_unused_u16 ); |
| printf( "bcm57xx: bcm_status.m_unused_u32: %08X\n", bcm_status.m_unused_u32 ); |
| printf( "bcm57xx: bcm_status.m_tx_cons_u16: %04X\n", ( uint32_t ) bcm_status.m_tx_cons_u16 ); |
| printf( "bcm57xx: bcm_status.m_rxret_prod_u16: %04X\n", ( uint32_t ) bcm_status.m_rxret_prod_u16 ); |
| #endif |
| #endif |
| |
| bcm_handle_events(); |
| |
| /* |
| * make all consumed bd's available in the ring again |
| * this way only a few buffers are needed instead of |
| * having 512 buffers allocated |
| */ |
| while( bcm_tx_start_u32 != l_tx_cons_u32 ) { |
| bcm_tx_ring[bcm_tx_stop_u32] = bcm_tx_ring[bcm_tx_start_u32]; |
| bcm_tx_stop_u32 = ( bcm_tx_stop_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 ); |
| bcm_tx_start_u32 = ( bcm_tx_start_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 ); |
| bcm_tx_bufavail_u32++; |
| } |
| |
| /* |
| * check for tx buffer availability |
| */ |
| if( bcm_tx_bufavail_u32 == 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: no more transmit buffers available\n" ); |
| #endif |
| return 0; |
| } |
| |
| /* |
| * setup next available bd in tx ring |
| */ |
| bcm_tx_ring[l_tx_prod_u32].m_lenflags_u32 = ( BIT32( 2 ) | BIT32( 7 ) /*| BIT32( 6 )*/ ); |
| bcm_tx_ring[l_tx_prod_u32].m_lenflags_u32 += ( (uint32_t) f_len_i << 16 ); |
| // bcm_tx_ring[l_tx_prod_u32].m_VLANtag_u32 = BCM_VLAN_TAG; |
| |
| l_cpyaddr_u64 = ( (uint64_t) bcm_tx_ring[l_tx_prod_u32].m_hostaddr_st.m_hi_u32 << 32 ); |
| l_cpyaddr_u64 += ( (uint64_t) bcm_tx_ring[l_tx_prod_u32].m_hostaddr_st.m_lo_u32 ); |
| |
| #ifdef BCM_DEBUG |
| #ifdef BCM_SHOW_XMIT_STATS |
| printf("bcm57xx: xmit: l_cpyaddr_u64: 0x%lx\n", l_cpyaddr_u64 ); |
| printf(" f_buffer_pc : 0x%lx\n", f_buffer_pc ); |
| printf(" f_len_i : %d\n", f_len_i ); |
| #endif |
| #endif |
| memcpy( (void *) l_cpyaddr_u64, (void *) f_buffer_pc, (size_t) f_len_i ); |
| |
| /* |
| * update tx producer index & available buffers |
| */ |
| l_tx_prod_u32 = ( l_tx_prod_u32 + 1 ) & ( BCM_TX_RING_SIZE - 1 ); |
| bcm_tx_bufavail_u32--; |
| |
| /* |
| * synchronize before new index is send to NIC |
| */ |
| mb(); |
| |
| bcm_write_reg32( TX_PROD_IND, l_tx_prod_u32 ); |
| |
| #ifdef BCM_DEBUG |
| #ifdef BCM_SHOW_XMIT |
| printf( "bcm57xx: sent bytes: %d\n", f_len_i ); |
| #ifdef BCM_SHOW_XMIT_DATA |
| for( i = 0, j = 0; i < f_len_i; i++ ) { |
| printf( "%02X ", ( uint32_t ) f_buffer_pc[i] ); |
| |
| if( ( ++j % 0x18 ) == 0 ) { |
| printf( "\n" ); |
| } |
| |
| } |
| if( ( i % 0x18 ) != 0 ) { |
| printf( "\n" ); |
| } |
| #endif |
| #endif |
| |
| #ifdef BCM_SHOW_STATS |
| // coalesce status block now |
| bcm_setb_reg32( HOST_COAL_MODE_R, BIT32( 3 ) | BIT32( 1 ) ); |
| #endif |
| |
| #endif |
| return f_len_i; |
| } |
| |
| static int |
| check_driver( uint16_t vendor_id, uint16_t device_id ) |
| { |
| uint64_t i; |
| |
| /* |
| * checks whether the driver is handling this device |
| * by verifying vendor & device id |
| * vendor id 0x14e4 == Broadcom |
| */ |
| if( vendor_id != 0x14e4 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: netdevice not supported, illegal vendor id\n" ); |
| #endif |
| return -1; |
| } |
| |
| for( i = 0; bcm_dev[i].m_dev_u32 != 0; i++ ) { |
| if( bcm_dev[i].m_dev_u32 == (uint32_t) device_id ) { |
| // success |
| break; |
| } |
| } |
| |
| if(bcm_dev[i].m_dev_u32 == 0) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: netdevice not supported, illegal device ID\n" ); |
| #endif |
| return -1; |
| } |
| |
| /* |
| * initialize static variables |
| */ |
| bcm_device_u64 = bcm_dev[i].m_devmsk_u64; |
| bcm_rxret_ring_sz = 0; |
| bcm_baseaddr_u64 = 0; |
| bcm_memaddr_u64 = 0; |
| |
| bcm_tx_start_u32 = 0; |
| bcm_tx_stop_u32 = 0; |
| bcm_tx_bufavail_u32 = 0; |
| |
| return 0; |
| } |
| |
| static void |
| bcm_wol_activate(void) |
| { |
| #ifdef BCM_DEBUG |
| uint16_t reg_pwr_cap; |
| #endif |
| uint16_t reg_pwr_crtl; |
| uint32_t wol_mode; |
| |
| wol_mode = bcm_read_reg32( WOL_MODE_R ); |
| bcm_write_reg32( WOL_MODE_R, wol_mode | BIT32(0) ); |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: WOL activating..." ); |
| #endif |
| |
| // bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_WOL ); |
| // SLOF_msleep( 100 ); |
| |
| #ifdef BCM_DEBUG |
| reg_pwr_cap = SLOF_pci_config_read16(0x4a); |
| /*reg_pwr_cap = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| 0x4a );*/ |
| printf( "bcm57xx: PM Capability Register: %04X\n", reg_pwr_cap ); |
| #endif |
| /* get curretn power control register */ |
| reg_pwr_crtl = SLOF_pci_config_read16(0x4c); |
| /*reg_pwr_crtl = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| 0x4c );*/ |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: PM Control/Status Register: %04X\n", reg_pwr_crtl ); |
| #endif |
| |
| /* switch to power state D0 */ |
| reg_pwr_crtl |= 0x8000; |
| reg_pwr_crtl &= ~(0x0003); |
| SLOF_pci_config_write16(0x4c, reg_pwr_crtl); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| 0x4c, |
| reg_pwr_crtl );*/ |
| SLOF_msleep(10); |
| |
| /* |
| bcm_write_mem32( BCM_NICDRV_WOL_MBX, BCM_WOL_MAGIC_NUMBER | |
| NIC_WOLDRV_STATE_SHUTDOWN | |
| NIC_WOLDRV_WOL | |
| NIC_WOLDRV_SET_MAGIC_PKT ); |
| */ |
| |
| /* switch to power state D3hot */ |
| /* |
| reg_pwr_crtl |= 0x0103; |
| SLOF_pci_config_write16(0x4c, reg_pwr_crtl); |
| snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| 0x4c, |
| reg_pwr_crtl ); |
| SLOF_msleep(10); |
| */ |
| |
| #ifdef BCM_DEBUG |
| reg_pwr_crtl = SLOF_pci_config_read16(0x4c); |
| /*reg_pwr_crtl = snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| 0x4c );*/ |
| |
| printf( "bcm57xx: PM Control/Status Register: %04X\n", reg_pwr_crtl ); |
| #endif |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: WOL activated" ); |
| #endif |
| } |
| |
| static int |
| bcm_init( net_driver_t *driver ) |
| { |
| static const uint32_t lc_Maxwait_u32 = (uint32_t) 1000; |
| uint32_t l_baseaddrL_u32; |
| uint32_t l_baseaddrH_u32; |
| uint32_t i; |
| uint8_t *mac_addr = driver->mac_addr; |
| |
| if(driver->running != 0) { |
| return 0; |
| } |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: detected device " ); |
| if( IS_5703 ) { |
| printf( "5703S\n" ); |
| } else if( IS_5704 ) { |
| printf( "5704" ); |
| |
| if( IS_SERDES ) { |
| printf( "S\n" ); |
| } else { |
| printf( "C\n" ); |
| } |
| |
| } else if( IS_5714 ) { |
| printf( "5714\n" ); |
| } |
| #endif |
| /* |
| * setup register & memory base addresses of NIC |
| */ |
| l_baseaddrL_u32 = (uint32_t) ~0xf & |
| (uint32_t) SLOF_pci_config_read32(PCI_BAR1_R); |
| /*l_baseaddrL_u32 = ( (uint32_t) ~0xf & |
| (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_BAR1_R ) );*/ |
| |
| l_baseaddrH_u32 = (uint32_t) SLOF_pci_config_read32(PCI_BAR2_R); |
| /*l_baseaddrH_u32 = |
| (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_BAR2_R );*/ |
| bcm_baseaddr_u64 = (uint64_t) l_baseaddrH_u32; |
| bcm_baseaddr_u64 <<= 32; |
| bcm_baseaddr_u64 += (uint64_t) l_baseaddrL_u32; |
| bcm_baseaddr_u64 = |
| (uint64_t) SLOF_translate_my_address((void *)bcm_baseaddr_u64); |
| /*snk_kernel_interface->translate_addr(((void *)&(bcm_baseaddr_u64)));*/ |
| bcm_memaddr_u64 = bcm_baseaddr_u64 + BCM_MEMORY_OFFS; |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: device's register base high address = 0x%08X\n", l_baseaddrH_u32 ); |
| printf( "bcm57xx: device's register base low address = 0x%08X\n", l_baseaddrL_u32 ); |
| printf( "bcm57xx: device's register address = 0x%llx\n", bcm_baseaddr_u64 ); |
| #endif |
| |
| /* |
| * 57xx hardware initialization |
| * BCM57xx Programmer's Guide: Section 8, "Initialization" |
| * steps 1 through 101 |
| */ |
| |
| // step 1: enable bus master & memory space in command reg |
| i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); |
| SLOF_pci_config_write16(PCI_COM_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_COM_R, |
| ( int ) i );*/ |
| // step 2: disable & mask interrupts & enable pci byte/word swapping & enable indirect addressing mode |
| i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); |
| |
| SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_MISC_HCTRL_R, |
| ( int ) i );*/ |
| |
| /* |
| * from now on access may be made through the local |
| * read/write functions |
| */ |
| |
| // step 3: Save ahche line size register |
| // omitted, because register is not used for 5704 |
| |
| // step 4: acquire the nvram lock |
| if( bcm_nvram_lock() != 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: locking NVRAM failed\n" ); |
| #endif |
| return -1; |
| } |
| |
| // step 5: prepare the chip for writing TG3_MAGIC_NUMBER |
| bcm_setb_reg32( MEMARB_MODE_R, BIT32( 1 ) ); |
| i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); |
| SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_MISC_HCTRL_R, |
| ( int ) i );*/ |
| bcm_write_reg32( MODE_CTRL_R, BIT32( 23 ) | BIT32( 20 ) | |
| BIT32( 17 ) | BIT32( 16 ) | |
| BIT32( 14 ) | BIT32( 13 ) | |
| BIT32( 5 ) | BIT32( 4 ) | |
| BIT32( 2 ) | BIT32( 1 ) ); |
| |
| // step 6: write TG3_MAGIC_NUMBER |
| bcm_write_mem32( BCM_FW_MBX, BCM_MAGIC_NUMBER ); |
| |
| // step 7: reset core clocks |
| |
| if( IS_5714 ) { |
| bcm_setb_reg32( MISC_CFG_R, BIT32( 26 ) | BIT32( 0 ) ); |
| } else { |
| bcm_setb_reg32( MISC_CFG_R, BIT32( 0 ) ); |
| } |
| // step 8 |
| SLOF_msleep( 20 ); |
| |
| // step 9: disable & mask interrupts & enable indirect addressing mode & |
| // enable pci byte/word swapping initialize the misc host control register |
| i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); |
| SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_MISC_HCTRL_R, |
| ( int ) i );*/ |
| |
| // step 10: set but master et cetera |
| i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); |
| SLOF_pci_config_write16(PCI_COM_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_COM_R, |
| ( int ) i );*/ |
| |
| // step 11: disable PCI-X relaxed ordering |
| bcm_clrb_reg16( PCI_X_COM_R, BIT16( 1 ) ); |
| |
| // step 12: enable the MAC memory arbiter |
| bcm_setb_reg32( MEMARB_MODE_R, BIT32( 1 ) ); |
| |
| // step 13: omitted, only for BCM5700 |
| // step 14: s. step 10 |
| i = ( BIT32( 8 ) | BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); |
| SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_MISC_HCTRL_R, |
| ( int ) i );*/ |
| // step 15: set byte swapping (incl. step 27/28/29/30) |
| // included prohibition of tx/rx interrupts |
| bcm_write_reg32( MODE_CTRL_R, BIT32( 23 ) | BIT32( 20 ) | |
| BIT32( 17 ) | BIT32( 16 ) | |
| BIT32( 14 ) | BIT32( 13 ) | |
| BIT32( 5 ) | BIT32( 4 ) | |
| BIT32( 2 ) | BIT32( 1 ) ); |
| // step 16: omitted |
| i = 1000; |
| while( ( --i ) && |
| ( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) ) { |
| #ifdef BCM_DEBUG |
| printf( "." ); |
| #endif |
| SLOF_msleep( 1 ); |
| } |
| |
| // return on error |
| if( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) { |
| printf( "bootcode not loaded: %x\n", bcm_read_mem32( BCM_FW_MBX ) ); |
| #ifdef BCM_DEBUG |
| printf( "failed\n" ); |
| #endif |
| return -1; |
| } |
| |
| |
| // if ASF Firmware enabled |
| bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START ); |
| SLOF_msleep( 10 ); |
| |
| // step 17: write ethernet mac mode register |
| /* |
| * WY 07.02.07 |
| * omitted for correct SOL function |
| */ |
| /* |
| if( IS_SERDES ) { |
| bcm_write_reg32( ETH_MAC_MODE_R, (uint32_t) 0xc ); |
| } else { |
| bcm_write_reg32( ETH_MAC_MODE_R, (uint32_t) 0x0 ); |
| } |
| */ |
| |
| // step 18/19: omitted |
| // step 20: enable hw bugfix for 5704 |
| if( IS_5704 || IS_5703 ) { |
| bcm_setb_reg32( MSG_DATA_R, BIT32( 26 ) | |
| BIT32( 28 ) | |
| BIT32( 29 ) ); |
| } |
| |
| // step 21: omitted |
| // step 22: omitted |
| // step 23: 5704 clear statistics block |
| if( IS_5703 || IS_5704 ) { |
| memset_ci( (void *) ( bcm_memaddr_u64 + BCM_STATISTIC_OFFS ), |
| 0, |
| BCM_STATISTIC_SIZE ); |
| } |
| |
| // step 24/25: omitted |
| // step 26: set DMA Read/Write Control register |
| // NOTE: recommended values from the spec are used here |
| if( IS_5714 ) { |
| bcm_write_reg32( DMA_RW_CTRL_R, DMA_RW_CTRL_VAL_5714 ); |
| } else { |
| uint32_t l_PCIState_u32 = bcm_read_reg32( PCI_STATE_R ); |
| uint32_t l_DMAVal_u32 = DMA_RW_CTRL_VAL; |
| |
| if( ( l_PCIState_u32 & BIT32( 2 ) ) != 0 ) { // PCI |
| l_DMAVal_u32 |= (uint32_t) 0x300000; |
| } else { // PCI-X |
| l_DMAVal_u32 |= (uint32_t) 0x900000; |
| |
| if( ( bcm_read_reg32( PCI_CLK_CTRL_R ) & (uint32_t) 0x1f ) |
| >= (uint32_t) 6 ) { |
| l_DMAVal_u32 |= (uint32_t) 0x4000; |
| } |
| |
| } |
| |
| bcm_write_reg32( DMA_RW_CTRL_R, l_DMAVal_u32 ); |
| } |
| |
| // step 27/28/29: s. step 14 |
| |
| // step 30: Configure TCP/UDP pseudo header checksum offloading |
| // already done in step 14: offloading disabled |
| |
| // step 31: setup timer prescaler |
| i = bcm_read_reg32( MISC_CFG_R ); |
| i &= (uint32_t) ~0xfe; // clear bits 7-1 first |
| i |= ( BCM_TMR_PRESCALE << 1 ); |
| bcm_write_reg32( MISC_CFG_R, i ); |
| |
| // step 32: 5703/4 configure Mbuf pool address/length |
| // step 33: 5703/4 configure MAC DMA resource pool |
| // step 34: configure MAC memory pool watermarks |
| // step 35: 5703/4 configure DMA resource watermarks |
| // using recommended settings (hard coded) |
| if( IS_5703 || IS_5704 ) { |
| |
| if( IS_5703 ) { |
| bcm_write_reg32( MBUF_POOL_ADDR_R, (uint32_t) 0x8000 ); |
| bcm_write_reg32( MBUF_POOL_LEN_R, (uint32_t) 0x18000 ); |
| } else { |
| bcm_write_reg32( MBUF_POOL_ADDR_R, (uint32_t) 0x10000 ); |
| bcm_write_reg32( MBUF_POOL_LEN_R, (uint32_t) 0x10000 ); |
| } |
| |
| bcm_write_reg32( DMA_DESC_POOL_ADDR_R, (uint32_t) 0x2000 ); |
| bcm_write_reg32( DMA_DESC_POOL_LEN_R, (uint32_t) 0x2000 ); |
| |
| bcm_write_reg32( DMA_RMBUF_LOW_WMARK_R, (uint32_t) 0x50 ); |
| bcm_write_reg32( MAC_RXMBUF_LOW_WMARK_R, (uint32_t) 0x20 ); |
| bcm_write_reg32( MBUF_HIGH_WMARK_R, (uint32_t) 0x60 ); |
| |
| bcm_write_reg32( DMA_DESC_LOW_WM_R, (uint32_t) 5 ); |
| bcm_write_reg32( DMA_DESC_HIGH_WM_R, (uint32_t) 10 ); |
| } else { |
| bcm_write_reg32( DMA_RMBUF_LOW_WMARK_R, (uint32_t) 0x00 ); |
| bcm_write_reg32( MAC_RXMBUF_LOW_WMARK_R, (uint32_t) 0x10 ); |
| bcm_write_reg32( MBUF_HIGH_WMARK_R, (uint32_t) 0x60 ); |
| } |
| |
| // step 35: omitted |
| // step 36: Configure flow control behaviour |
| // using recommended settings (hard coded) |
| bcm_write_reg32( LOW_WMARK_MAX_RXFRAM_R, (uint32_t) 0x02 ); |
| |
| // step 37/38: enable buffer manager & wait for successful start |
| bcm_setb_reg32( BUF_MAN_MODE_R, BIT32( 2 ) | BIT32( 1 ) ); |
| |
| i = lc_Maxwait_u32; |
| while( ( --i ) && |
| ( ( bcm_read_reg32( BUF_MAN_MODE_R ) & BIT32( 1 ) ) == 0 ) ) { |
| SLOF_usleep( 10 ); |
| } |
| |
| // return on error |
| if( i == 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: init step 38: enable buffer manager failed\n" ); |
| #endif |
| return -1; |
| } |
| |
| // step 39: enable internal hardware queues |
| bcm_write_reg32( FTQ_RES_R, (uint32_t) ~0 ); |
| bcm_write_reg32( FTQ_RES_R, (uint32_t) 0 ); |
| |
| // step 40/41/42: initialize rx producer ring |
| bcm_init_rxprod_ring(); |
| |
| // step 43: set rx producer ring replenish threshold |
| // using recommended setting of maximum allocated BD's/8 |
| bcm_write_reg32( STD_RXPR_REP_THR_R, (uint32_t) BCM_MAX_RX_BUF / 8 ); |
| |
| // step 44/45/46: initialize send rings |
| bcm_init_tx_ring(); |
| bcm_init_rxret_ring(); |
| |
| // steps 47-50 done in ring init functions |
| // step 51: configure MAC unicast address |
| bcm_nvram_init(); |
| if( bcm_mac_init( (uint8_t *) mac_addr ) < 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: init step 51: configure MAC unicast address failed\n" ); |
| #endif |
| return -1; |
| } |
| memcpy(driver->mac_addr, mac_addr, 6); |
| |
| // step 52: configure backoff random seed for transmit |
| // using recommended algorithm |
| i = (uint32_t) mac_addr[0] + (uint32_t) mac_addr[1] + |
| (uint32_t) mac_addr[2] + (uint32_t) mac_addr[3] + |
| (uint32_t) mac_addr[4] + (uint32_t) mac_addr[5]; |
| i &= (uint32_t) 0x03ff; |
| bcm_write_reg32( ETH_TX_RND_BO_R, i ); |
| |
| // step 53: configure message transfer unit MTU size |
| bcm_write_reg32( RX_MTU_SIZE_R, (uint32_t) BCM_MTU_MAX_LEN ); |
| |
| // step 54: configure IPG for transmit |
| // using recommended value (through #define) |
| bcm_write_reg32( TX_MAC_LEN_R, TX_MAC_LEN_VAL ); |
| |
| // step 55: configure receive rules |
| |
| // set RX rule default class |
| bcm_write_reg32( RX_RULE_CFG_R, RX_RULE_CFG_VAL ); |
| |
| // step 56: configure the number of receive lists |
| bcm_write_reg32( RX_LST_PLACE_CFG_R, RX_LST_PLC_CFG_VAL ); |
| bcm_write_reg32( RX_LST_PLACE_STAT_EN_R, RX_LST_PLC_STAT_EN_VAL ); |
| |
| /* |
| // rule 1: accept frames for our MAC address |
| bcm_write_reg32( RX_RULE_CTRL_R ( 0 ), |
| BIT32( 31 ) | // enable rule |
| BIT32( 30 ) | // and with next |
| BIT32( 26 ) | // split value register |
| BIT32( 8 ) ); // class 1 |
| bcm_write_reg32( RX_RULE_VAL_R ( 0 ), |
| (uint32_t) 0xffff0000 | |
| ( bcm_read_reg32( MAC_ADDR_OFFS_HI(0) ) & |
| (uint32_t) 0xffff ) ); |
| |
| bcm_write_reg32( RX_RULE_CTRL_R ( 1 ), |
| BIT32( 31 ) | // enable rule |
| BIT32( 8 ) | // class 1 |
| BIT32( 1 ) ); // offset 2 |
| bcm_write_reg32( RX_RULE_VAL_R ( 1 ), |
| bcm_read_reg32( MAC_ADDR_OFFS_LO(0) ) ); |
| |
| // rule 2: accept broadcast frames |
| bcm_write_reg32( RX_RULE_CTRL_R ( 2 ), |
| BIT32( 31 ) | // enable rule |
| BIT32( 30 ) | // and with next |
| BIT32( 26 ) | // split value register |
| BIT32( 8 ) ); // class 1 |
| bcm_write_reg32( RX_RULE_VAL_R ( 2 ), |
| (uint32_t) ~0 ); |
| |
| bcm_write_reg32( RX_RULE_CTRL_R ( 3 ), |
| BIT32( 31 ) | // enable rule |
| BIT32( 8 ) | // class 1 |
| BIT32( 1 ) ); // offset 2 |
| bcm_write_reg32( RX_RULE_VAL_R ( 3 ), |
| (uint32_t) ~0 ); |
| */ |
| for( i=0; i<NUM_RX_RULE_ASF; ++i) { |
| bcm_write_reg32( RX_RULE_CTRL_R ( i ), 0 ); |
| bcm_write_reg32( RX_RULE_VAL_R ( i ), 0 ); |
| } |
| |
| // step 57-60: enable rx/tx statistics |
| // omitted, no need for statistics (so far) |
| |
| // step 61/62: disable host coalescing engine/wait 20ms |
| bcm_write_reg32( HOST_COAL_MODE_R, (uint32_t) 0 ); |
| |
| i = lc_Maxwait_u32 * 2; |
| while( ( --i ) && |
| ( bcm_read_reg32( HOST_COAL_MODE_R ) != 0 ) ) { |
| SLOF_usleep( 10 ); |
| } |
| |
| // return on error |
| if( i == 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: init step 62: disable host coal. engine failed\n" ); |
| #endif |
| return -1; |
| } |
| |
| // step 63-66: initialize coalescing engine |
| // NOTE: status block is unused in this driver, |
| // therefore the coal. engine status block |
| // automatic update is disabled (by writing |
| // 0 to every counter |
| bcm_write_reg32( RX_COAL_TICKS_R, 0 ); |
| bcm_write_reg32( TX_COAL_TICKS_R, 0 ); |
| bcm_write_reg32( RX_COAL_MAX_BD_R, 0 ); |
| bcm_write_reg32( TX_COAL_MAX_BD_R, 0 ); |
| bcm_write_reg32( RX_COAL_TICKS_INT_R, 0 ); |
| bcm_write_reg32( TX_COAL_TICKS_INT_R, 0 ); |
| bcm_write_reg32( RX_COAL_MAX_BD_INT_R, 0 ); |
| bcm_write_reg32( TX_COAL_MAX_BD_INT_R, 0 ); |
| |
| // step 67: initialize host status block address |
| // NOTE: status block is not needed in this driver, |
| // still it needs to be set up |
| i = (uint32_t) ( (uint64_t) &bcm_status >> 32 ); |
| bcm_write_reg32( STB_HOST_ADDR_HI_R, i ); |
| i = (uint32_t) ( (uint64_t) &bcm_status & (uint64_t) 0xffffffff ); |
| bcm_write_reg32( STB_HOST_ADDR_LO_R, i ); |
| |
| // 5704/3 adaption |
| if( IS_5703 || IS_5704 ) { |
| // step 68: 5704, for now omitted |
| // step 69: 5704 set the statistics coalescing tick counter |
| bcm_write_reg32( STAT_TICK_CNT_R, 0 ); |
| // step 70: 5704 configure statistics block address in NIC memory |
| // using recommended values (hard coded) |
| bcm_write_reg32( STAT_NIC_ADDR_R, (uint32_t) 0x300 ); |
| // step 71: 5704 configure status block address in NIC memory |
| // using recommended values (hard coded) |
| bcm_write_reg32( STB_NIC_ADDR_R, (uint32_t) 0xb00 ); |
| } |
| |
| // step 72: enable host coalescing engine |
| bcm_setb_reg32( HOST_COAL_MODE_R, BIT32( 12 ) | BIT32( 11 ) | BIT32( 1 ) ); |
| |
| // step 73: enable rx bd completion functional block |
| bcm_write_reg32( RX_BD_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); |
| |
| // step 74: enable rx list placement functional block |
| bcm_write_reg32( RX_LST_PLACE_MODE_R, BIT32( 1 ) ); |
| // 5704/3 adaption |
| if( IS_5703 || IS_5704 ) { |
| // step 75: 5704/3 enable receive list selector func block |
| bcm_write_reg32( RX_LST_SEL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); |
| } |
| |
| // step 76: enable DMA engines |
| bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 23 ) | BIT32( 22 ) | BIT32( 21 ) ); |
| /* |
| * WY 26.10.07 This is wrong for 5714, better leave it alone |
| if( IS_5714 ) { |
| bcm_setb_reg32( ETH_MAC_MODE_R, BIT32( 20 ) ); |
| } |
| */ |
| |
| // step 77: omitted, statistics are not used |
| // step 78: Configure the General Misc Local Control register |
| // NOTE: as known so far nothing needs to be done here, |
| // default values should work fine |
| //bcm_setb_reg32( MISC_LOCAL_CTRL_R, 0 ); |
| |
| // step 79: clear interrupts in INT_MBX0_R low word |
| bcm_write_reg32( INT_MBX0_R, 0 ); |
| // 5704/3 adaption |
| // step 80: 5704/3 enable DMA completion functional block |
| if( IS_5703 || IS_5704 ) { |
| bcm_write_reg32( DMA_COMPL_MODE_R, BIT32( 1 ) ); |
| } |
| |
| // step 81/82: configure write/read DMA mode registers |
| // disable MSI |
| bcm_write_reg32( RD_DMA_MODE_R, BIT32( 10 ) | BIT32( 9 ) | BIT32( 8 ) | |
| BIT32( 7 ) | BIT32( 6 ) | BIT32( 5 ) | |
| BIT32( 4 ) | BIT32( 3 ) | BIT32( 2 ) | |
| BIT32( 1 ) ); |
| bcm_write_reg32( WR_DMA_MODE_R, BIT32( 9 ) | BIT32( 8 ) | BIT32( 7 ) | |
| BIT32( 6 ) | BIT32( 5 ) | BIT32( 4 ) | |
| BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) ); |
| bcm_clrb_reg32( MSI_MODE_R, BIT32( 1 ) ); |
| SLOF_usleep( 100 ); |
| |
| // step 83-91: enable all these functional blocks... |
| bcm_write_reg32( RX_DAT_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); |
| |
| if( IS_5703 || IS_5704 ) { |
| bcm_write_reg32( MBUF_CLSTR_FREE_MODE_R, BIT32( 1 ) ); |
| } |
| |
| bcm_write_reg32( TX_DAT_COMPL_MODE_R, BIT32( 1 ) ); |
| bcm_write_reg32( TX_BD_COMPL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); |
| bcm_write_reg32( RX_BD_INIT_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); |
| bcm_write_reg32( RX_DAT_BD_INIT_MODE_R, BIT32( 1 ) ); |
| bcm_write_reg32( TX_DAT_INIT_MODE_R, BIT32( 1 ) | BIT32( 3 ) ); |
| bcm_write_reg32( TX_BD_INIT_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); |
| bcm_write_reg32( TX_BD_RING_SEL_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); |
| |
| // step 92: omitted |
| // step 93/94: Enable Tx/Rx MAC |
| bcm_setb_reg32( TX_MAC_MODE_R, BIT32( 1 ) ); |
| // bcm_setb_reg32( RX_MAC_MODE_R, BIT32( 1 ) | BIT32( 2 ) ); // set BIT32( 8 ) for promiscious mode! |
| bcm_setb_reg32( RX_MAC_MODE_R, BIT32( 1 ) ); // set BIT32( 8 ) for promiscious mode! |
| // set BIT32( 10) for VLAN |
| |
| // step 95: disable auto polling: |
| // bcm_phy_init takes care of this |
| // step 96: omitted |
| // step 97: omitted, may change though, but is not important |
| // step 98: activate link & enable MAC functional block |
| // NOTE autopolling is enabled so bit 0 needs not to be set |
| //bcm_setb_reg32( MI_STATUS_R, BIT32( 0 ) ); |
| |
| // step 99: setup PHY |
| // return if link is down |
| if( bcm_phy_init() < 0 ) { |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: init step 99: PHY initialization failed\n" ); |
| #endif |
| return -1; |
| } |
| |
| // step 100: setup multicast filters |
| bcm_write_reg32( MAC_HASH0_R, (uint32_t) 0 ); |
| bcm_write_reg32( MAC_HASH1_R, (uint32_t) 0 ); |
| bcm_write_reg32( MAC_HASH2_R, (uint32_t) 0 ); |
| bcm_write_reg32( MAC_HASH3_R, (uint32_t) 0 ); |
| /* |
| // accept all multicast frames |
| bcm_write_reg32( MAC_HASH0_R, (uint32_t) 0xffffffff ); |
| bcm_write_reg32( MAC_HASH1_R, (uint32_t) 0xffffffff ); |
| bcm_write_reg32( MAC_HASH2_R, (uint32_t) 0xffffffff ); |
| bcm_write_reg32( MAC_HASH3_R, (uint32_t) 0xffffffff ); |
| */ |
| // step 101: omitted, no interrupts used |
| |
| // make initial receive buffers available for NIC |
| // this step has to be done here after RX DMA engine has started (step 94) |
| bcm_write_reg32( RXPROD_PROD_IND, BCM_MAX_RX_BUF ); |
| |
| // if ASF Firmware enabled |
| bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START_DONE ); |
| SLOF_msleep( 10 ); |
| |
| // enable heartbeat timer |
| |
| bcm_write_reg32( ASF_HEARTBEAT_TIMER_R, 0x5 ); |
| |
| driver->running = 1; |
| // off we go.. |
| return 0; |
| } |
| |
| static int |
| bcm_reset( void ) |
| { |
| uint32_t i; |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: resetting controller.." ); |
| #endif |
| |
| bcm_write_mem32( BCM_FW_MBX, BCM_MAGIC_NUMBER ); |
| |
| if( IS_5714 ) { |
| bcm_setb_reg32( MISC_CFG_R, BIT32( 26 ) | BIT32( 0 ) ); |
| } else { |
| bcm_setb_reg32( MISC_CFG_R, BIT32( 0 ) ); |
| } |
| |
| SLOF_msleep( 20 ); |
| |
| /* |
| * after reset local read/write functions cannot be used annymore |
| * until bus master & stuff is set up again |
| */ |
| |
| i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); |
| SLOF_pci_config_write16(PCI_COM_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_COM_R, |
| ( int ) i );*/ |
| |
| // step 9 & 13: disable & mask interrupts & enable indirect addressing mode & |
| // enable pci byte/word swapping initialize the misc host control register |
| i = ( BIT32( 7 ) | BIT32( 5 ) | BIT32( 4 ) | |
| BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); |
| SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_MISC_HCTRL_R, |
| ( int ) i );*/ |
| |
| // step 16: poll for bootcode completion by waiting for the one's |
| // complement of the magic number previously written |
| i = 1000; |
| while( ( --i ) && |
| ( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) ) { |
| #ifdef BCM_DEBUG |
| printf( "." ); |
| #else |
| SLOF_msleep( 1 ); |
| #endif |
| } |
| |
| // return on error |
| if( bcm_read_mem32( BCM_FW_MBX ) != ~BCM_MAGIC_NUMBER ) { |
| #ifdef BCM_DEBUG |
| printf( "failed\n" ); |
| #endif |
| return -1; |
| } |
| |
| #ifdef BCM_DEBUG |
| printf( "done\n" ); |
| #endif |
| return 0; |
| } |
| |
| static int |
| bcm_term( void ) |
| { |
| uint32_t i; |
| uint16_t v; |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: driver shutdown.." ); |
| #endif |
| |
| /* |
| * halt ASF firmware |
| */ |
| bcm_fw_halt(); |
| |
| /* |
| * unload ASF firmware |
| */ |
| bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD ); |
| |
| /* |
| * disable RX producer rings |
| */ |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_JUM ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_JUM ), 0 ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_JUM ), 0 ); |
| bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_JUM ), 0 ); |
| |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_STD ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_STD ), 0 ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_STD ), 0 ); |
| bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_STD ), 0 ); |
| |
| bcm_write_reg32( BCM_RCB_LENFLAG_u16( BCM_RXPROD_RCB_MIN ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_HI_u16( BCM_RXPROD_RCB_MIN ), 0 ); |
| bcm_write_reg32( BCM_RCB_HOSTADDR_LOW_u16( BCM_RXPROD_RCB_MIN ), 0 ); |
| bcm_write_reg32( BCM_RCB_NICADDR_u16( BCM_RXPROD_RCB_MIN ), 0 ); |
| |
| /* |
| * disable RX return rings |
| */ |
| v = BCM_RXRET_RCB_OFFS; |
| for( i = 0; i < BCM_MAX_RXRET_RING; i++ ) { |
| bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); |
| |
| v += BCM_RCB_SIZE_u16; |
| } |
| |
| /* |
| * disable TX rings |
| */ |
| v = BCM_TX_RCB_OFFS; |
| for( i = 0; i < BCM_MAX_TX_RING; i++ ) { |
| bcm_write_mem32( BCM_RCB_LENFLAG_u16( v ), RCB_FLAG_RING_DISABLED ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_HI_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_HOSTADDR_LOW_u16( v ), 0 ); |
| bcm_write_mem32( BCM_RCB_NICADDR_u16( v ), 0 ); |
| |
| v += BCM_RCB_SIZE_u16; |
| } |
| |
| /* |
| * remove receive rules |
| */ |
| bcm_write_reg32( RX_RULE_CTRL_R ( 0 ), 0 ); |
| bcm_write_reg32( RX_RULE_VAL_R ( 0 ), 0 ); |
| bcm_write_reg32( RX_RULE_CTRL_R ( 1 ), 0 ); |
| bcm_write_reg32( RX_RULE_VAL_R ( 1 ), 0 ); |
| |
| /* |
| * shutdown sequence |
| * BCM57xx Programmer's Guide: Section 8, "Shutdown" |
| * the enable bit of every state machine of the 57xx |
| * has to be reset. |
| */ |
| |
| /* |
| * receive path shutdown sequence |
| */ |
| bcm_clr_wait_bit32( RX_MAC_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( RX_LST_PLACE_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( RX_BD_INIT_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( RX_DAT_BD_INIT_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( RX_DAT_COMPL_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( RX_BD_COMPL_MODE_R, BIT32( 1 ) ); |
| |
| if( IS_5704 || IS_5703 ) { |
| bcm_clr_wait_bit32( RX_LST_SEL_MODE_R, BIT32( 1 ) ); |
| } |
| |
| /* |
| * transmit path & memory shutdown sequence |
| */ |
| bcm_clr_wait_bit32( TX_BD_RING_SEL_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( TX_BD_INIT_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( TX_DAT_INIT_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( RD_DMA_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( TX_DAT_COMPL_MODE_R, BIT32( 1 ) ); |
| |
| if( IS_5704 ) { |
| bcm_clr_wait_bit32( DMA_COMPL_MODE_R, BIT32( 1 ) ); |
| } |
| |
| bcm_clr_wait_bit32( TX_BD_COMPL_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( ETH_MAC_MODE_R, BIT32( 21 ) ); |
| bcm_clr_wait_bit32( TX_MAC_MODE_R, BIT32( 1 ) ); |
| |
| bcm_clr_wait_bit32( HOST_COAL_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( WR_DMA_MODE_R, BIT32( 1 ) ); |
| |
| if( IS_5704 || IS_5703 ) { |
| bcm_clr_wait_bit32( MBUF_CLSTR_FREE_MODE_R, BIT32( 1 ) ); |
| } |
| |
| bcm_write_reg32( FTQ_RES_R, (uint32_t) ~0 ); |
| bcm_write_reg32( FTQ_RES_R, (uint32_t) 0 ); |
| |
| if( IS_5704 || IS_5703 ) { |
| bcm_clr_wait_bit32( BUF_MAN_MODE_R, BIT32( 1 ) ); |
| bcm_clr_wait_bit32( MEMARB_MODE_R, BIT32( 1 ) ); |
| } |
| |
| #ifdef BCM_DEBUG |
| printf( "done.\n" ); |
| #endif |
| /* |
| * controller reset |
| */ |
| if( bcm_reset() != 0 ) { |
| return -1; |
| } |
| |
| /* |
| * restart ASF firmware |
| */ |
| bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD ); |
| SLOF_msleep( 10 ); |
| bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_UNLOAD_DONE ); |
| SLOF_msleep( 100 ); |
| bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START ); |
| SLOF_msleep( 10 ); |
| bcm_write_mem32( BCM_NICDRV_STATE_MBX, NIC_FWDRV_STATE_START_DONE ); |
| |
| /* |
| * activate Wake-on-LAN |
| */ |
| bcm_wol_activate(); |
| |
| /* |
| * PCI shutdown |
| */ |
| bcm_clrb_reg32( PCI_MISC_HCTRL_R, BIT32( 3 ) | BIT32( 2 ) ); |
| |
| /* |
| * from now on local rw functions cannot be used anymore |
| */ |
| |
| // bcm_clrb_reg32( PCI_COM_R, BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); |
| |
| SLOF_pci_config_write32(PCI_COM_R, BIT32(8) | BIT32(6)); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_COM_R, |
| BIT32(8) | BIT32(6) );*/ |
| |
| // no more networking... |
| return 0; |
| } |
| |
| static int |
| bcm_getmac(uint32_t addr, char mac[6]) |
| { |
| uint32_t t1, t2; |
| uint64_t t3; |
| |
| if (bcm_nvram_read(addr, &t1, 1) != 0) |
| return -1; |
| if (bcm_nvram_read(addr+4, &t2, 1) != 0) |
| return -1; |
| t3 = ((uint64_t)t1 << 32) + t2; |
| |
| mac[0] = (t3 >> 40) & 0xFF; |
| mac[1] = (t3 >> 32) & 0xFF; |
| mac[2] = (t3 >> 24) & 0xFF; |
| mac[3] = (t3 >> 16) & 0xFF; |
| mac[4] = (t3 >> 8) & 0xFF; |
| mac[5] = (t3 >> 0) & 0xFF; |
| |
| return 0; |
| } |
| |
| static char* |
| print_itoa(char *text, uint32_t value) |
| { |
| if(value >= 10) |
| text = print_itoa(text, value / 10); |
| *text = '0' + (value % 10); |
| ++text; |
| return text; |
| } |
| |
| static int |
| bcm_get_version(char *text) |
| { |
| uint32_t t1; |
| |
| if (bcm_nvram_read(0x94, &t1, 1) != 0) |
| return -1; |
| |
| text = print_itoa(text, (t1 >> 8) & 0xFF); |
| text[0] = '.'; |
| text = print_itoa(&text[1], t1 & 0xFF); |
| text[0] = '\n'; |
| return 0; |
| } |
| |
| static uint32_t |
| util_gen_crc( char *pcDatabuf, uint32_t ulDatalen, uint32_t ulCrc_in) |
| { |
| unsigned char data; |
| uint32_t idx, bit, crc = ulCrc_in; |
| |
| for(idx = 0; idx < ulDatalen; idx++) { |
| data = *pcDatabuf++; |
| for(bit = 0; bit < 8; bit++, data >>= 1) { |
| crc = (crc >> 1) ^ (((crc ^ data) & 1) ? |
| CRC32_POLYNOMIAL : 0); |
| } |
| } |
| return bswap_32(~crc); |
| } |
| |
| static int |
| bcm_setmac(char mac_addr1[6], char mac_addr2[6]) |
| { |
| uint64_t mac1 = 0, mac2 = 0; |
| uint32_t manu[MANUFACTURING_INFO_SIZE/4]; |
| int addr, i; |
| uint32_t crc, val1, val2, val3, val4; |
| |
| #ifdef BCM_DEBUG |
| printf("Flashing MAC 1: %02X:%02X:%02X:%02X:%02X:%02X\n", |
| ((unsigned int) mac_addr1[0]) & 0xFF, |
| ((unsigned int) mac_addr1[1]) & 0xFF, |
| ((unsigned int) mac_addr1[2]) & 0xFF, |
| ((unsigned int) mac_addr1[3]) & 0xFF, |
| ((unsigned int) mac_addr1[4]) & 0xFF, |
| ((unsigned int) mac_addr1[5]) & 0xFF); |
| |
| printf("Flashing MAC 2: %02X:%02X:%02X:%02X:%02X:%02X\n", |
| ((unsigned int) mac_addr2[0]) & 0xFF, |
| ((unsigned int) mac_addr2[1]) & 0xFF, |
| ((unsigned int) mac_addr2[2]) & 0xFF, |
| ((unsigned int) mac_addr2[3]) & 0xFF, |
| ((unsigned int) mac_addr2[4]) & 0xFF, |
| ((unsigned int) mac_addr2[5]) & 0xFF); |
| #endif |
| |
| mac1 |= ((uint64_t) mac_addr1[0]) & 0xFF; mac1 = mac1 << 8; |
| mac1 |= ((uint64_t) mac_addr1[1]) & 0xFF; mac1 = mac1 << 8; |
| mac1 |= ((uint64_t) mac_addr1[2]) & 0xFF; mac1 = mac1 << 8; |
| mac1 |= ((uint64_t) mac_addr1[3]) & 0xFF; mac1 = mac1 << 8; |
| mac1 |= ((uint64_t) mac_addr1[4]) & 0xFF; mac1 = mac1 << 8; |
| mac1 |= ((uint64_t) mac_addr1[5]) & 0xFF; |
| |
| mac2 |= ((uint64_t) mac_addr2[0]) & 0xFF; mac2 = mac2 << 8; |
| mac2 |= ((uint64_t) mac_addr2[1]) & 0xFF; mac2 = mac2 << 8; |
| mac2 |= ((uint64_t) mac_addr2[2]) & 0xFF; mac2 = mac2 << 8; |
| mac2 |= ((uint64_t) mac_addr2[3]) & 0xFF; mac2 = mac2 << 8; |
| mac2 |= ((uint64_t) mac_addr2[4]) & 0xFF; mac2 = mac2 << 8; |
| mac2 |= ((uint64_t) mac_addr2[5]) & 0xFF; |
| |
| /* Extract the manufacturing data, starts at 0x74 */ |
| if(bcm_nvram_lock() == -1) { |
| return -1; |
| } |
| |
| addr = 0x74; |
| for (i = 0; i < (MANUFACTURING_INFO_SIZE/4); i++) { |
| if (bcm_nvram_read(addr, &manu[i], 0) != 0) { |
| printf("\nREAD FAILED\n"); |
| bcm_nvram_unlock(); |
| return -1; |
| } |
| addr+=4; |
| } |
| bcm_nvram_unlock(); |
| |
| /* Store the new MAC address in the manufacturing data */ |
| val1 = mac1 >> 32; |
| val2 = mac1 & 0xFFFFFFFF; |
| val3 = mac2 >> 32; |
| val4 = mac2 & 0xFFFFFFFF; |
| manu[(0x7C-0x74)/4] = val1; |
| manu[(0x80-0x74)/4] = val2; |
| manu[(0xCC-0x74)/4] = val3; |
| manu[(0xD0-0x74)/4] = val4; |
| |
| /* Calculate the new manufacturing datas CRC */ |
| crc = util_gen_crc(((char *)manu), |
| MANUFACTURING_INFO_SIZE - 4, 0xFFFFFFFF); |
| |
| /* Now write the new MAC addresses and CRC */ |
| if ((bcm_nvram_write(0x7C, val1, 1) != 0) || |
| (bcm_nvram_write(0x80, val2, 1) != 0) || |
| (bcm_nvram_write(0xCC, val3, 1) != 0) || |
| (bcm_nvram_write(0xD0, val4, 1) != 0) || |
| (bcm_nvram_write(0xFC, crc, 1) != 0) ) |
| { |
| /* Disastor ! */ |
| #ifdef BCM_DEBUG |
| printf("failed to write MAC address\n"); |
| #endif |
| return -1; |
| } |
| |
| /* Success !!!! */ |
| return 0; |
| } |
| |
| static int |
| bcm_ioctl( int request, void* data ) |
| { |
| uint32_t l_baseaddrL_u32; |
| uint32_t l_baseaddrH_u32; |
| uint32_t i; |
| int ret_val = 0; |
| char mac_addr[6]; |
| ioctl_net_data_t *ioctl_data = (ioctl_net_data_t*) data; |
| |
| if(request != SIOCETHTOOL) { |
| return -1; |
| } |
| |
| #ifdef BCM_DEBUG |
| printf( "bcm57xx: detected device " ); |
| if( IS_5703 ) { |
| printf( "5703S" ); |
| } else if( IS_5704 ) { |
| printf( "5704" ); |
| if( IS_SERDES ) { |
| printf( "S\n" ); |
| } else { |
| printf( "C\n" ); |
| } |
| } else if( IS_5714 ) { |
| printf( "5714\n" ); |
| } |
| #endif |
| /* |
| * setup register & memory base addresses of NIC |
| */ |
| l_baseaddrL_u32 = (uint32_t) ~0xf & |
| SLOF_pci_config_read32(PCI_BAR1_R); |
| /*l_baseaddrL_u32 = ( (uint32_t) ~0xf & |
| (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_BAR1_R ) );*/ |
| |
| l_baseaddrH_u32 = SLOF_pci_config_read32(PCI_BAR2_R); |
| /*l_baseaddrH_u32 = |
| (uint32_t) snk_kernel_interface->pci_config_read( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_BAR2_R );*/ |
| |
| bcm_baseaddr_u64 = (uint64_t) l_baseaddrH_u32; |
| bcm_baseaddr_u64 <<= 32; |
| bcm_baseaddr_u64 += (uint64_t) l_baseaddrL_u32; |
| bcm_baseaddr_u64 = |
| (uint64_t)SLOF_translate_my_address((void *)bcm_baseaddr_u64); |
| /*snk_kernel_interface->translate_addr(((void *)&(bcm_baseaddr_u64)));*/ |
| bcm_memaddr_u64 = bcm_baseaddr_u64 + BCM_MEMORY_OFFS; |
| |
| /* |
| * 57xx hardware initialization |
| * BCM57xx Programmer's Guide: Section 8, "Initialization" |
| * steps 1 through 101 |
| */ |
| |
| // step 1: enable bus master & memory space in command reg |
| i = ( BIT32( 10 ) | BIT32( 2 ) | BIT32( 1 ) ); |
| SLOF_pci_config_write16(PCI_COM_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 2, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_COM_R, |
| ( int ) i );*/ |
| |
| // step 2: disable & mask interrupts & enable pci byte/word swapping & enable indirect addressing mode |
| i = ( BIT32( 7 ) | BIT32( 3 ) | BIT32( 2 ) | BIT32( 1 ) | BIT32( 0 ) ); |
| SLOF_pci_config_write32(PCI_MISC_HCTRL_R, i); |
| /*snk_kernel_interface->pci_config_write( bcm_pcicfg_puid, |
| 4, |
| bcm_pcicfg_bus, |
| bcm_pcicfg_devfn, |
| PCI_MISC_HCTRL_R, |
| ( int ) i );*/ |
| |
| bcm_nvram_init(); |
| |
| switch(ioctl_data->subcmd) { |
| case ETHTOOL_GMAC: |
| switch(ioctl_data->data.mac.idx) { |
| case 0: |
| ret_val = bcm_getmac(0x7C, ioctl_data->data.mac.address); |
| break; |
| case 1: |
| ret_val = bcm_getmac(0xCC, ioctl_data->data.mac.address); |
| break; |
| default: |
| ret_val = -1; |
| break; |
| } |
| break; |
| case ETHTOOL_SMAC: |
| switch(ioctl_data->data.mac.idx) { |
| case 0: |
| ret_val = bcm_getmac(0xCC, mac_addr); |
| if(ret_val == 0) |
| ret_val = bcm_setmac(ioctl_data->data.mac.address, mac_addr); |
| break; |
| case 1: |
| ret_val = bcm_getmac(0x7C, mac_addr); |
| if(ret_val == 0) |
| ret_val = bcm_setmac(mac_addr, ioctl_data->data.mac.address); |
| break; |
| default: |
| ret_val = -1; |
| break; |
| } |
| break; |
| case ETHTOOL_VERSION: { |
| char *text = ioctl_data->data.version.text; |
| memcpy(text, " BCM57xx Boot code level: ", 27); |
| ret_val = bcm_get_version(&text[27]); |
| break; |
| } |
| default: |
| ret_val = -1; |
| break; |
| } |
| |
| bcm_term(); |
| return ret_val; |
| } |
| |
| net_driver_t *bcm57xx_open(void) |
| { |
| net_driver_t *driver; |
| uint16_t vendor_id, device_id; |
| |
| vendor_id = SLOF_pci_config_read16(0); |
| device_id = SLOF_pci_config_read16(2); |
| if (check_driver(vendor_id, device_id)) |
| return NULL; |
| |
| driver = SLOF_alloc_mem(sizeof(*driver)); |
| if (!driver) { |
| printf("Unable to allocate virtio-net driver\n"); |
| return NULL; |
| } |
| memset(driver, 0, sizeof(*driver)); |
| |
| if (bcm_init(driver)) |
| goto FAIL; |
| |
| return driver; |
| |
| FAIL: SLOF_free_mem(driver, sizeof(*driver)); |
| return NULL; |
| |
| return 0; |
| } |
| |
| void bcm57xx_close(net_driver_t *driver) |
| { |
| if (driver->running == 0) |
| return; |
| |
| bcm_term(); |
| driver->running = 0; |
| SLOF_free_mem(driver, sizeof(*driver)); |
| } |
| |
| int bcm57xx_read(char *buf, int len) |
| { |
| if (buf) |
| return bcm_receive(buf, len); |
| return -1; |
| } |
| |
| int bcm57xx_write(char *buf, int len) |
| { |
| if (buf) |
| return bcm_xmit(buf, len); |
| return -1; |
| } |