mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-04 12:52:15 +00:00
Driver for PCI Ethernet NIC on Alpine V1 and V2.
Obtained from: Semihalf Submitted by: Michal Stanek <mst@semihalf.com> Sponsored by: Annapurna Labs Reviewed by: wma Differential Revision: https://reviews.freebsd.org/D7814
This commit is contained in:
parent
246d07a7a3
commit
7902c8dca8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=307670
@ -35,6 +35,9 @@ options INTRNG
|
||||
# Annapurna Alpine drivers
|
||||
device al_ccu # Alpine Cache Coherency Unit
|
||||
device al_nb_service # Alpine North Bridge Service
|
||||
device al_iofic # I/O Fabric Interrupt Controller
|
||||
device al_serdes # Serializer/Deserializer
|
||||
device al_udma # Universal DMA
|
||||
|
||||
# Pseudo devices
|
||||
device loop
|
||||
@ -69,6 +72,7 @@ device al_pci # Annapurna Alpine PCI-E
|
||||
device ether
|
||||
device mii
|
||||
device bpf
|
||||
device al_eth # Annapurna Alpine Ethernet NIC
|
||||
options DEVICE_POLLING
|
||||
|
||||
# USB ethernet support, requires miibus
|
||||
|
@ -94,6 +94,9 @@ options SOC_HISI_HI6220
|
||||
# Annapurna Alpine drivers
|
||||
device al_ccu # Alpine Cache Coherency Unit
|
||||
device al_nb_service # Alpine North Bridge Service
|
||||
device al_iofic # I/O Fabric Interrupt Controller
|
||||
device al_serdes # Serializer/Deserializer
|
||||
device al_udma # Universal DMA
|
||||
|
||||
# VirtIO support
|
||||
device virtio
|
||||
@ -119,6 +122,7 @@ device igb # Intel PRO/1000 PCIE Server Gigabit Family
|
||||
device ix # Intel 10Gb Ethernet Family
|
||||
device msk # Marvell/SysKonnect Yukon II Gigabit Ethernet
|
||||
device vnic # Cavium ThunderX NIC
|
||||
device al_eth # Annapurna Alpine Ethernet NIC
|
||||
|
||||
# Block devices
|
||||
device ahci
|
||||
|
@ -709,6 +709,45 @@ dev/aic7xxx/aic7xxx_93cx6.c optional ahc
|
||||
dev/aic7xxx/aic7xxx_osm.c optional ahc
|
||||
dev/aic7xxx/aic7xxx_pci.c optional ahc pci
|
||||
dev/aic7xxx/aic7xxx_reg_print.c optional ahc ahc_reg_pretty_print
|
||||
dev/al_eth/al_eth.c optional al_eth \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
dev/al_eth/al_init_eth_lm.c optional al_eth \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
dev/al_eth/al_init_eth_kr.c optional al_eth \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_hal_iofic.c optional al_iofic \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_hal_serdes_25g.c optional al_serdes \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_hal_serdes_hssp.c optional al_serdes \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_hal_udma_config.c optional al_udma \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_hal_udma_debug.c optional al_udma \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_hal_udma_iofic.c optional al_udma \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_hal_udma_main.c optional al_udma \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/al_serdes.c optional al_serdes \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/eth/al_hal_eth_kr.c optional al_eth \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
contrib/alpine-hal/eth/al_hal_eth_main.c optional al_eth \
|
||||
no-depend \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
dev/alc/if_alc.c optional alc pci
|
||||
dev/ale/if_ale.c optional ale pci
|
||||
dev/alpm/alpm.c optional alpm pci
|
||||
|
3584
sys/dev/al_eth/al_eth.c
Normal file
3584
sys/dev/al_eth/al_eth.c
Normal file
File diff suppressed because it is too large
Load Diff
366
sys/dev/al_eth/al_eth.h
Normal file
366
sys/dev/al_eth/al_eth.h
Normal file
@ -0,0 +1,366 @@
|
||||
/*-
|
||||
* Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by Semihalf.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef __AL_ETH_H__
|
||||
#define __AL_ETH_H__
|
||||
|
||||
#include "al_init_eth_lm.h"
|
||||
#include "al_hal_eth.h"
|
||||
#include "al_hal_udma_iofic.h"
|
||||
#include "al_hal_udma_debug.h"
|
||||
#include "al_serdes.h"
|
||||
|
||||
enum board_t {
|
||||
ALPINE_INTEGRATED = 0,
|
||||
ALPINE_NIC = 1,
|
||||
ALPINE_FPGA_NIC = 2,
|
||||
};
|
||||
|
||||
#define AL_ETH_MAX_HW_QUEUES 4
|
||||
#define AL_ETH_NUM_QUEUES 4
|
||||
#define AL_ETH_MAX_MSIX_VEC (1 + 2 * AL_ETH_MAX_HW_QUEUES)
|
||||
|
||||
#define AL_ETH_DEFAULT_TX_SW_DESCS (512)
|
||||
#define AL_ETH_DEFAULT_TX_HW_DESCS (512)
|
||||
#define AL_ETH_DEFAULT_RX_DESCS (512)
|
||||
|
||||
#if ((AL_ETH_DEFAULT_TX_SW_DESCS / 4) < (AL_ETH_PKT_MAX_BUFS + 2))
|
||||
#define AL_ETH_TX_WAKEUP_THRESH (AL_ETH_DEFAULT_TX_SW_DESCS / 4)
|
||||
#else
|
||||
#define AL_ETH_TX_WAKEUP_THRESH (AL_ETH_PKT_MAX_BUFS + 2)
|
||||
#endif
|
||||
|
||||
#define NET_IP_ALIGN 2
|
||||
#define AL_ETH_DEFAULT_SMALL_PACKET_LEN (128 - NET_IP_ALIGN)
|
||||
#define AL_ETH_HEADER_COPY_SIZE (128 - NET_IP_ALIGN)
|
||||
|
||||
#define AL_ETH_DEFAULT_MAX_RX_BUFF_ALLOC_SIZE 9216
|
||||
/*
|
||||
* Minimum the buffer size to 600 to avoid situation the mtu will be changed
|
||||
* from too little buffer to very big one and then the number of buffer per
|
||||
* packet could reach the maximum AL_ETH_PKT_MAX_BUFS
|
||||
*/
|
||||
#define AL_ETH_DEFAULT_MIN_RX_BUFF_ALLOC_SIZE 600
|
||||
#define AL_ETH_DEFAULT_FORCE_1000_BASEX FALSE
|
||||
|
||||
#define AL_ETH_DEFAULT_LINK_POLL_INTERVAL 100
|
||||
#define AL_ETH_FIRST_LINK_POLL_INTERVAL 1
|
||||
|
||||
#define AL_ETH_NAME_MAX_LEN 20
|
||||
#define AL_ETH_IRQNAME_SIZE 40
|
||||
|
||||
#define AL_ETH_DEFAULT_MDIO_FREQ_KHZ 2500
|
||||
#define AL_ETH_MDIO_FREQ_1000_KHZ 1000
|
||||
|
||||
struct al_eth_irq {
|
||||
driver_filter_t *handler;
|
||||
void *data;
|
||||
unsigned int vector;
|
||||
uint8_t requested;
|
||||
char name[AL_ETH_IRQNAME_SIZE];
|
||||
struct resource *res;
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
struct al_eth_tx_buffer {
|
||||
struct mbuf *m;
|
||||
struct al_eth_pkt hal_pkt;
|
||||
bus_dmamap_t dma_map;
|
||||
unsigned int tx_descs;
|
||||
};
|
||||
|
||||
struct al_eth_rx_buffer {
|
||||
struct mbuf *m;
|
||||
unsigned int data_size;
|
||||
bus_dmamap_t dma_map;
|
||||
struct al_buf al_buf;
|
||||
};
|
||||
|
||||
struct al_eth_ring {
|
||||
device_t dev;
|
||||
struct al_eth_adapter *adapter;
|
||||
/* Used to get rx packets from hal */
|
||||
struct al_eth_pkt hal_pkt;
|
||||
/* Udma queue handler */
|
||||
struct al_udma_q *dma_q;
|
||||
uint32_t ring_id;
|
||||
uint16_t next_to_use;
|
||||
uint16_t next_to_clean;
|
||||
/* The offset of the interrupt unmask register */
|
||||
uint32_t *unmask_reg_offset;
|
||||
/*
|
||||
* The value to write to the above register to
|
||||
* unmask the interrupt of this ring
|
||||
*/
|
||||
uint32_t unmask_val;
|
||||
struct al_eth_meta_data hal_meta;
|
||||
/* Contex of tx packet */
|
||||
struct al_eth_tx_buffer *tx_buffer_info;
|
||||
/* Contex of rx packet */
|
||||
struct al_eth_rx_buffer *rx_buffer_info;
|
||||
/* Number of tx/rx_buffer_info's entries */
|
||||
int sw_count;
|
||||
/* Number of hw descriptors */
|
||||
int hw_count;
|
||||
/* Size (in bytes) of hw descriptors */
|
||||
size_t descs_size;
|
||||
/* Size (in bytes) of hw completion descriptors, used for rx */
|
||||
size_t cdescs_size;
|
||||
struct ifnet *netdev;
|
||||
struct al_udma_q_params q_params;
|
||||
struct buf_ring *br;
|
||||
struct mtx br_mtx;
|
||||
struct task enqueue_task;
|
||||
struct taskqueue *enqueue_tq;
|
||||
volatile uint32_t enqueue_is_running;
|
||||
struct task cmpl_task;
|
||||
struct taskqueue *cmpl_tq;
|
||||
volatile uint32_t cmpl_is_running;
|
||||
uint32_t lro_enabled;
|
||||
struct lro_ctrl lro;
|
||||
bus_dma_tag_t dma_buf_tag;
|
||||
volatile uint32_t stall;
|
||||
};
|
||||
|
||||
#define AL_ETH_TX_RING_IDX_NEXT(tx_ring, idx) (((idx) + 1) & (AL_ETH_DEFAULT_TX_SW_DESCS - 1))
|
||||
|
||||
#define AL_ETH_RX_RING_IDX_NEXT(rx_ring, idx) (((idx) + 1) & (AL_ETH_DEFAULT_RX_DESCS - 1))
|
||||
#define AL_ETH_RX_RING_IDX_ADD(rx_ring, idx, n) (((idx) + (n)) & (AL_ETH_DEFAULT_RX_DESCS - 1))
|
||||
|
||||
/* flow control configuration */
|
||||
#define AL_ETH_FLOW_CTRL_RX_FIFO_TH_HIGH 0x160
|
||||
#define AL_ETH_FLOW_CTRL_RX_FIFO_TH_LOW 0x90
|
||||
#define AL_ETH_FLOW_CTRL_QUANTA 0xffff
|
||||
#define AL_ETH_FLOW_CTRL_QUANTA_TH 0x8000
|
||||
|
||||
#define AL_ETH_FLOW_CTRL_AUTONEG 1
|
||||
#define AL_ETH_FLOW_CTRL_RX_PAUSE 2
|
||||
#define AL_ETH_FLOW_CTRL_TX_PAUSE 4
|
||||
|
||||
/* link configuration for 1G port */
|
||||
struct al_eth_link_config {
|
||||
int old_link;
|
||||
/* Describes what we actually have. */
|
||||
int active_duplex;
|
||||
int active_speed;
|
||||
|
||||
/* current flow control status */
|
||||
uint8_t flow_ctrl_active;
|
||||
/* supported configuration (can be changed from ethtool) */
|
||||
uint8_t flow_ctrl_supported;
|
||||
|
||||
/* the following are not relevant to RGMII */
|
||||
boolean_t force_1000_base_x;
|
||||
boolean_t autoneg;
|
||||
};
|
||||
|
||||
/* SFP detection event */
|
||||
enum al_eth_sfp_detect_evt {
|
||||
/* No change (no connect, disconnect, or new SFP module */
|
||||
AL_ETH_SFP_DETECT_EVT_NO_CHANGE,
|
||||
/* SFP module connected */
|
||||
AL_ETH_SFP_DETECT_EVT_CONNECTED,
|
||||
/* SFP module disconnected */
|
||||
AL_ETH_SFP_DETECT_EVT_DISCONNECTED,
|
||||
/* SFP module replaced */
|
||||
AL_ETH_SFP_DETECT_EVT_CHANGED,
|
||||
};
|
||||
|
||||
/* SFP detection status */
|
||||
struct al_eth_sfp_detect_stat {
|
||||
/* Status is valid (i.e. rest of fields are valid) */
|
||||
boolean_t valid;
|
||||
boolean_t connected;
|
||||
uint8_t sfp_10g;
|
||||
uint8_t sfp_1g;
|
||||
uint8_t sfp_cable_tech;
|
||||
boolean_t lt_en;
|
||||
boolean_t an_en;
|
||||
enum al_eth_mac_mode mac_mode;
|
||||
};
|
||||
|
||||
struct al_eth_retimer_params {
|
||||
boolean_t exist;
|
||||
uint8_t bus_id;
|
||||
uint8_t i2c_addr;
|
||||
enum al_eth_retimer_channel channel;
|
||||
};
|
||||
|
||||
struct msix_entry {
|
||||
int entry;
|
||||
int vector;
|
||||
};
|
||||
|
||||
/* board specific private data structure */
|
||||
struct al_eth_adapter {
|
||||
enum board_t board_type;
|
||||
device_t miibus;
|
||||
struct mii_data *mii;
|
||||
uint16_t dev_id;
|
||||
uint8_t rev_id;
|
||||
|
||||
device_t dev;
|
||||
struct ifnet *netdev;
|
||||
struct ifmedia media;
|
||||
struct resource *udma_res;
|
||||
struct resource *mac_res;
|
||||
struct resource *ec_res;
|
||||
int if_flags;
|
||||
struct callout wd_callout;
|
||||
struct mtx wd_mtx;
|
||||
struct callout stats_callout;
|
||||
struct mtx stats_mtx;
|
||||
|
||||
/* this is for intx mode */
|
||||
void *irq_cookie;
|
||||
struct resource *irq_res;
|
||||
|
||||
/*
|
||||
* Some features need tri-state capability,
|
||||
* thus the additional *_CAPABLE flags.
|
||||
*/
|
||||
uint32_t flags;
|
||||
#define AL_ETH_FLAG_MSIX_CAPABLE (uint32_t)(1 << 1)
|
||||
#define AL_ETH_FLAG_MSIX_ENABLED (uint32_t)(1 << 2)
|
||||
#define AL_ETH_FLAG_IN_NETPOLL (uint32_t)(1 << 3)
|
||||
#define AL_ETH_FLAG_MQ_CAPABLE (uint32_t)(1 << 4)
|
||||
#define AL_ETH_FLAG_SRIOV_CAPABLE (uint32_t)(1 << 5)
|
||||
#define AL_ETH_FLAG_SRIOV_ENABLED (uint32_t)(1 << 6)
|
||||
#define AL_ETH_FLAG_RESET_REQUESTED (uint32_t)(1 << 7)
|
||||
|
||||
struct al_hal_eth_adapter hal_adapter;
|
||||
|
||||
/*
|
||||
* Rx packets that shorter that this len will be copied to the mbuf
|
||||
*/
|
||||
unsigned int small_copy_len;
|
||||
|
||||
/* Maximum size for rx buffer */
|
||||
unsigned int max_rx_buff_alloc_size;
|
||||
uint32_t rx_mbuf_sz;
|
||||
|
||||
/* Tx fast path data */
|
||||
int num_tx_queues;
|
||||
|
||||
/* Rx fast path data */
|
||||
int num_rx_queues;
|
||||
|
||||
/* TX */
|
||||
struct al_eth_ring tx_ring[AL_ETH_NUM_QUEUES];
|
||||
|
||||
/* RX */
|
||||
struct al_eth_ring rx_ring[AL_ETH_NUM_QUEUES];
|
||||
|
||||
enum al_iofic_mode int_mode;
|
||||
|
||||
#define AL_ETH_MGMT_IRQ_IDX 0
|
||||
#define AL_ETH_RXQ_IRQ_IDX(adapter, q) (1 + (q))
|
||||
#define AL_ETH_TXQ_IRQ_IDX(adapter, q) (1 + (adapter)->num_rx_queues + (q))
|
||||
struct al_eth_irq irq_tbl[AL_ETH_MAX_MSIX_VEC];
|
||||
struct msix_entry *msix_entries;
|
||||
int msix_vecs;
|
||||
int irq_vecs;
|
||||
|
||||
unsigned int tx_usecs, rx_usecs; /* interrupt coalescing */
|
||||
|
||||
unsigned int tx_ring_count;
|
||||
unsigned int tx_descs_count;
|
||||
unsigned int rx_ring_count;
|
||||
unsigned int rx_descs_count;
|
||||
|
||||
/* RSS */
|
||||
uint32_t toeplitz_hash_key[AL_ETH_RX_HASH_KEY_NUM];
|
||||
#define AL_ETH_RX_RSS_TABLE_SIZE AL_ETH_RX_THASH_TABLE_SIZE
|
||||
uint8_t rss_ind_tbl[AL_ETH_RX_RSS_TABLE_SIZE];
|
||||
|
||||
uint32_t msg_enable;
|
||||
struct al_eth_mac_stats mac_stats;
|
||||
|
||||
enum al_eth_mac_mode mac_mode;
|
||||
boolean_t mac_mode_set; /* Relevant only when 'auto_speed' is set */
|
||||
uint8_t mac_addr[ETHER_ADDR_LEN];
|
||||
/* mdio and phy*/
|
||||
boolean_t phy_exist;
|
||||
struct mii_bus *mdio_bus;
|
||||
struct phy_device *phydev;
|
||||
uint8_t phy_addr;
|
||||
struct al_eth_link_config link_config;
|
||||
|
||||
/* HAL layer data */
|
||||
int id_number;
|
||||
char name[AL_ETH_NAME_MAX_LEN];
|
||||
void *internal_pcie_base; /* use for ALPINE_NIC devices */
|
||||
void *udma_base;
|
||||
void *ec_base;
|
||||
void *mac_base;
|
||||
|
||||
struct al_eth_flow_control_params flow_ctrl_params;
|
||||
|
||||
struct al_eth_adapter_params eth_hal_params;
|
||||
|
||||
struct task link_status_task;
|
||||
uint32_t link_poll_interval; /* task interval in mSec */
|
||||
|
||||
boolean_t serdes_init;
|
||||
struct al_serdes_grp_obj serdes_obj;
|
||||
uint8_t serdes_grp;
|
||||
uint8_t serdes_lane;
|
||||
|
||||
boolean_t an_en; /* run kr auto-negotiation */
|
||||
boolean_t lt_en; /* run kr link-training */
|
||||
|
||||
boolean_t sfp_detection_needed; /* true if need to run sfp detection */
|
||||
boolean_t auto_speed; /* true if allowed to change SerDes speed configuration */
|
||||
uint8_t i2c_adapter_id; /* identifier for the i2c adapter to use to access SFP+ module */
|
||||
enum al_eth_ref_clk_freq ref_clk_freq; /* reference clock frequency */
|
||||
unsigned int mdio_freq; /* MDIO frequency [Khz] */
|
||||
|
||||
boolean_t up;
|
||||
|
||||
boolean_t last_link;
|
||||
boolean_t last_establish_failed;
|
||||
struct al_eth_lm_context lm_context;
|
||||
boolean_t use_lm;
|
||||
|
||||
boolean_t dont_override_serdes; /* avoid overriding serdes parameters
|
||||
to preset static values */
|
||||
struct mtx serdes_config_lock;
|
||||
struct mtx if_rx_lock;
|
||||
|
||||
uint32_t wol;
|
||||
|
||||
struct al_eth_retimer_params retimer;
|
||||
|
||||
bool phy_fixup_needed;
|
||||
|
||||
enum al_eth_lm_max_speed max_speed;
|
||||
};
|
||||
|
||||
#endif /* !(AL_ETH_H) */
|
841
sys/dev/al_eth/al_init_eth_kr.c
Normal file
841
sys/dev/al_eth/al_init_eth_kr.c
Normal file
@ -0,0 +1,841 @@
|
||||
/*-
|
||||
* Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by Semihalf.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "al_init_eth_kr.h"
|
||||
#include "al_serdes.h"
|
||||
|
||||
/**
|
||||
* Ethernet
|
||||
* @{
|
||||
* @file al_init_eth_kr.c
|
||||
*
|
||||
* @brief auto-negotiation and link training algorithms and state machines
|
||||
*
|
||||
* The link training algorithm implemented in this file going over the
|
||||
* coefficients and looking for the best eye measurement possible for every one
|
||||
* of them. it's using state machine to move between the different states.
|
||||
* the state machine has 3 parts:
|
||||
* - preparation - waiting till the link partner (lp) will be ready and
|
||||
* change his state to preset.
|
||||
* - measurement (per coefficient) - issue decrement for the coefficient
|
||||
* under control till the eye measurement not increasing
|
||||
* and remains in the optimum.
|
||||
* - completion - indicate the receiver is ready and wait for the lp to
|
||||
* finish his work.
|
||||
*/
|
||||
|
||||
/* TODO: fix with more reasonable numbers */
|
||||
/* timeout in mSec before auto-negotiation will be terminated */
|
||||
#define AL_ETH_KR_AN_TIMEOUT (500)
|
||||
#define AL_ETH_KR_EYE_MEASURE_TIMEOUT (100)
|
||||
/* timeout in uSec before the process will be terminated */
|
||||
#define AL_ETH_KR_FRAME_LOCK_TIMEOUT (500 * 1000)
|
||||
#define AL_ETH_KR_LT_DONE_TIMEOUT (500 * 1000)
|
||||
/* number of times the receiver and transmitter tasks will be called before the
|
||||
* algorithm will be terminated */
|
||||
#define AL_ETH_KR_LT_MAX_ROUNDS (50000)
|
||||
|
||||
/* mac algorithm state machine */
|
||||
enum al_eth_kr_mac_lt_state {
|
||||
TX_INIT = 0, /* start of all */
|
||||
WAIT_BEGIN, /* wait for initial training lock */
|
||||
DO_PRESET, /* issue PRESET to link partner */
|
||||
DO_HOLD, /* issue HOLD to link partner */
|
||||
/* preparation is done, start testing the coefficient. */
|
||||
QMEASURE, /* EyeQ measurement. */
|
||||
QCHECK, /* Check if measurement shows best value. */
|
||||
DO_NEXT_TRY, /* issue DEC command to coeff for next measurement. */
|
||||
END_STEPS, /* perform last steps to go back to optimum. */
|
||||
END_STEPS_HOLD, /* perform last steps HOLD command. */
|
||||
COEFF_DONE, /* done with the current coefficient updates.
|
||||
* Check if another should be done. */
|
||||
/* end of training to all coefficients */
|
||||
SET_READY, /* indicate local receiver ready */
|
||||
TX_DONE /* transmit process completed, training can end. */
|
||||
};
|
||||
|
||||
static const char * const al_eth_kr_mac_sm_name[] = {
|
||||
"TX_INIT", "WAIT_BEGIN", "DO_PRESET",
|
||||
"DO_HOLD", "QMEASURE", "QCHECK",
|
||||
"DO_NEXT_TRY", "END_STEPS", "END_STEPS_HOLD",
|
||||
"COEFF_DONE", "SET_READY", "TX_DONE"
|
||||
};
|
||||
|
||||
/* Constants used for the measurement. */
|
||||
enum al_eth_kr_coef {
|
||||
AL_ETH_KR_COEF_C_MINUS,
|
||||
AL_ETH_KR_COEF_C_ZERO,
|
||||
AL_ETH_KR_COEF_C_PLUS,
|
||||
};
|
||||
|
||||
/*
|
||||
* test coefficients from COEFF_TO_MANIPULATE to COEFF_TO_MANIPULATE_LAST.
|
||||
*/
|
||||
#define COEFF_TO_MANIPULATE AL_ETH_KR_COEF_C_MINUS
|
||||
#define COEFF_TO_MANIPULATE_LAST AL_ETH_KR_COEF_C_MINUS
|
||||
#define QARRAY_SIZE 3 /**< how many entries we want in our history array. */
|
||||
|
||||
struct al_eth_kr_data {
|
||||
struct al_hal_eth_adapter *adapter;
|
||||
struct al_serdes_grp_obj *serdes_obj;
|
||||
enum al_serdes_lane lane;
|
||||
|
||||
/* Receiver side data */
|
||||
struct al_eth_kr_status_report_data status_report; /* report to response */
|
||||
struct al_eth_kr_coef_up_data last_lpcoeff; /* last coeff received */
|
||||
|
||||
/* Transmitter side data */
|
||||
enum al_eth_kr_mac_lt_state algo_state; /* Statemachine. */
|
||||
unsigned int qarray[QARRAY_SIZE]; /* EyeQ measurements history */
|
||||
/* How many entries in the array are valid for compares yet. */
|
||||
unsigned int qarray_cnt;
|
||||
enum al_eth_kr_coef curr_coeff;
|
||||
/*
|
||||
* Status of coefficient during the last
|
||||
* DEC/INC command (before issuing HOLD again).
|
||||
*/
|
||||
unsigned int coeff_status_step;
|
||||
unsigned int end_steps_cnt; /* Number of end steps needed */
|
||||
};
|
||||
|
||||
static int
|
||||
al_eth_kr_an_run(struct al_eth_kr_data *kr_data, struct al_eth_an_adv *an_adv,
|
||||
struct al_eth_an_adv *an_partner_adv)
|
||||
{
|
||||
int rc;
|
||||
boolean_t page_received = FALSE;
|
||||
boolean_t an_completed = FALSE;
|
||||
boolean_t error = FALSE;
|
||||
int timeout = AL_ETH_KR_AN_TIMEOUT;
|
||||
|
||||
rc = al_eth_kr_an_init(kr_data->adapter, an_adv);
|
||||
if (rc != 0) {
|
||||
al_err("%s %s autonegotiation init failed\n",
|
||||
kr_data->adapter->name, __func__);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
rc = al_eth_kr_an_start(kr_data->adapter, AL_ETH_AN__LT_LANE_0,
|
||||
FALSE, TRUE);
|
||||
if (rc != 0) {
|
||||
al_err("%s %s autonegotiation enable failed\n",
|
||||
kr_data->adapter->name, __func__);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
do {
|
||||
DELAY(10000);
|
||||
timeout -= 10;
|
||||
if (timeout <= 0) {
|
||||
al_info("%s %s autonegotiation failed on timeout\n",
|
||||
kr_data->adapter->name, __func__);
|
||||
|
||||
return (ETIMEDOUT);
|
||||
}
|
||||
|
||||
al_eth_kr_an_status_check(kr_data->adapter, &page_received,
|
||||
&an_completed, &error);
|
||||
} while (page_received == FALSE);
|
||||
|
||||
if (error != 0) {
|
||||
al_info("%s %s autonegotiation failed (status error)\n",
|
||||
kr_data->adapter->name, __func__);
|
||||
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
al_eth_kr_an_read_adv(kr_data->adapter, an_partner_adv);
|
||||
|
||||
al_dbg("%s %s autonegotiation completed. error = %d\n",
|
||||
kr_data->adapter->name, __func__, error);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/***************************** receiver side *********************************/
|
||||
static enum al_eth_kr_cl72_cstate
|
||||
al_eth_lt_coeff_set(struct al_eth_kr_data *kr_data,
|
||||
enum al_serdes_tx_deemph_param param, uint32_t op)
|
||||
{
|
||||
enum al_eth_kr_cl72_cstate status = 0;
|
||||
|
||||
switch (op) {
|
||||
case AL_PHY_KR_COEF_UP_HOLD:
|
||||
/* no need to update the serdes - return not updated*/
|
||||
status = C72_CSTATE_NOT_UPDATED;
|
||||
break;
|
||||
case AL_PHY_KR_COEF_UP_INC:
|
||||
status = C72_CSTATE_UPDATED;
|
||||
|
||||
if (kr_data->serdes_obj->tx_deemph_inc(
|
||||
kr_data->serdes_obj,
|
||||
kr_data->lane,
|
||||
param) == 0)
|
||||
status = C72_CSTATE_MAX;
|
||||
break;
|
||||
case AL_PHY_KR_COEF_UP_DEC:
|
||||
status = C72_CSTATE_UPDATED;
|
||||
|
||||
if (kr_data->serdes_obj->tx_deemph_dec(
|
||||
kr_data->serdes_obj,
|
||||
kr_data->lane,
|
||||
param) == 0)
|
||||
status = C72_CSTATE_MIN;
|
||||
break;
|
||||
default: /* 3=reserved */
|
||||
break;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Inspect the received coefficient update request and update all coefficients
|
||||
* in the serdes accordingly.
|
||||
*/
|
||||
static void
|
||||
al_eth_coeff_req_handle(struct al_eth_kr_data *kr_data,
|
||||
struct al_eth_kr_coef_up_data *lpcoeff)
|
||||
{
|
||||
struct al_eth_kr_status_report_data *report = &kr_data->status_report;
|
||||
|
||||
/* First check for Init and Preset commands. */
|
||||
if ((lpcoeff->preset != 0) || (lpcoeff->initialize) != 0) {
|
||||
kr_data->serdes_obj->tx_deemph_preset(
|
||||
kr_data->serdes_obj,
|
||||
kr_data->lane);
|
||||
|
||||
/*
|
||||
* in case of preset c(0) should be set to maximum and both c(1)
|
||||
* and c(-1) should be updated
|
||||
*/
|
||||
report->c_minus = C72_CSTATE_UPDATED;
|
||||
|
||||
report->c_plus = C72_CSTATE_UPDATED;
|
||||
|
||||
report->c_zero = C72_CSTATE_MAX;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* in case preset and initialize are false need to perform per
|
||||
* coefficient action.
|
||||
*/
|
||||
report->c_minus = al_eth_lt_coeff_set(kr_data,
|
||||
AL_SERDES_TX_DEEMP_C_MINUS, lpcoeff->c_minus);
|
||||
|
||||
report->c_zero = al_eth_lt_coeff_set(kr_data,
|
||||
AL_SERDES_TX_DEEMP_C_ZERO, lpcoeff->c_zero);
|
||||
|
||||
report->c_plus = al_eth_lt_coeff_set(kr_data,
|
||||
AL_SERDES_TX_DEEMP_C_PLUS, lpcoeff->c_plus);
|
||||
|
||||
al_dbg("%s: c(0) = 0x%x c(-1) = 0x%x c(1) = 0x%x\n",
|
||||
__func__, report->c_zero, report->c_plus, report->c_minus);
|
||||
}
|
||||
|
||||
static void
|
||||
al_eth_kr_lt_receiver_task_init(struct al_eth_kr_data *kr_data)
|
||||
{
|
||||
|
||||
al_memset(&kr_data->last_lpcoeff, 0,
|
||||
sizeof(struct al_eth_kr_coef_up_data));
|
||||
al_memset(&kr_data->status_report, 0,
|
||||
sizeof(struct al_eth_kr_status_report_data));
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
al_eth_lp_coeff_up_change(struct al_eth_kr_data *kr_data,
|
||||
struct al_eth_kr_coef_up_data *lpcoeff)
|
||||
{
|
||||
struct al_eth_kr_coef_up_data *last_lpcoeff = &kr_data->last_lpcoeff;
|
||||
|
||||
if (al_memcmp(last_lpcoeff, lpcoeff,
|
||||
sizeof(struct al_eth_kr_coef_up_data)) == 0) {
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
al_memcpy(last_lpcoeff, lpcoeff, sizeof(struct al_eth_kr_coef_up_data));
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the receiver task for one cycle.
|
||||
* The receiver task continuously inspects the received coefficient update
|
||||
* requests and acts upon.
|
||||
*
|
||||
* @return <0 if error occur
|
||||
*/
|
||||
static int
|
||||
al_eth_kr_lt_receiver_task_run(struct al_eth_kr_data *kr_data)
|
||||
{
|
||||
struct al_eth_kr_coef_up_data new_lpcoeff;
|
||||
|
||||
/*
|
||||
* First inspect status of the link. It may have dropped frame lock as
|
||||
* the remote did some reconfiguration of its serdes.
|
||||
* Then we simply have nothing to do and return immediately as caller
|
||||
* will call us continuously until lock comes back.
|
||||
*/
|
||||
|
||||
if (al_eth_kr_receiver_frame_lock_get(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0) != 0) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* check if a new update command was received */
|
||||
al_eth_lp_coeff_up_get(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0, &new_lpcoeff);
|
||||
|
||||
if (al_eth_lp_coeff_up_change(kr_data, &new_lpcoeff) != 0) {
|
||||
/* got some new coefficient update request. */
|
||||
al_eth_coeff_req_handle(kr_data, &new_lpcoeff);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/******************************** transmitter side ***************************/
|
||||
static int
|
||||
al_eth_kr_lt_transmitter_task_init(struct al_eth_kr_data *kr_data)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
unsigned int temp_val;
|
||||
|
||||
for (i = 0; i < QARRAY_SIZE; i++)
|
||||
kr_data->qarray[i] = 0;
|
||||
|
||||
kr_data->qarray_cnt = 0;
|
||||
kr_data->algo_state = TX_INIT;
|
||||
kr_data->curr_coeff = COEFF_TO_MANIPULATE; /* first coeff to test. */
|
||||
kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED;
|
||||
kr_data->end_steps_cnt = QARRAY_SIZE-1; /* go back to first entry */
|
||||
|
||||
/*
|
||||
* Perform measure eye here to run the rx equalizer
|
||||
* for the first time to get init values
|
||||
*/
|
||||
rc = kr_data->serdes_obj->eye_measure_run(
|
||||
kr_data->serdes_obj,
|
||||
kr_data->lane,
|
||||
AL_ETH_KR_EYE_MEASURE_TIMEOUT,
|
||||
&temp_val);
|
||||
if (rc != 0) {
|
||||
al_warn("%s: Failed to run Rx equalizer (rc = 0x%x)\n",
|
||||
__func__, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
al_eth_kr_lt_all_not_updated(struct al_eth_kr_status_report_data *report)
|
||||
{
|
||||
|
||||
if ((report->c_zero == C72_CSTATE_NOT_UPDATED) &&
|
||||
(report->c_minus == C72_CSTATE_NOT_UPDATED) &&
|
||||
(report->c_plus == C72_CSTATE_NOT_UPDATED)) {
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
al_eth_kr_lt_coef_set(struct al_eth_kr_coef_up_data *ldcoeff,
|
||||
enum al_eth_kr_coef coef, enum al_eth_kr_cl72_coef_op op)
|
||||
{
|
||||
|
||||
switch (coef) {
|
||||
case AL_ETH_KR_COEF_C_MINUS:
|
||||
ldcoeff->c_minus = op;
|
||||
break;
|
||||
case AL_ETH_KR_COEF_C_PLUS:
|
||||
ldcoeff->c_plus = op;
|
||||
break;
|
||||
case AL_ETH_KR_COEF_C_ZERO:
|
||||
ldcoeff->c_zero = op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static enum al_eth_kr_cl72_cstate
|
||||
al_eth_kr_lt_coef_report_get(struct al_eth_kr_status_report_data *report,
|
||||
enum al_eth_kr_coef coef)
|
||||
{
|
||||
|
||||
switch (coef) {
|
||||
case AL_ETH_KR_COEF_C_MINUS:
|
||||
return (report->c_minus);
|
||||
case AL_ETH_KR_COEF_C_PLUS:
|
||||
return (report->c_plus);
|
||||
case AL_ETH_KR_COEF_C_ZERO:
|
||||
return (report->c_zero);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the transmitter_task for one cycle.
|
||||
*
|
||||
* @return <0 if error occurs
|
||||
*/
|
||||
static int
|
||||
al_eth_kr_lt_transmitter_task_run(struct al_eth_kr_data *kr_data)
|
||||
{
|
||||
struct al_eth_kr_status_report_data report;
|
||||
unsigned int coeff_status_cur;
|
||||
struct al_eth_kr_coef_up_data ldcoeff = { 0, 0, 0, 0, 0 };
|
||||
unsigned int val;
|
||||
int i;
|
||||
enum al_eth_kr_mac_lt_state nextstate;
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* do nothing if currently there is no frame lock (which may happen
|
||||
* when remote updates its analogs).
|
||||
*/
|
||||
if (al_eth_kr_receiver_frame_lock_get(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0) == 0) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
al_eth_lp_status_report_get(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0, &report);
|
||||
|
||||
/* extract curr status of the coefficient in use */
|
||||
coeff_status_cur = al_eth_kr_lt_coef_report_get(&report,
|
||||
kr_data->curr_coeff);
|
||||
|
||||
nextstate = kr_data->algo_state; /* default we stay in curr state; */
|
||||
|
||||
switch (kr_data->algo_state) {
|
||||
case TX_INIT:
|
||||
/* waiting for start */
|
||||
if (al_eth_kr_startup_proto_prog_get(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0) != 0) {
|
||||
/* training is on and frame lock */
|
||||
nextstate = WAIT_BEGIN;
|
||||
}
|
||||
break;
|
||||
case WAIT_BEGIN:
|
||||
kr_data->qarray_cnt = 0;
|
||||
kr_data->curr_coeff = COEFF_TO_MANIPULATE;
|
||||
kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED;
|
||||
coeff_status_cur = C72_CSTATE_NOT_UPDATED;
|
||||
kr_data->end_steps_cnt = QARRAY_SIZE-1;
|
||||
|
||||
/* Wait for not_updated for all coefficients from remote */
|
||||
if (al_eth_kr_lt_all_not_updated(&report) != 0) {
|
||||
ldcoeff.preset = TRUE;
|
||||
nextstate = DO_PRESET;
|
||||
}
|
||||
break;
|
||||
case DO_PRESET:
|
||||
/*
|
||||
* Send PRESET and wait for for updated for all
|
||||
* coefficients from remote
|
||||
*/
|
||||
if (al_eth_kr_lt_all_not_updated(&report) == 0)
|
||||
nextstate = DO_HOLD;
|
||||
else /* as long as the lp didn't response to the preset
|
||||
* we should continue sending it */
|
||||
ldcoeff.preset = TRUE;
|
||||
break;
|
||||
case DO_HOLD:
|
||||
/*
|
||||
* clear the PRESET, issue HOLD command and wait for
|
||||
* hold handshake
|
||||
*/
|
||||
if (al_eth_kr_lt_all_not_updated(&report) != 0)
|
||||
nextstate = QMEASURE;
|
||||
break;
|
||||
|
||||
case QMEASURE:
|
||||
/* makes a measurement and fills the new value into the array */
|
||||
rc = kr_data->serdes_obj->eye_measure_run(
|
||||
kr_data->serdes_obj,
|
||||
kr_data->lane,
|
||||
AL_ETH_KR_EYE_MEASURE_TIMEOUT,
|
||||
&val);
|
||||
if (rc != 0) {
|
||||
al_warn("%s: Rx eye measurement failed\n", __func__);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
al_dbg("%s: Rx Measure eye returned 0x%x\n", __func__, val);
|
||||
|
||||
/* put the new value into the array at the top. */
|
||||
for (i = 0; i < QARRAY_SIZE-1; i++)
|
||||
kr_data->qarray[i] = kr_data->qarray[i+1];
|
||||
|
||||
kr_data->qarray[QARRAY_SIZE-1] = val;
|
||||
|
||||
if (kr_data->qarray_cnt < QARRAY_SIZE)
|
||||
kr_data->qarray_cnt++;
|
||||
|
||||
nextstate = QCHECK;
|
||||
break;
|
||||
case QCHECK:
|
||||
/* check if we reached the best link quality yet. */
|
||||
if (kr_data->qarray_cnt < QARRAY_SIZE) {
|
||||
/* keep going until at least the history is
|
||||
* filled. check that we can keep going or if
|
||||
* coefficient has already reached minimum.
|
||||
*/
|
||||
|
||||
if (kr_data->coeff_status_step == C72_CSTATE_MIN)
|
||||
nextstate = COEFF_DONE;
|
||||
else {
|
||||
/*
|
||||
* request a DECREMENT of the
|
||||
* coefficient under control
|
||||
*/
|
||||
al_eth_kr_lt_coef_set(&ldcoeff,
|
||||
kr_data->curr_coeff, AL_PHY_KR_COEF_UP_DEC);
|
||||
|
||||
nextstate = DO_NEXT_TRY;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* check if current value and last both are worse than
|
||||
* the 2nd last. This we take as an ending condition
|
||||
* assuming the minimum was reached two tries before
|
||||
* so we will now go back to that point.
|
||||
*/
|
||||
if ((kr_data->qarray[0] < kr_data->qarray[1]) &&
|
||||
(kr_data->qarray[0] < kr_data->qarray[2])) {
|
||||
/*
|
||||
* request a INCREMENT of the
|
||||
* coefficient under control
|
||||
*/
|
||||
al_eth_kr_lt_coef_set(&ldcoeff,
|
||||
kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC);
|
||||
|
||||
/* start going back to the maximum */
|
||||
nextstate = END_STEPS;
|
||||
if (kr_data->end_steps_cnt > 0)
|
||||
kr_data->end_steps_cnt--;
|
||||
} else {
|
||||
if (kr_data->coeff_status_step ==
|
||||
C72_CSTATE_MIN) {
|
||||
nextstate = COEFF_DONE;
|
||||
} else {
|
||||
/*
|
||||
* request a DECREMENT of the
|
||||
* coefficient under control
|
||||
*/
|
||||
al_eth_kr_lt_coef_set(&ldcoeff,
|
||||
kr_data->curr_coeff,
|
||||
AL_PHY_KR_COEF_UP_DEC);
|
||||
|
||||
nextstate = DO_NEXT_TRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DO_NEXT_TRY:
|
||||
/*
|
||||
* save the status when we issue the DEC step to the remote,
|
||||
* before the HOLD is done again.
|
||||
*/
|
||||
kr_data->coeff_status_step = coeff_status_cur;
|
||||
|
||||
if (coeff_status_cur != C72_CSTATE_NOT_UPDATED)
|
||||
nextstate = DO_HOLD; /* go to next measurement round */
|
||||
else
|
||||
al_eth_kr_lt_coef_set(&ldcoeff,
|
||||
kr_data->curr_coeff, AL_PHY_KR_COEF_UP_DEC);
|
||||
break;
|
||||
/*
|
||||
* Coefficient iteration completed, go back to the optimum step
|
||||
* In this algorithm we assume 2 before curr was best hence need to do
|
||||
* two INC runs.
|
||||
*/
|
||||
case END_STEPS:
|
||||
if (coeff_status_cur != C72_CSTATE_NOT_UPDATED)
|
||||
nextstate = END_STEPS_HOLD;
|
||||
else
|
||||
al_eth_kr_lt_coef_set(&ldcoeff,
|
||||
kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC);
|
||||
break;
|
||||
case END_STEPS_HOLD:
|
||||
if (coeff_status_cur == C72_CSTATE_NOT_UPDATED) {
|
||||
if (kr_data->end_steps_cnt != 0) {
|
||||
/*
|
||||
* request a INCREMENT of the
|
||||
* coefficient under control
|
||||
*/
|
||||
al_eth_kr_lt_coef_set(&ldcoeff,
|
||||
kr_data->curr_coeff, AL_PHY_KR_COEF_UP_INC);
|
||||
|
||||
/* go 2nd time - dec the end step count */
|
||||
nextstate = END_STEPS;
|
||||
|
||||
if (kr_data->end_steps_cnt > 0)
|
||||
kr_data->end_steps_cnt--;
|
||||
|
||||
} else {
|
||||
nextstate = COEFF_DONE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case COEFF_DONE:
|
||||
/*
|
||||
* now this coefficient is done.
|
||||
* We can now either choose to finish here,
|
||||
* or keep going with another coefficient.
|
||||
*/
|
||||
if ((int)kr_data->curr_coeff < COEFF_TO_MANIPULATE_LAST) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < QARRAY_SIZE; i++)
|
||||
kr_data->qarray[i] = 0;
|
||||
|
||||
kr_data->qarray_cnt = 0;
|
||||
kr_data->end_steps_cnt = QARRAY_SIZE-1;
|
||||
kr_data->coeff_status_step = C72_CSTATE_NOT_UPDATED;
|
||||
kr_data->curr_coeff++;
|
||||
|
||||
al_dbg("[%s]: doing next coefficient: %d ---\n\n",
|
||||
kr_data->adapter->name, kr_data->curr_coeff);
|
||||
|
||||
nextstate = QMEASURE;
|
||||
} else {
|
||||
nextstate = SET_READY;
|
||||
}
|
||||
break;
|
||||
case SET_READY:
|
||||
/*
|
||||
* our receiver is ready for data.
|
||||
* no training will occur any more.
|
||||
*/
|
||||
kr_data->status_report.receiver_ready = TRUE;
|
||||
/*
|
||||
* in addition to the status we transmit, we also must tell our
|
||||
* local hardware state-machine that we are done, so the
|
||||
* training can eventually complete when the remote indicates
|
||||
* it is ready also. The hardware will then automatically
|
||||
* give control to the PCS layer completing training.
|
||||
*/
|
||||
al_eth_receiver_ready_set(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0);
|
||||
|
||||
nextstate = TX_DONE;
|
||||
break;
|
||||
case TX_DONE:
|
||||
break; /* nothing else to do */
|
||||
default:
|
||||
nextstate = kr_data->algo_state;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The status we want to transmit to remote.
|
||||
* Note that the status combines the receiver status of all coefficients
|
||||
* with the transmitter's rx ready status.
|
||||
*/
|
||||
if (kr_data->algo_state != nextstate) {
|
||||
al_dbg("[%s] [al_eth_kr_lt_transmit_run] STM changes %s -> %s: "
|
||||
" Qarray=%d/%d/%d\n", kr_data->adapter->name,
|
||||
al_eth_kr_mac_sm_name[kr_data->algo_state],
|
||||
al_eth_kr_mac_sm_name[nextstate],
|
||||
kr_data->qarray[0], kr_data->qarray[1], kr_data->qarray[2]);
|
||||
}
|
||||
|
||||
kr_data->algo_state = nextstate;
|
||||
|
||||
/*
|
||||
* write fields for transmission into hardware.
|
||||
* Important: this must be done always, as the receiver may have
|
||||
* received update commands and wants to return its status.
|
||||
*/
|
||||
al_eth_ld_coeff_up_set(kr_data->adapter, AL_ETH_AN__LT_LANE_0, &ldcoeff);
|
||||
al_eth_ld_status_report_set(kr_data->adapter, AL_ETH_AN__LT_LANE_0,
|
||||
&kr_data->status_report);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
al_eth_kr_run_lt(struct al_eth_kr_data *kr_data)
|
||||
{
|
||||
unsigned int cnt;
|
||||
int ret = 0;
|
||||
boolean_t page_received = FALSE;
|
||||
boolean_t an_completed = FALSE;
|
||||
boolean_t error = FALSE;
|
||||
boolean_t training_failure = FALSE;
|
||||
|
||||
al_eth_kr_lt_initialize(kr_data->adapter, AL_ETH_AN__LT_LANE_0);
|
||||
|
||||
if (al_eth_kr_lt_frame_lock_wait(kr_data->adapter, AL_ETH_AN__LT_LANE_0,
|
||||
AL_ETH_KR_FRAME_LOCK_TIMEOUT) == TRUE) {
|
||||
|
||||
/*
|
||||
* when locked, for the first time initialize the receiver and
|
||||
* transmitter tasks to prepare it for detecting coefficient
|
||||
* update requests.
|
||||
*/
|
||||
al_eth_kr_lt_receiver_task_init(kr_data);
|
||||
ret = al_eth_kr_lt_transmitter_task_init(kr_data);
|
||||
if (ret != 0)
|
||||
goto error;
|
||||
|
||||
cnt = 0;
|
||||
do {
|
||||
ret = al_eth_kr_lt_receiver_task_run(kr_data);
|
||||
if (ret != 0)
|
||||
break; /* stop the link training */
|
||||
|
||||
ret = al_eth_kr_lt_transmitter_task_run(kr_data);
|
||||
if (ret != 0)
|
||||
break; /* stop the link training */
|
||||
|
||||
cnt++;
|
||||
DELAY(100);
|
||||
|
||||
} while ((al_eth_kr_startup_proto_prog_get(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0)) && (cnt <= AL_ETH_KR_LT_MAX_ROUNDS));
|
||||
|
||||
training_failure =
|
||||
al_eth_kr_training_status_fail_get(kr_data->adapter,
|
||||
AL_ETH_AN__LT_LANE_0);
|
||||
al_dbg("[%s] training ended after %d rounds, failed = %s\n",
|
||||
kr_data->adapter->name, cnt,
|
||||
(training_failure) ? "Yes" : "No");
|
||||
if (training_failure || cnt > AL_ETH_KR_LT_MAX_ROUNDS) {
|
||||
al_warn("[%s] Training Fail: status: %s, timeout: %s\n",
|
||||
kr_data->adapter->name,
|
||||
(training_failure) ? "Failed" : "OK",
|
||||
(cnt > AL_ETH_KR_LT_MAX_ROUNDS) ? "Yes" : "No");
|
||||
|
||||
/*
|
||||
* note: link is now disabled,
|
||||
* until training becomes disabled (see below).
|
||||
*/
|
||||
ret = EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
al_info("[%s] FAILED: did not achieve initial frame lock...\n",
|
||||
kr_data->adapter->name);
|
||||
|
||||
ret = EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* ensure to stop link training at the end to allow normal PCS
|
||||
* datapath to operate in case of training failure.
|
||||
*/
|
||||
al_eth_kr_lt_stop(kr_data->adapter, AL_ETH_AN__LT_LANE_0);
|
||||
|
||||
cnt = AL_ETH_KR_LT_DONE_TIMEOUT;
|
||||
while (an_completed == FALSE) {
|
||||
al_eth_kr_an_status_check(kr_data->adapter, &page_received,
|
||||
&an_completed, &error);
|
||||
DELAY(1);
|
||||
if ((cnt--) == 0) {
|
||||
al_info("%s: wait for an complete timeout!\n", __func__);
|
||||
ret = ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
al_eth_kr_an_stop(kr_data->adapter);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* execute Autonegotiation process */
|
||||
int al_eth_an_lt_execute(struct al_hal_eth_adapter *adapter,
|
||||
struct al_serdes_grp_obj *serdes_obj,
|
||||
enum al_serdes_lane lane,
|
||||
struct al_eth_an_adv *an_adv,
|
||||
struct al_eth_an_adv *partner_adv)
|
||||
{
|
||||
struct al_eth_kr_data kr_data;
|
||||
int rc;
|
||||
struct al_serdes_adv_rx_params rx_params;
|
||||
|
||||
al_memset(&kr_data, 0, sizeof(struct al_eth_kr_data));
|
||||
|
||||
kr_data.adapter = adapter;
|
||||
kr_data.serdes_obj = serdes_obj;
|
||||
kr_data.lane = lane;
|
||||
|
||||
/*
|
||||
* the link training progress will run rx equalization so need to make
|
||||
* sure rx parameters is not been override
|
||||
*/
|
||||
rx_params.override = FALSE;
|
||||
kr_data.serdes_obj->rx_advanced_params_set(
|
||||
kr_data.serdes_obj,
|
||||
kr_data.lane,
|
||||
&rx_params);
|
||||
|
||||
rc = al_eth_kr_an_run(&kr_data, an_adv, partner_adv);
|
||||
if (rc != 0) {
|
||||
al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0);
|
||||
al_eth_kr_an_stop(adapter);
|
||||
al_dbg("%s: auto-negotiation failed!\n", __func__);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
if (partner_adv->technology != AL_ETH_AN_TECH_10GBASE_KR) {
|
||||
al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0);
|
||||
al_eth_kr_an_stop(adapter);
|
||||
al_dbg("%s: link partner isn't 10GBASE_KR.\n", __func__);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
rc = al_eth_kr_run_lt(&kr_data);
|
||||
if (rc != 0) {
|
||||
al_eth_kr_lt_stop(adapter, AL_ETH_AN__LT_LANE_0);
|
||||
al_eth_kr_an_stop(adapter);
|
||||
al_dbg("%s: Link-training failed!\n", __func__);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
66
sys/dev/al_eth/al_init_eth_kr.h
Normal file
66
sys/dev/al_eth/al_init_eth_kr.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*-
|
||||
* Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by Semihalf.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ethernet
|
||||
* @{
|
||||
* @file al_init_eth_kr.h
|
||||
*
|
||||
* @brief auto-negotiation and link training activation sequence
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AL_INIT_ETH_KR_H__
|
||||
#define __AL_INIT_ETH_KR_H__
|
||||
|
||||
#include <al_hal_eth_kr.h>
|
||||
#include <al_serdes.h>
|
||||
|
||||
/**
|
||||
* execute Auto-negotiation process
|
||||
*
|
||||
* @param adapter pointer to the private structure
|
||||
* @param serdes_obj pointer to serdes private structure
|
||||
* @param grp serdes's group
|
||||
* @param lane serdes's lane
|
||||
* @param an_adv pointer to the AN Advertisement Registers structure
|
||||
* when NULL, the registers will not be updated.
|
||||
* @param partner_adv pointer to the AN Advertisement received from the lp
|
||||
*
|
||||
* @return 0 on success. otherwise on failure.
|
||||
*/
|
||||
int al_eth_an_lt_execute(struct al_hal_eth_adapter *adapter,
|
||||
struct al_serdes_grp_obj *serdes_obj,
|
||||
enum al_serdes_lane lane,
|
||||
struct al_eth_an_adv *an_adv,
|
||||
struct al_eth_an_adv *partner_adv);
|
||||
|
||||
#endif /*__AL_INIT_ETH_KR_H__*/
|
1537
sys/dev/al_eth/al_init_eth_lm.c
Normal file
1537
sys/dev/al_eth/al_init_eth_lm.c
Normal file
File diff suppressed because it is too large
Load Diff
376
sys/dev/al_eth/al_init_eth_lm.h
Normal file
376
sys/dev/al_eth/al_init_eth_lm.h
Normal file
@ -0,0 +1,376 @@
|
||||
/*-
|
||||
* Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by Semihalf.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Ethernet
|
||||
* @{
|
||||
* @file al_init_eth_lm.h
|
||||
*
|
||||
* @brief ethernet link management common utilities
|
||||
*
|
||||
* Common operation example:
|
||||
* @code
|
||||
* int main()
|
||||
* {
|
||||
* struct al_eth_lm_context lm_context;
|
||||
* struct al_eth_lm_init_params lm_params;
|
||||
* enum al_eth_lm_link_mode old_mode;
|
||||
* enum al_eth_lm_link_mode new_mode;
|
||||
* al_bool fault;
|
||||
* al_bool link_up;
|
||||
* int rc = 0;
|
||||
*
|
||||
* lm_params.adapter = hal_adapter;
|
||||
* lm_params.serdes_obj = serdes;
|
||||
* lm_params.grp = grp;
|
||||
* lm_params.lane = lane;
|
||||
* lm_params.sfp_detection = true;
|
||||
* lm_params.link_training = true;
|
||||
* lm_params.rx_equal = true
|
||||
* lm_params.static_values = true;
|
||||
* lm_params.kr_fec_enable = false;
|
||||
* lm_params.eeprom_read = &my_eeprom_read;
|
||||
* lm_params.eeprom_context = context;
|
||||
* lm_params.get_random_byte = &my_rand_byte;
|
||||
* lm_params.default_mode = AL_ETH_LM_MODE_10G_DA;
|
||||
*
|
||||
* al_eth_lm_init(&lm_context, &lm_params);
|
||||
*
|
||||
* rc = al_eth_lm_link_detection(&lm_context, &fault, &old_mode, &new_mode);
|
||||
* if (fault == false)
|
||||
* return; // in this case the link is still up
|
||||
*
|
||||
* if (rc) {
|
||||
* printf("link detection failed on error\n");
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* if (old_mode != new_mode) {
|
||||
* // perform serdes configuration if needed
|
||||
*
|
||||
* // mac stop / start / config if needed
|
||||
* }
|
||||
*
|
||||
* spin_lock(lock);
|
||||
* rc = al_eth_lm_link_establish($lm_context, &link_up);
|
||||
* spin_unlock(lock);
|
||||
* if (rc) {
|
||||
* printf("establish link failed\n");
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* if (link_up)
|
||||
* printf("Link established successfully\n");
|
||||
* else
|
||||
* printf("No signal found. probably the link partner is disconnected\n");
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AL_INIT_ETH_LM_H__
|
||||
#define __AL_INIT_ETH_LM_H__
|
||||
|
||||
#include <al_serdes.h>
|
||||
#include <al_hal_eth.h>
|
||||
#include "al_init_eth_kr.h"
|
||||
|
||||
enum al_eth_lm_link_mode {
|
||||
AL_ETH_LM_MODE_DISCONNECTED,
|
||||
AL_ETH_LM_MODE_10G_OPTIC,
|
||||
AL_ETH_LM_MODE_10G_DA,
|
||||
AL_ETH_LM_MODE_1G,
|
||||
AL_ETH_LM_MODE_25G,
|
||||
};
|
||||
|
||||
enum al_eth_lm_max_speed {
|
||||
AL_ETH_LM_MAX_SPEED_MAX,
|
||||
AL_ETH_LM_MAX_SPEED_25G,
|
||||
AL_ETH_LM_MAX_SPEED_10G,
|
||||
AL_ETH_LM_MAX_SPEED_1G,
|
||||
};
|
||||
|
||||
enum al_eth_lm_link_state {
|
||||
AL_ETH_LM_LINK_DOWN,
|
||||
AL_ETH_LM_LINK_DOWN_RF,
|
||||
AL_ETH_LM_LINK_UP,
|
||||
};
|
||||
|
||||
enum al_eth_lm_led_config_speed {
|
||||
AL_ETH_LM_LED_CONFIG_1G,
|
||||
AL_ETH_LM_LED_CONFIG_10G,
|
||||
AL_ETH_LM_LED_CONFIG_25G,
|
||||
};
|
||||
|
||||
struct al_eth_lm_led_config_data {
|
||||
enum al_eth_lm_led_config_speed speed;
|
||||
};
|
||||
|
||||
|
||||
struct al_eth_lm_context {
|
||||
struct al_hal_eth_adapter *adapter;
|
||||
struct al_serdes_grp_obj *serdes_obj;
|
||||
enum al_serdes_lane lane;
|
||||
|
||||
uint32_t link_training_failures;
|
||||
|
||||
boolean_t tx_param_dirty;
|
||||
boolean_t serdes_tx_params_valid;
|
||||
struct al_serdes_adv_tx_params tx_params_override;
|
||||
boolean_t rx_param_dirty;
|
||||
boolean_t serdes_rx_params_valid;
|
||||
struct al_serdes_adv_rx_params rx_params_override;
|
||||
|
||||
struct al_eth_an_adv local_adv;
|
||||
struct al_eth_an_adv partner_adv;
|
||||
|
||||
enum al_eth_lm_link_mode mode;
|
||||
uint8_t da_len;
|
||||
boolean_t debug;
|
||||
|
||||
/* configurations */
|
||||
boolean_t sfp_detection;
|
||||
uint8_t sfp_bus_id;
|
||||
uint8_t sfp_i2c_addr;
|
||||
|
||||
enum al_eth_lm_link_mode default_mode;
|
||||
uint8_t default_dac_len;
|
||||
boolean_t link_training;
|
||||
boolean_t rx_equal;
|
||||
boolean_t static_values;
|
||||
|
||||
boolean_t retimer_exist;
|
||||
enum al_eth_retimer_type retimer_type;
|
||||
uint8_t retimer_bus_id;
|
||||
uint8_t retimer_i2c_addr;
|
||||
enum al_eth_retimer_channel retimer_channel;
|
||||
|
||||
/* services */
|
||||
int (*i2c_read)(void *handle, uint8_t bus_id, uint8_t i2c_addr,
|
||||
uint8_t reg_addr, uint8_t *val);
|
||||
int (*i2c_write)(void *handle, uint8_t bus_id, uint8_t i2c_addr,
|
||||
uint8_t reg_addr, uint8_t val);
|
||||
void *i2c_context;
|
||||
uint8_t (*get_random_byte)(void);
|
||||
|
||||
int (*gpio_get)(unsigned int gpio);
|
||||
uint32_t gpio_present;
|
||||
|
||||
enum al_eth_retimer_channel retimer_tx_channel;
|
||||
boolean_t retimer_configured;
|
||||
|
||||
enum al_eth_lm_max_speed max_speed;
|
||||
|
||||
boolean_t sfp_detect_force_mode;
|
||||
|
||||
enum al_eth_lm_link_state link_state;
|
||||
boolean_t new_port;
|
||||
|
||||
boolean_t (*lm_pause)(void *handle);
|
||||
|
||||
void (*led_config)(void *handle, struct al_eth_lm_led_config_data *data);
|
||||
};
|
||||
|
||||
struct al_eth_lm_init_params {
|
||||
/* pointer to HAL context */
|
||||
struct al_hal_eth_adapter *adapter;
|
||||
/* pointer to serdes object */
|
||||
struct al_serdes_grp_obj *serdes_obj;
|
||||
/* serdes lane for this port */
|
||||
enum al_serdes_lane lane;
|
||||
|
||||
/*
|
||||
* set to true to perform sfp detection if the link is down.
|
||||
* when set to true, eeprom_read below should NOT be NULL.
|
||||
*/
|
||||
boolean_t sfp_detection;
|
||||
/* i2c bus id of the SFP for this port */
|
||||
uint8_t sfp_bus_id;
|
||||
/* i2c addr of the SFP for this port */
|
||||
uint8_t sfp_i2c_addr;
|
||||
/*
|
||||
* default mode, and dac length will be used in case sfp_detection
|
||||
* is not set or in case the detection failed.
|
||||
*/
|
||||
enum al_eth_lm_link_mode default_mode;
|
||||
uint8_t default_dac_len;
|
||||
|
||||
/* the i2c bus id and addr of the retimer in case it exist */
|
||||
uint8_t retimer_bus_id;
|
||||
uint8_t retimer_i2c_addr;
|
||||
/* retimer channel connected to this port */
|
||||
enum al_eth_retimer_channel retimer_channel;
|
||||
enum al_eth_retimer_channel retimer_tx_channel;
|
||||
/* retimer type if exist */
|
||||
enum al_eth_retimer_type retimer_type;
|
||||
|
||||
/*
|
||||
* the following parameters control what mechanisms to run
|
||||
* on link_establish with the following steps:
|
||||
* - if retimer_exist is set, the retimer will be configured based on DA len.
|
||||
* - if link_training is set and DA detected run link training. if succeed return 0
|
||||
* - if rx_equal is set serdes equalization will be run to configure the rx parameters.
|
||||
* - if static_values is set, tx and rx values will be set based on static values.
|
||||
*/
|
||||
boolean_t retimer_exist;
|
||||
boolean_t link_training;
|
||||
boolean_t rx_equal;
|
||||
boolean_t static_values;
|
||||
|
||||
/* enable / disable fec capabilities in AN */
|
||||
boolean_t kr_fec_enable;
|
||||
|
||||
/*
|
||||
* pointer to function that's read 1 byte from eeprom
|
||||
* in case no eeprom is connected should return -ETIMEDOUT
|
||||
*/
|
||||
int (*i2c_read)(void *handle, uint8_t bus_id, uint8_t i2c_addr,
|
||||
uint8_t reg_addr, uint8_t *val);
|
||||
int (*i2c_write)(void *handle, uint8_t bus_id, uint8_t i2c_addr,
|
||||
uint8_t reg_addr, uint8_t val);
|
||||
void *i2c_context;
|
||||
/* pointer to function that return 1 rand byte */
|
||||
uint8_t (*get_random_byte)(void);
|
||||
|
||||
/* pointer to function that gets GPIO value - if NULL gpio present won't be used */
|
||||
int (*gpio_get)(unsigned int gpio);
|
||||
/* gpio number connected to the SFP present pin */
|
||||
uint32_t gpio_present;
|
||||
|
||||
enum al_eth_lm_max_speed max_speed;
|
||||
|
||||
/* in case force mode is true - the default mode will be set regardless to
|
||||
* the SFP EEPROM content */
|
||||
boolean_t sfp_detect_force_mode;
|
||||
|
||||
/* lm pause callback - in case it return true the LM will try to preserve
|
||||
* the current link status and will not try to establish new link (and will not
|
||||
* access to i2c bus) */
|
||||
boolean_t (*lm_pause)(void *handle);
|
||||
|
||||
/* config ethernet LEDs according to data. can be NULL if no configuration needed */
|
||||
void (*led_config)(void *handle, struct al_eth_lm_led_config_data *data);
|
||||
};
|
||||
|
||||
/**
|
||||
* initialize link management context and set configuration
|
||||
*
|
||||
* @param lm_context pointer to link management context
|
||||
* @param params parameters passed from upper layer
|
||||
*
|
||||
* @return 0 in case of success. otherwise on failure.
|
||||
*/
|
||||
int al_eth_lm_init(struct al_eth_lm_context *lm_context,
|
||||
struct al_eth_lm_init_params *params);
|
||||
|
||||
/**
|
||||
* perform link status check. in case link is down perform sfp detection
|
||||
*
|
||||
* @param lm_context pointer to link management context
|
||||
* @param link_fault indicate if the link is down
|
||||
* @param old_mode the last working mode
|
||||
* @param new_mode the new mode detected in this call
|
||||
*
|
||||
* @return 0 in case of success. otherwise on failure.
|
||||
*/
|
||||
int al_eth_lm_link_detection(struct al_eth_lm_context *lm_context,
|
||||
boolean_t *link_fault, enum al_eth_lm_link_mode *old_mode,
|
||||
enum al_eth_lm_link_mode *new_mode);
|
||||
|
||||
/**
|
||||
* run LT, rx equalization and static values override according to configuration
|
||||
* This function MUST be called inside a lock as it using common serdes registers
|
||||
*
|
||||
* @param lm_context pointer to link management context
|
||||
* @param link_up set to true in case link is establish successfully
|
||||
*
|
||||
* @return < 0 in case link was failed to be established
|
||||
*/
|
||||
int al_eth_lm_link_establish(struct al_eth_lm_context *lm_context,
|
||||
boolean_t *link_up);
|
||||
|
||||
/**
|
||||
* override the default static parameters
|
||||
*
|
||||
* @param lm_context pointer to link management context
|
||||
* @param tx_params pointer to new tx params
|
||||
* @param rx_params pointer to new rx params
|
||||
*
|
||||
* @return 0 in case of success. otherwise on failure.
|
||||
**/
|
||||
int al_eth_lm_static_parameters_override(struct al_eth_lm_context *lm_context,
|
||||
struct al_serdes_adv_tx_params *tx_params,
|
||||
struct al_serdes_adv_rx_params *rx_params);
|
||||
|
||||
/**
|
||||
* disable serdes parameters override
|
||||
*
|
||||
* @param lm_context pointer to link management context
|
||||
* @param tx_params set to true to disable override of tx params
|
||||
* @param rx_params set to true to disable override of rx params
|
||||
*
|
||||
* @return 0 in case of success. otherwise on failure.
|
||||
**/
|
||||
int al_eth_lm_static_parameters_override_disable(struct al_eth_lm_context *lm_context,
|
||||
boolean_t tx_params, boolean_t rx_params);
|
||||
|
||||
/**
|
||||
* get the static parameters that are being used
|
||||
* if the parameters was override - return the override values
|
||||
* else return the current values of the parameters
|
||||
*
|
||||
* @param lm_context pointer to link management context
|
||||
* @param tx_params pointer to new tx params
|
||||
* @param rx_params pointer to new rx params
|
||||
*
|
||||
* @return 0 in case of success. otherwise on failure.
|
||||
*/
|
||||
int al_eth_lm_static_parameters_get(struct al_eth_lm_context *lm_context,
|
||||
struct al_serdes_adv_tx_params *tx_params,
|
||||
struct al_serdes_adv_rx_params *rx_params);
|
||||
|
||||
/**
|
||||
* convert link management mode to string
|
||||
*
|
||||
* @param val link management mode
|
||||
*
|
||||
* @return string of the mode
|
||||
*/
|
||||
const char *al_eth_lm_mode_convert_to_str(enum al_eth_lm_link_mode val);
|
||||
|
||||
/**
|
||||
* print all debug messages
|
||||
*
|
||||
* @param lm_context pointer to link management context
|
||||
* @param enable set to true to enable debug mode
|
||||
*/
|
||||
void al_eth_lm_debug_mode_set(struct al_eth_lm_context *lm_context,
|
||||
boolean_t enable);
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user