On Thu, Mar 26, 2020 at 03:51:19PM +0200, Florinel Iordache wrote: > Add support for backplane kr generic driver including link training > (ieee802.3ap/ba) and fixed equalization algorithm > > Signed-off-by: Florinel Iordache <florinel.iordache@xxxxxxx> > --- > drivers/net/phy/Kconfig | 2 + > drivers/net/phy/Makefile | 1 + > drivers/net/phy/backplane/Kconfig | 20 + > drivers/net/phy/backplane/Makefile | 9 + > drivers/net/phy/backplane/backplane.c | 1538 +++++++++++++++++++++++++++ > drivers/net/phy/backplane/backplane.h | 262 +++++ > drivers/net/phy/backplane/eq_fixed.c | 83 ++ > drivers/net/phy/backplane/equalization.h | 282 +++++ > drivers/net/phy/backplane/link_training.c | 1604 +++++++++++++++++++++++++++++ > drivers/net/phy/backplane/link_training.h | 34 + > 10 files changed, 3835 insertions(+) > create mode 100644 drivers/net/phy/backplane/Kconfig > create mode 100644 drivers/net/phy/backplane/Makefile > create mode 100644 drivers/net/phy/backplane/backplane.c > create mode 100644 drivers/net/phy/backplane/backplane.h > create mode 100644 drivers/net/phy/backplane/eq_fixed.c > create mode 100644 drivers/net/phy/backplane/equalization.h > create mode 100644 drivers/net/phy/backplane/link_training.c > create mode 100644 drivers/net/phy/backplane/link_training.h > > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig > index cc7f1df..abab4e5 100644 > --- a/drivers/net/phy/Kconfig > +++ b/drivers/net/phy/Kconfig > @@ -523,6 +523,8 @@ config XILINX_GMII2RGMII > the Reduced Gigabit Media Independent Interface(RGMII) between > Ethernet physical media devices and the Gigabit Ethernet controller. > > +source "drivers/net/phy/backplane/Kconfig" > + > endif # PHYLIB > > config MICREL_KS8995MA > diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile > index 70774ab..0b867fb 100644 > --- a/drivers/net/phy/Makefile > +++ b/drivers/net/phy/Makefile > @@ -101,3 +101,4 @@ obj-$(CONFIG_STE10XP) += ste10Xp.o > obj-$(CONFIG_TERANETICS_PHY) += teranetics.o > obj-$(CONFIG_VITESSE_PHY) += vitesse.o > obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o > +obj-$(CONFIG_ETH_BACKPLANE) += backplane/ > diff --git a/drivers/net/phy/backplane/Kconfig b/drivers/net/phy/backplane/Kconfig > new file mode 100644 > index 0000000..9ec54b5 > --- /dev/null > +++ b/drivers/net/phy/backplane/Kconfig > @@ -0,0 +1,20 @@ > +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) > +config ETH_BACKPLANE > + tristate "Ethernet Backplane support" > + depends on OF_MDIO > + help > + This module provides driver support for Ethernet Operation over > + Electrical Backplanes. It includes Backplane generic > + driver including support for Link Training (IEEE802.3ap/ba). > + Based on the link quality, a signal equalization is required. > + The standard specifies that a start-up algorithm should be in place > + in order to get the link up. > + > +config ETH_BACKPLANE_FIXED > + tristate "Fixed: No Equalization algorithm" > + depends on ETH_BACKPLANE > + help > + This module provides a driver to setup fixed user configurable > + coefficient values for backplanes equalization. This means > + No Equalization algorithm is used to adapt the initial coefficients > + initially set by the user. > \ No newline at end of file > diff --git a/drivers/net/phy/backplane/Makefile b/drivers/net/phy/backplane/Makefile > new file mode 100644 > index 0000000..ded6f2d > --- /dev/null > +++ b/drivers/net/phy/backplane/Makefile > @@ -0,0 +1,9 @@ > +# SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) > +# > +# Makefile for Ethernet Backplane driver > +# > + > +obj-$(CONFIG_ETH_BACKPLANE) += eth_backplane.o > +obj-$(CONFIG_ETH_BACKPLANE_FIXED) += eq_fixed.o > + > +eth_backplane-objs := backplane.o link_training.o > diff --git a/drivers/net/phy/backplane/backplane.c b/drivers/net/phy/backplane/backplane.c > new file mode 100644 > index 0000000..1b580bc > --- /dev/null > +++ b/drivers/net/phy/backplane/backplane.c > @@ -0,0 +1,1538 @@ > +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) > +/* Backplane driver > + * > + * Copyright 2015 Freescale Semiconductor, Inc. > + * Copyright 2018-2020 NXP > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/mii.h> > +#include <linux/mdio.h> > +#include <linux/ethtool.h> > +#include <linux/io.h> > +#include <linux/of.h> > +#include <linux/of_net.h> > +#include <linux/of_address.h> > +#include <linux/of_platform.h> > +#include <linux/timer.h> > +#include <linux/delay.h> > +#include <linux/workqueue.h> > +#include <linux/netdevice.h> > +#include <linux/list.h> > + > +#include "backplane.h" > +#include "link_training.h" > + > +/* KR timeouts in milliseconds */ > +#define KR_TIMEOUT_1 100 > +#define KR_TIMEOUT_2 1000 > +#define KR_DENY_RT_INTERVAL 3000 > +#define KR_LT_TIMEOUT 500 > + > +/* KR timings in interations */ > +#define KR_AN_WAIT_ITERATIONS 5 > +#define KR_TRAIN_STEP_ITERATIONS 2 > +#define CDR_LOCK_RETRY_COUNT 3 > + > +/* AN status register (Clause 45) (MMD 7): MDIO_STAT1 */ > +#define AN_LINK_UP_MASK 0x04 > + > +/* Logging buffer size */ > +#define LOG_BUFFER_SIZE 200 > + > +/* Backplane custom logging */ > +#define BPDEV_LOG(name) \ > + char log_buffer[LOG_BUFFER_SIZE]; \ > + va_list args; va_start(args, msg); \ > + vsnprintf(log_buffer, LOG_BUFFER_SIZE - 1, msg, args); \ > + if (!bpphy->attached_dev) \ > + dev_##name(&bpphy->mdio.dev, log_buffer); \ > + else \ > + dev_##name(&bpphy->mdio.dev, "%s: %s", \ > + netdev_name(bpphy->attached_dev), log_buffer); \ > + va_end(args) > + > +/* Backplane features */ > +__ETHTOOL_DECLARE_LINK_MODE_MASK(backplane_features) __ro_after_init; > +EXPORT_SYMBOL(backplane_features); > + > +const int backplane_common_features_array[] = { > + ETHTOOL_LINK_MODE_Backplane_BIT, > + ETHTOOL_LINK_MODE_Autoneg_BIT, > + ETHTOOL_LINK_MODE_MII_BIT, > +}; > + > +const int backplane_protocol_features_array[] = { > + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, > + ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, > +}; > + > +/* map string key to pointer data */ > +struct spmap_node { > + struct list_head entry; > + const char *key; > + void *pdata; > +}; > + > +/* registered equalization algorithms info */ > +static LIST_HEAD(eqalg_list); > + > +/* lanes attached to an equalization algorithm */ > +static LIST_HEAD(lnalg_list); > + > +/* Backplane mutex between all KR PHY threads */ > +static struct mutex backplane_lock; > + > +static int get_backplane_speed(phy_interface_t bp_mode) > +{ > + switch (bp_mode) { > + case PHY_INTERFACE_MODE_10GKR: > + return SPEED_10000; > + case PHY_INTERFACE_MODE_40GKR4: > + return SPEED_40000; > + default: > + pr_err("%s: Unsupported backplane phy interface\n", > + BACKPLANE_DRIVER_NAME); > + return SPEED_UNKNOWN; > + } > + return SPEED_UNKNOWN; > +} > + > +static enum ethtool_link_mode_bit_indices > + get_backplane_supported_mode(phy_interface_t bp_mode) > +{ > + switch (bp_mode) { > + case PHY_INTERFACE_MODE_10GKR: > + return ETHTOOL_LINK_MODE_10000baseKR_Full_BIT; > + case PHY_INTERFACE_MODE_40GKR4: > + return ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT; > + default: > + pr_err("%s: Unsupported backplane phy interface\n", > + BACKPLANE_DRIVER_NAME); > + return ETHTOOL_LINK_MODE_Backplane_BIT; > + } > + return ETHTOOL_LINK_MODE_Backplane_BIT; > +} > + > +static int spmap_add(struct list_head *list, const char *key, void *pdata) > +{ > + struct spmap_node *node; > + > + /* create a new entry with desired key */ > + node = kzalloc(sizeof(*node), GFP_KERNEL); > + if (!node) > + return -ENOMEM; > + > + node->key = key; > + node->pdata = pdata; > + > + list_add(&node->entry, list); > + > + return 0; > +} > + > +static const struct equalization_algorithm *eq_find(const char *key) > +{ > + struct spmap_node *eqalg, *eqalg_tmp; > + > + if (!key) > + return NULL; > + > + /* search desired single key */ > + list_for_each_entry_safe(eqalg, eqalg_tmp, &eqalg_list, entry) { > + if (strcmp(eqalg->key, key) == 0) > + return (struct equalization_algorithm *)eqalg->pdata; > + } > + return NULL; > +} > + > +static void backplane_features_init(void) > +{ > + linkmode_set_bit_array(backplane_common_features_array, > + ARRAY_SIZE(backplane_common_features_array), > + backplane_features); > + > + linkmode_set_bit_array(backplane_protocol_features_array, > + ARRAY_SIZE(backplane_protocol_features_array), > + backplane_features); > +} > + > +static u32 le_ioread32(void __iomem *reg) > +{ > + return ioread32(reg); > +} > + > +static void le_iowrite32(u32 value, void __iomem *reg) > +{ > + iowrite32(value, reg); > +} > + > +static u32 be_ioread32(void __iomem *reg) > +{ > + return ioread32be(reg); > +} > + > +static void be_iowrite32(u32 value, void __iomem *reg) > +{ > + iowrite32be(value, reg); > +} > + > +static void training_status_init(struct training_status *trst) > +{ > + trst->done_training = false; > + trst->remote_tx_complete = false; > + trst->remote_tx_running = false; > + trst->sent_init = false; > + trst->lp_rx_ready = 0; > + trst->local_tx_running = false; > +} > + > +static void init_krln(struct kr_lane_info *krln, bool revert_default) > +{ > + if (revert_default) > + backplane_default_kr_lane(krln); > + > + training_status_init(&krln->trst); > + krln->state = DETECTING_LP; > + krln->an_acquired = false; > + > + krln->ld_update = 0; > + krln->prev_ld_update = 0; > + krln->ld_last_nonhold_update = 0; > + krln->lp_status = 0; > + krln->lp_last_change_status = 0; > + krln->last_lp_update_status[C_M1] = 0; > + krln->last_lp_update_status[C_Z0] = 0; > + krln->last_lp_update_status[C_P1] = 0; > + krln->ld_status = 0; > + krln->move_back_prev = false; > + krln->move_back_cnt = 0; > + krln->move_back_lp_status = 0; > + > + lt_init_ld(krln); > +} > + > +static void setup_supported_linkmode(struct phy_device *bpphy) > +{ > + struct backplane_phy_info *bp_phy = bpphy->priv; > + int i; > + > + /* Clear all supported backplane protocols features > + * and setup only the currently configured protocol > + */ > + for (i = 0; i < ARRAY_SIZE(backplane_protocol_features_array); i++) > + linkmode_clear_bit(backplane_protocol_features_array[i], > + bpphy->supported); > + > + linkmode_set_bit(get_backplane_supported_mode(bp_phy->bp_mode), > + bpphy->supported); > +} > + > +/* Read AN Link Status */ > +static int is_an_link_up(struct phy_device *bpphy) > +{ > + struct backplane_phy_info *bp_phy = bpphy->priv; > + int ret, val = 0; > + > + mutex_lock(&bp_phy->bpphy_lock); > + > + /* Read twice because Link_Status is LL (Latched Low) bit */ > + val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status); > + val = phy_read_mmd(bpphy, MDIO_MMD_AN, bp_phy->bp_dev.mdio.an_status); Why not just val = phy_read_mmd(bpphy, MDIO_MMD_AN, MDIO_CTRL1); Or is your hardware not actually conformant to the standard? There has also been a lot of discussion of reading the status twice is correct or not. Don't you care the link has briefly gone down and up again? Andrew