aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch')
-rw-r--r--target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch1776
1 files changed, 1776 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch b/target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch
new file mode 100644
index 0000000000..0e8e3695f3
--- /dev/null
+++ b/target/linux/layerscape/patches-4.9/703-phy-support-layerscape.patch
@@ -0,0 +1,1776 @@
+From be07319b9897738a4ab1501880b7dd9be26eba66 Mon Sep 17 00:00:00 2001
+From: Yangbo Lu <yangbo.lu@nxp.com>
+Date: Mon, 25 Sep 2017 11:54:28 +0800
+Subject: [PATCH] phy: support layerscape
+
+This is a integrated patch for layerscape mdio-phy support.
+
+Signed-off-by: Bogdan Purcareata <bogdan.purcareata@nxp.com>
+Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
+Signed-off-by: costi <constantin.tudor@freescale.com>
+Signed-off-by: Madalin Bucur <madalin.bucur@freescale.com>
+Signed-off-by: Shaohui Xie <Shaohui.Xie@nxp.com>
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
+---
+ drivers/net/phy/Kconfig | 11 +
+ drivers/net/phy/Makefile | 2 +
+ drivers/net/phy/aquantia.c | 28 +
+ drivers/net/phy/cortina.c | 118 ++++
+ drivers/net/phy/fsl_backplane.c | 1358 +++++++++++++++++++++++++++++++++++++++
+ drivers/net/phy/phy.c | 23 +-
+ drivers/net/phy/phy_device.c | 6 +-
+ drivers/net/phy/swphy.c | 1 +
+ include/linux/phy.h | 4 +
+ 9 files changed, 1544 insertions(+), 7 deletions(-)
+ create mode 100644 drivers/net/phy/cortina.c
+ create mode 100644 drivers/net/phy/fsl_backplane.c
+
+diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
+index 30a3a2f5..3521c1ac 100644
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -89,6 +89,12 @@ config MDIO_BUS_MUX_MMIOREG
+ config MDIO_CAVIUM
+ tristate
+
++config MDIO_FSL_BACKPLANE
++ tristate "Support for backplane on Freescale XFI interface"
++ depends on OF_MDIO
++ help
++ This module provides a driver for Freescale XFI's backplane.
++
+ config MDIO_GPIO
+ tristate "GPIO lib-based bitbanged MDIO buses"
+ depends on MDIO_BITBANG && GPIOLIB
+@@ -298,6 +304,11 @@ config CICADA_PHY
+ ---help---
+ Currently supports the cis8204
+
++config CORTINA_PHY
++ tristate "Cortina EDC CDR 10G Ethernet PHY"
++ ---help---
++ Currently supports the CS4340 phy.
++
+ config DAVICOM_PHY
+ tristate "Davicom PHYs"
+ ---help---
+diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
+index 93a68fcd..ef3ec265 100644
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -30,6 +30,7 @@ obj-$(CONFIG_MDIO_BUS_MUX_BCM_IPROC) += mdio-mux-bcm-iproc.o
+ obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
+ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
+ obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o
++obj-$(CONFIG_MDIO_FSL_BACKPLANE) += fsl_backplane.o
+ obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
+ obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o
+ obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
+@@ -48,6 +49,7 @@ obj-$(CONFIG_BCM_CYGNUS_PHY) += bcm-cygnus.o
+ obj-$(CONFIG_BCM_NET_PHYLIB) += bcm-phy-lib.o
+ obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
+ obj-$(CONFIG_CICADA_PHY) += cicada.o
++obj-$(CONFIG_CORTINA_PHY) += cortina.o
+ obj-$(CONFIG_DAVICOM_PHY) += davicom.o
+ obj-$(CONFIG_DP83640_PHY) += dp83640.o
+ obj-$(CONFIG_DP83848_PHY) += dp83848.o
+diff --git a/drivers/net/phy/aquantia.c b/drivers/net/phy/aquantia.c
+index 09b0b0aa..e8ae50e1 100644
+--- a/drivers/net/phy/aquantia.c
++++ b/drivers/net/phy/aquantia.c
+@@ -21,6 +21,8 @@
+ #define PHY_ID_AQ1202 0x03a1b445
+ #define PHY_ID_AQ2104 0x03a1b460
+ #define PHY_ID_AQR105 0x03a1b4a2
++#define PHY_ID_AQR106 0x03a1b4d0
++#define PHY_ID_AQR107 0x03a1b4e0
+ #define PHY_ID_AQR405 0x03a1b4b0
+
+ #define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \
+@@ -153,6 +155,30 @@ static struct phy_driver aquantia_driver[] = {
+ .ack_interrupt = aquantia_ack_interrupt,
+ .read_status = aquantia_read_status,
+ },
++{
++ .phy_id = PHY_ID_AQR106,
++ .phy_id_mask = 0xfffffff0,
++ .name = "Aquantia AQR106",
++ .features = PHY_AQUANTIA_FEATURES,
++ .flags = PHY_HAS_INTERRUPT,
++ .aneg_done = aquantia_aneg_done,
++ .config_aneg = aquantia_config_aneg,
++ .config_intr = aquantia_config_intr,
++ .ack_interrupt = aquantia_ack_interrupt,
++ .read_status = aquantia_read_status,
++},
++{
++ .phy_id = PHY_ID_AQR107,
++ .phy_id_mask = 0xfffffff0,
++ .name = "Aquantia AQR107",
++ .features = PHY_AQUANTIA_FEATURES,
++ .flags = PHY_HAS_INTERRUPT,
++ .aneg_done = aquantia_aneg_done,
++ .config_aneg = aquantia_config_aneg,
++ .config_intr = aquantia_config_intr,
++ .ack_interrupt = aquantia_ack_interrupt,
++ .read_status = aquantia_read_status,
++},
+ {
+ .phy_id = PHY_ID_AQR405,
+ .phy_id_mask = 0xfffffff0,
+@@ -173,6 +199,8 @@ static struct mdio_device_id __maybe_unused aquantia_tbl[] = {
+ { PHY_ID_AQ1202, 0xfffffff0 },
+ { PHY_ID_AQ2104, 0xfffffff0 },
+ { PHY_ID_AQR105, 0xfffffff0 },
++ { PHY_ID_AQR106, 0xfffffff0 },
++ { PHY_ID_AQR107, 0xfffffff0 },
+ { PHY_ID_AQR405, 0xfffffff0 },
+ { }
+ };
+diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c
+new file mode 100644
+index 00000000..72f4228a
+--- /dev/null
++++ b/drivers/net/phy/cortina.c
+@@ -0,0 +1,118 @@
++/*
++ * Copyright 2017 NXP
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * CORTINA is a registered trademark of Cortina Systems, Inc.
++ *
++ */
++#include <linux/module.h>
++#include <linux/phy.h>
++
++#define PHY_ID_CS4340 0x13e51002
++
++#define VILLA_GLOBAL_CHIP_ID_LSB 0x0
++#define VILLA_GLOBAL_CHIP_ID_MSB 0x1
++
++#define VILLA_GLOBAL_GPIO_1_INTS 0x017
++
++static int cortina_read_reg(struct phy_device *phydev, u16 regnum)
++{
++ return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
++ MII_ADDR_C45 | regnum);
++}
++
++static int cortina_config_aneg(struct phy_device *phydev)
++{
++ phydev->supported = SUPPORTED_10000baseT_Full;
++ phydev->advertising = SUPPORTED_10000baseT_Full;
++
++ return 0;
++}
++
++static int cortina_read_status(struct phy_device *phydev)
++{
++ int gpio_int_status, ret = 0;
++
++ gpio_int_status = cortina_read_reg(phydev, VILLA_GLOBAL_GPIO_1_INTS);
++ if (gpio_int_status < 0) {
++ ret = gpio_int_status;
++ goto err;
++ }
++
++ if (gpio_int_status & 0x8) {
++ /* up when edc_convergedS set */
++ phydev->speed = SPEED_10000;
++ phydev->duplex = DUPLEX_FULL;
++ phydev->link = 1;
++ } else {
++ phydev->link = 0;
++ }
++
++err:
++ return ret;
++}
++
++static int cortina_soft_reset(struct phy_device *phydev)
++{
++ return 0;
++}
++
++static int cortina_probe(struct phy_device *phydev)
++{
++ u32 phy_id = 0;
++ int id_lsb = 0, id_msb = 0;
++
++ /* Read device id from phy registers. */
++ id_lsb = cortina_read_reg(phydev, VILLA_GLOBAL_CHIP_ID_LSB);
++ if (id_lsb < 0)
++ return -ENXIO;
++
++ phy_id = id_lsb << 16;
++
++ id_msb = cortina_read_reg(phydev, VILLA_GLOBAL_CHIP_ID_MSB);
++ if (id_msb < 0)
++ return -ENXIO;
++
++ phy_id |= id_msb;
++
++ /* Make sure the device tree binding matched the driver with the
++ * right device.
++ */
++ if (phy_id != phydev->drv->phy_id) {
++ phydev_err(phydev, "Error matching phy with %s driver\n",
++ phydev->drv->name);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static struct phy_driver cortina_driver[] = {
++{
++ .phy_id = PHY_ID_CS4340,
++ .phy_id_mask = 0xffffffff,
++ .name = "Cortina CS4340",
++ .config_aneg = cortina_config_aneg,
++ .read_status = cortina_read_status,
++ .soft_reset = cortina_soft_reset,
++ .probe = cortina_probe,
++},
++};
++
++module_phy_driver(cortina_driver);
++
++static struct mdio_device_id __maybe_unused cortina_tbl[] = {
++ { PHY_ID_CS4340, 0xffffffff},
++ {},
++};
++
++MODULE_DEVICE_TABLE(mdio, cortina_tbl);
+diff --git a/drivers/net/phy/fsl_backplane.c b/drivers/net/phy/fsl_backplane.c
+new file mode 100644
+index 00000000..76865261
+--- /dev/null
++++ b/drivers/net/phy/fsl_backplane.c
+@@ -0,0 +1,1358 @@
++/* Freescale backplane driver.
++ * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
++ *
++ * Copyright 2015 Freescale Semiconductor, Inc.
++ *
++ * Licensed under the GPL-2 or later.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/mii.h>
++#include <linux/mdio.h>
++#include <linux/ethtool.h>
++#include <linux/phy.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>
++
++/* XFI PCS Device Identifier */
++#define FSL_PCS_PHY_ID 0x0083e400
++
++/* Freescale KR PMD registers */
++#define FSL_KR_PMD_CTRL 0x96
++#define FSL_KR_PMD_STATUS 0x97
++#define FSL_KR_LP_CU 0x98
++#define FSL_KR_LP_STATUS 0x99
++#define FSL_KR_LD_CU 0x9a
++#define FSL_KR_LD_STATUS 0x9b
++
++/* Freescale KR PMD defines */
++#define PMD_RESET 0x1
++#define PMD_STATUS_SUP_STAT 0x4
++#define PMD_STATUS_FRAME_LOCK 0x2
++#define TRAIN_EN 0x3
++#define TRAIN_DISABLE 0x1
++#define RX_STAT 0x1
++
++#define FSL_KR_RX_LINK_STAT_MASK 0x1000
++#define FSL_XFI_PCS_10GR_SR1 0x20
++
++/* Freescale KX PCS mode register */
++#define FSL_PCS_IF_MODE 0x8014
++
++/* Freescale KX PCS mode register init value */
++#define IF_MODE_INIT 0x8
++
++/* Freescale KX/KR AN registers */
++#define FSL_AN_AD1 0x11
++#define FSL_AN_BP_STAT 0x30
++
++/* Freescale KX/KR AN registers defines */
++#define AN_CTRL_INIT 0x1200
++#define KX_AN_AD1_INIT 0x25
++#define KR_AN_AD1_INIT 0x85
++#define AN_LNK_UP_MASK 0x4
++#define KR_AN_MASK 0x8
++#define TRAIN_FAIL 0x8
++
++/* C(-1) */
++#define BIN_M1 0
++/* C(1) */
++#define BIN_LONG 1
++#define BIN_M1_SEL 6
++#define BIN_Long_SEL 7
++#define CDR_SEL_MASK 0x00070000
++#define BIN_SNAPSHOT_NUM 5
++#define BIN_M1_THRESHOLD 3
++#define BIN_LONG_THRESHOLD 2
++
++#define PRE_COE_SHIFT 22
++#define POST_COE_SHIFT 16
++#define ZERO_COE_SHIFT 8
++
++#define PRE_COE_MAX 0x0
++#define PRE_COE_MIN 0x8
++#define POST_COE_MAX 0x0
++#define POST_COE_MIN 0x10
++#define ZERO_COE_MAX 0x30
++#define ZERO_COE_MIN 0x0
++
++#define TECR0_INIT 0x24200000
++#define RATIO_PREQ 0x3
++#define RATIO_PST1Q 0xd
++#define RATIO_EQ 0x20
++
++#define GCR0_RESET_MASK 0x600000
++#define GCR1_SNP_START_MASK 0x00000040
++#define GCR1_CTL_SNP_START_MASK 0x00002000
++#define GCR1_REIDL_TH_MASK 0x00700000
++#define GCR1_REIDL_EX_SEL_MASK 0x000c0000
++#define GCR1_REIDL_ET_MAS_MASK 0x00004000
++#define TECR0_AMP_RED_MASK 0x0000003f
++
++#define RECR1_CTL_SNP_DONE_MASK 0x00000002
++#define RECR1_SNP_DONE_MASK 0x00000004
++#define TCSR1_SNP_DATA_MASK 0x0000ffc0
++#define TCSR1_SNP_DATA_SHIFT 6
++#define TCSR1_EQ_SNPBIN_SIGN_MASK 0x100
++
++#define RECR1_GAINK2_MASK 0x0f000000
++#define RECR1_GAINK2_SHIFT 24
++#define RECR1_GAINK3_MASK 0x000f0000
++#define RECR1_GAINK3_SHIFT 16
++#define RECR1_OFFSET_MASK 0x00003f80
++#define RECR1_OFFSET_SHIFT 7
++#define RECR1_BLW_MASK 0x00000f80
++#define RECR1_BLW_SHIFT 7
++#define EYE_CTRL_SHIFT 12
++#define BASE_WAND_SHIFT 10
++
++#define XGKR_TIMEOUT 1050
++
++#define INCREMENT 1
++#define DECREMENT 2
++#define TIMEOUT_LONG 3
++#define TIMEOUT_M1 3
++
++#define RX_READY_MASK 0x8000
++#define PRESET_MASK 0x2000
++#define INIT_MASK 0x1000
++#define COP1_MASK 0x30
++#define COP1_SHIFT 4
++#define COZ_MASK 0xc
++#define COZ_SHIFT 2
++#define COM1_MASK 0x3
++#define COM1_SHIFT 0
++#define REQUEST_MASK 0x3f
++#define LD_ALL_MASK (PRESET_MASK | INIT_MASK | \
++ COP1_MASK | COZ_MASK | COM1_MASK)
++
++#define NEW_ALGORITHM_TRAIN_TX
++#ifdef NEW_ALGORITHM_TRAIN_TX
++#define FORCE_INC_COP1_NUMBER 0
++#define FORCE_INC_COM1_NUMBER 1
++#endif
++
++#define VAL_INVALID 0xff
++
++static const u32 preq_table[] = {0x0, 0x1, 0x3, 0x5,
++ 0x7, 0x9, 0xb, 0xc, VAL_INVALID};
++static const u32 pst1q_table[] = {0x0, 0x1, 0x3, 0x5, 0x7,
++ 0x9, 0xb, 0xd, 0xf, 0x10, VAL_INVALID};
++
++enum backplane_mode {
++ PHY_BACKPLANE_1000BASE_KX,
++ PHY_BACKPLANE_10GBASE_KR,
++ PHY_BACKPLANE_INVAL
++};
++
++enum coe_filed {
++ COE_COP1,
++ COE_COZ,
++ COE_COM
++};
++
++enum coe_update {
++ COE_NOTUPDATED,
++ COE_UPDATED,
++ COE_MIN,
++ COE_MAX,
++ COE_INV
++};
++
++enum train_state {
++ DETECTING_LP,
++ TRAINED,
++};
++
++struct per_lane_ctrl_status {
++ __be32 gcr0; /* 0x.000 - General Control Register 0 */
++ __be32 gcr1; /* 0x.004 - General Control Register 1 */
++ __be32 gcr2; /* 0x.008 - General Control Register 2 */
++ __be32 resv1; /* 0x.00C - Reserved */
++ __be32 recr0; /* 0x.010 - Receive Equalization Control Register 0 */
++ __be32 recr1; /* 0x.014 - Receive Equalization Control Register 1 */
++ __be32 tecr0; /* 0x.018 - Transmit Equalization Control Register 0 */
++ __be32 resv2; /* 0x.01C - Reserved */
++ __be32 tlcr0; /* 0x.020 - TTL Control Register 0 */
++ __be32 tlcr1; /* 0x.024 - TTL Control Register 1 */
++ __be32 tlcr2; /* 0x.028 - TTL Control Register 2 */
++ __be32 tlcr3; /* 0x.02C - TTL Control Register 3 */
++ __be32 tcsr0; /* 0x.030 - Test Control/Status Register 0 */
++ __be32 tcsr1; /* 0x.034 - Test Control/Status Register 1 */
++ __be32 tcsr2; /* 0x.038 - Test Control/Status Register 2 */
++ __be32 tcsr3; /* 0x.03C - Test Control/Status Register 3 */
++};
++
++struct tx_condition {
++ bool bin_m1_late_early;
++ bool bin_long_late_early;
++ bool bin_m1_stop;
++ bool bin_long_stop;
++ bool tx_complete;
++ bool sent_init;
++ int m1_min_max_cnt;
++ int long_min_max_cnt;
++#ifdef NEW_ALGORITHM_TRAIN_TX
++ int pre_inc;
++ int post_inc;
++#endif
++};
++
++struct fsl_xgkr_inst {
++ void *reg_base;
++ struct phy_device *phydev;
++ struct tx_condition tx_c;
++ struct delayed_work xgkr_wk;
++ enum train_state state;
++ u32 ld_update;
++ u32 ld_status;
++ u32 ratio_preq;
++ u32 ratio_pst1q;
++ u32 adpt_eq;
++};
++
++static void tx_condition_init(struct tx_condition *tx_c)
++{
++ tx_c->bin_m1_late_early = true;
++ tx_c->bin_long_late_early = false;
++ tx_c->bin_m1_stop = false;
++ tx_c->bin_long_stop = false;
++ tx_c->tx_complete = false;
++ tx_c->sent_init = false;
++ tx_c->m1_min_max_cnt = 0;
++ tx_c->long_min_max_cnt = 0;
++#ifdef NEW_ALGORITHM_TRAIN_TX
++ tx_c->pre_inc = FORCE_INC_COM1_NUMBER;
++ tx_c->post_inc = FORCE_INC_COP1_NUMBER;
++#endif
++}
++
++void tune_tecr0(struct fsl_xgkr_inst *inst)
++{
++ struct per_lane_ctrl_status *reg_base = inst->reg_base;
++ u32 val;
++
++ val = TECR0_INIT |
++ inst->adpt_eq << ZERO_COE_SHIFT |
++ inst->ratio_preq << PRE_COE_SHIFT |
++ inst->ratio_pst1q << POST_COE_SHIFT;
++
++ /* reset the lane */
++ iowrite32(ioread32(&reg_base->gcr0) & ~GCR0_RESET_MASK,
++ &reg_base->gcr0);
++ udelay(1);
++ iowrite32(val, &reg_base->tecr0);
++ udelay(1);
++ /* unreset the lane */
++ iowrite32(ioread32(&reg_base->gcr0) | GCR0_RESET_MASK,
++ &reg_base->gcr0);
++ udelay(1);
++}
++
++static void start_lt(struct phy_device *phydev)
++{
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_CTRL, TRAIN_EN);
++}
++
++static void stop_lt(struct phy_device *phydev)
++{
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_CTRL, TRAIN_DISABLE);
++}
++
++static void reset_gcr0(struct fsl_xgkr_inst *inst)
++{
++ struct per_lane_ctrl_status *reg_base = inst->reg_base;
++
++ iowrite32(ioread32(&reg_base->gcr0) & ~GCR0_RESET_MASK,
++ &reg_base->gcr0);
++ udelay(1);
++ iowrite32(ioread32(&reg_base->gcr0) | GCR0_RESET_MASK,
++ &reg_base->gcr0);
++ udelay(1);
++}
++
++void lane_set_1gkx(void *reg)
++{
++ struct per_lane_ctrl_status *reg_base = reg;
++ u32 val;
++
++ /* reset the lane */
++ iowrite32(ioread32(&reg_base->gcr0) & ~GCR0_RESET_MASK,
++ &reg_base->gcr0);
++ udelay(1);
++
++ /* set gcr1 for 1GKX */
++ val = ioread32(&reg_base->gcr1);
++ val &= ~(GCR1_REIDL_TH_MASK | GCR1_REIDL_EX_SEL_MASK |
++ GCR1_REIDL_ET_MAS_MASK);
++ iowrite32(val, &reg_base->gcr1);
++ udelay(1);
++
++ /* set tecr0 for 1GKX */
++ val = ioread32(&reg_base->tecr0);
++ val &= ~TECR0_AMP_RED_MASK;
++ iowrite32(val, &reg_base->tecr0);
++ udelay(1);
++
++ /* unreset the lane */
++ iowrite32(ioread32(&reg_base->gcr0) | GCR0_RESET_MASK,
++ &reg_base->gcr0);
++ udelay(1);
++}
++
++static void reset_lt(struct phy_device *phydev)
++{
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, PMD_RESET);
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_CTRL, TRAIN_DISABLE);
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LD_CU, 0);
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LD_STATUS, 0);
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_STATUS, 0);
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_CU, 0);
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_STATUS, 0);
++}
++
++static void start_xgkr_state_machine(struct delayed_work *work)
++{
++ queue_delayed_work(system_power_efficient_wq, work,
++ msecs_to_jiffies(XGKR_TIMEOUT));
++}
++
++static void start_xgkr_an(struct phy_device *phydev)
++{
++ struct fsl_xgkr_inst *inst;
++
++ reset_lt(phydev);
++ phy_write_mmd(phydev, MDIO_MMD_AN, FSL_AN_AD1, KR_AN_AD1_INIT);
++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, AN_CTRL_INIT);
++
++ inst = phydev->priv;
++
++ /* start state machine*/
++ start_xgkr_state_machine(&inst->xgkr_wk);
++}
++
++static void start_1gkx_an(struct phy_device *phydev)
++{
++ phy_write_mmd(phydev, MDIO_MMD_PCS, FSL_PCS_IF_MODE, IF_MODE_INIT);
++ phy_write_mmd(phydev, MDIO_MMD_AN, FSL_AN_AD1, KX_AN_AD1_INIT);
++ phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
++ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, AN_CTRL_INIT);
++}
++
++static void ld_coe_status(struct fsl_xgkr_inst *inst)
++{
++ phy_write_mmd(inst->phydev, MDIO_MMD_PMAPMD,
++ FSL_KR_LD_STATUS, inst->ld_status);
++}
++
++static void ld_coe_update(struct fsl_xgkr_inst *inst)
++{
++ dev_dbg(&inst->phydev->mdio.dev, "sending request: %x\n", inst->ld_update);
++ phy_write_mmd(inst->phydev, MDIO_MMD_PMAPMD,
++ FSL_KR_LD_CU, inst->ld_update);
++}
++
++static void init_inst(struct fsl_xgkr_inst *inst, int reset)
++{
++ if (reset) {
++ inst->ratio_preq = RATIO_PREQ;
++ inst->ratio_pst1q = RATIO_PST1Q;
++ inst->adpt_eq = RATIO_EQ;
++ tune_tecr0(inst);
++ }
++
++ tx_condition_init(&inst->tx_c);
++ inst->state = DETECTING_LP;
++ inst->ld_status &= RX_READY_MASK;
++ ld_coe_status(inst);
++ inst->ld_update = 0;
++ inst->ld_status &= ~RX_READY_MASK;
++ ld_coe_status(inst);
++}
++
++#ifdef NEW_ALGORITHM_TRAIN_TX
++static int get_median_gaink2(u32 *reg)
++{
++ int gaink2_snap_shot[BIN_SNAPSHOT_NUM];
++ u32 rx_eq_snp;
++ struct per_lane_ctrl_status *reg_base;
++ int timeout;
++ int i, j, tmp, pos;
++
++ reg_base = (struct per_lane_ctrl_status *)reg;
++
++ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) {
++ /* wait RECR1_CTL_SNP_DONE_MASK has cleared */
++ timeout = 100;
++ while (ioread32(&reg_base->recr1) &
++ RECR1_CTL_SNP_DONE_MASK) {
++ udelay(1);
++ timeout--;
++ if (timeout == 0)
++ break;
++ }
++
++ /* start snap shot */
++ iowrite32((ioread32(&reg_base->gcr1) |
++ GCR1_CTL_SNP_START_MASK),
++ &reg_base->gcr1);
++
++ /* wait for SNP done */
++ timeout = 100;
++ while (!(ioread32(&reg_base->recr1) &
++ RECR1_CTL_SNP_DONE_MASK)) {
++ udelay(1);
++ timeout--;
++ if (timeout == 0)
++ break;
++ }
++
++ /* read and save the snap shot */
++ rx_eq_snp = ioread32(&reg_base->recr1);
++ gaink2_snap_shot[i] = (rx_eq_snp & RECR1_GAINK2_MASK) >>
++ RECR1_GAINK2_SHIFT;
++
++ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */
++ iowrite32((ioread32(&reg_base->gcr1) &
++ ~GCR1_CTL_SNP_START_MASK),
++ &reg_base->gcr1);
++ }
++
++ /* get median of the 5 snap shot */
++ for (i = 0; i < BIN_SNAPSHOT_NUM - 1; i++) {
++ tmp = gaink2_snap_shot[i];
++ pos = i;
++ for (j = i + 1; j < BIN_SNAPSHOT_NUM; j++) {
++ if (gaink2_snap_shot[j] < tmp) {
++ tmp = gaink2_snap_shot[j];
++ pos = j;
++ }
++ }
++
++ gaink2_snap_shot[pos] = gaink2_snap_shot[i];
++ gaink2_snap_shot[i] = tmp;
++ }
++
++ return gaink2_snap_shot[2];
++}
++#endif
++
++static bool is_bin_early(int bin_sel, void *reg)
++{
++ bool early = false;
++ int bin_snap_shot[BIN_SNAPSHOT_NUM];
++ int i, negative_count = 0;
++ struct per_lane_ctrl_status *reg_base = reg;
++ int timeout;
++
++ for (i = 0; i < BIN_SNAPSHOT_NUM; i++) {
++ /* wait RECR1_SNP_DONE_MASK has cleared */
++ timeout = 100;
++ while ((ioread32(&reg_base->recr1) & RECR1_SNP_DONE_MASK)) {
++ udelay(1);
++ timeout--;
++ if (timeout == 0)
++ break;
++ }
++
++ /* set TCSR1[CDR_SEL] to BinM1/BinLong */
++ if (bin_sel == BIN_M1) {
++ iowrite32((ioread32(&reg_base->tcsr1) &
++ ~CDR_SEL_MASK) | BIN_M1_SEL,
++ &reg_base->tcsr1);
++ } else {
++ iowrite32((ioread32(&reg_base->tcsr1) &
++ ~CDR_SEL_MASK) | BIN_Long_SEL,
++ &reg_base->tcsr1);
++ }
++
++ /* start snap shot */
++ iowrite32(ioread32(&reg_base->gcr1) | GCR1_SNP_START_MASK,
++ &reg_base->gcr1);
++
++ /* wait for SNP done */
++ timeout = 100;
++ while (!(ioread32(&reg_base->recr1) & RECR1_SNP_DONE_MASK)) {
++ udelay(1);
++ timeout--;
++ if (timeout == 0)
++ break;
++ }
++
++ /* read and save the snap shot */
++ bin_snap_shot[i] = (ioread32(&reg_base->tcsr1) &
++ TCSR1_SNP_DATA_MASK) >> TCSR1_SNP_DATA_SHIFT;
++ if (bin_snap_shot[i] & TCSR1_EQ_SNPBIN_SIGN_MASK)
++ negative_count++;
++
++ /* terminate the snap shot by setting GCR1[REQ_CTL_SNP] */
++ iowrite32(ioread32(&reg_base->gcr1) & ~GCR1_SNP_START_MASK,
++ &reg_base->gcr1);
++ }
++
++ if (((bin_sel == BIN_M1) && (negative_count > BIN_M1_THRESHOLD)) ||
++ ((bin_sel == BIN_LONG && (negative_count > BIN_LONG_THRESHOLD)))) {
++ early = true;
++ }
++
++ return early;
++}
++
++static void train_tx(struct fsl_xgkr_inst *inst)
++{
++ struct phy_device *phydev = inst->phydev;
++ struct tx_condition *tx_c = &inst->tx_c;
++ bool bin_m1_early, bin_long_early;
++ u32 lp_status, old_ld_update;
++ u32 status_cop1, status_coz, status_com1;
++ u32 req_cop1, req_coz, req_com1, req_preset, req_init;
++ u32 temp;
++#ifdef NEW_ALGORITHM_TRAIN_TX
++ u32 median_gaink2;
++#endif
++
++recheck:
++ if (tx_c->bin_long_stop && tx_c->bin_m1_stop) {
++ tx_c->tx_complete = true;
++ inst->ld_status |= RX_READY_MASK;
++ ld_coe_status(inst);
++ /* tell LP we are ready */
++ phy_write_mmd(phydev, MDIO_MMD_PMAPMD,
++ FSL_KR_PMD_STATUS, RX_STAT);
++ return;
++ }
++
++ /* We start by checking the current LP status. If we got any responses,
++ * we can clear up the appropriate update request so that the
++ * subsequent code may easily issue new update requests if needed.
++ */
++ lp_status = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_STATUS) &
++ REQUEST_MASK;
++ status_cop1 = (lp_status & COP1_MASK) >> COP1_SHIFT;
++ status_coz = (lp_status & COZ_MASK) >> COZ_SHIFT;
++ status_com1 = (lp_status & COM1_MASK) >> COM1_SHIFT;
++
++ old_ld_update = inst->ld_update;
++ req_cop1 = (old_ld_update & COP1_MASK) >> COP1_SHIFT;
++ req_coz = (old_ld_update & COZ_MASK) >> COZ_SHIFT;
++ req_com1 = (old_ld_update & COM1_MASK) >> COM1_SHIFT;
++ req_preset = old_ld_update & PRESET_MASK;
++ req_init = old_ld_update & INIT_MASK;
++
++ /* IEEE802.3-2008, 72.6.10.2.3.1
++ * We may clear PRESET when all coefficients show UPDATED or MAX.
++ */
++ if (req_preset) {
++ if ((status_cop1 == COE_UPDATED || status_cop1 == COE_MAX) &&
++ (status_coz == COE_UPDATED || status_coz == COE_MAX) &&
++ (status_com1 == COE_UPDATED || status_com1 == COE_MAX)) {
++ inst->ld_update &= ~PRESET_MASK;
++ }
++ }
++
++ /* IEEE802.3-2008, 72.6.10.2.3.2
++ * We may clear INITIALIZE when no coefficients show NOT UPDATED.
++ */
++ if (req_init) {
++ if (status_cop1 != COE_NOTUPDATED &&
++ status_coz != COE_NOTUPDATED &&
++ status_com1 != COE_NOTUPDATED) {
++ inst->ld_update &= ~INIT_MASK;
++ }
++ }
++
++ /* IEEE802.3-2008, 72.6.10.2.3.2
++ * we send initialize to the other side to ensure default settings
++ * for the LP. Naturally, we should do this only once.
++ */
++ if (!tx_c->sent_init) {
++ if (!lp_status && !(old_ld_update & (LD_ALL_MASK))) {
++ inst->ld_update = INIT_MASK;
++ tx_c->sent_init = true;
++ }
++ }
++
++ /* IEEE802.3-2008, 72.6.10.2.3.3
++ * We set coefficient requests to HOLD when we get the information
++ * about any updates On clearing our prior response, we also update
++ * our internal status.
++ */
++ if (status_cop1 != COE_NOTUPDATED) {
++ if (req_cop1) {
++ inst->ld_update &= ~COP1_MASK;
++#ifdef NEW_ALGORITHM_TRAIN_TX
++ if (tx_c->post_inc) {
++ if (req_cop1 == INCREMENT &&
++ status_cop1 == COE_MAX) {
++ tx_c->post_inc = 0;
++ tx_c->bin_long_stop = true;
++ tx_c->bin_m1_stop = true;
++ } else {
++ tx_c->post_inc -= 1;
++ }
++
++ ld_coe_update(inst);
++ goto recheck;
++ }
++#endif
++ if ((req_cop1 == DECREMENT && status_cop1 == COE_MIN) ||
++ (req_cop1 == INCREMENT && status_cop1 == COE_MAX)) {
++ dev_dbg(&inst->phydev->mdio.dev, "COP1 hit limit %s",
++ (status_cop1 == COE_MIN) ?
++ "DEC MIN" : "INC MAX");
++ tx_c->long_min_max_cnt++;
++ if (tx_c->long_min_max_cnt >= TIMEOUT_LONG) {
++ tx_c->bin_long_stop = true;
++ ld_coe_update(inst);
++ goto recheck;
++ }
++ }
++ }
++ }
++
++ if (status_coz != COE_NOTUPDATED) {
++ if (req_coz)
++ inst->ld_update &= ~COZ_MASK;
++ }
++
++ if (status_com1 != COE_NOTUPDATED) {
++ if (req_com1) {
++ inst->ld_update &= ~COM1_MASK;
++#ifdef NEW_ALGORITHM_TRAIN_TX
++ if (tx_c->pre_inc) {
++ if (req_com1 == INCREMENT &&
++ status_com1 == COE_MAX)
++ tx_c->pre_inc = 0;
++ else
++ tx_c->pre_inc -= 1;
++
++ ld_coe_update(inst);
++ goto recheck;
++ }
++#endif
++ /* Stop If we have reached the limit for a parameter. */
++ if ((req_com1 == DECREMENT && status_com1 == COE_MIN) ||
++ (req_com1 == INCREMENT && status_com1 == COE_MAX)) {
++ dev_dbg(&inst->phydev->mdio.dev, "COM1 hit limit %s",
++ (status_com1 == COE_MIN) ?
++ "DEC MIN" : "INC MAX");
++ tx_c->m1_min_max_cnt++;
++ if (tx_c->m1_min_max_cnt >= TIMEOUT_M1) {
++ tx_c->bin_m1_stop = true;
++ ld_coe_update(inst);
++ goto recheck;
++ }
++ }
++ }
++ }
++
++ if (old_ld_update != inst->ld_update) {
++ ld_coe_update(inst);
++ /* Redo these status checks and updates until we have no more
++ * changes, to speed up the overall process.
++ */
++ goto recheck;
++ }
++
++ /* Do nothing if we have pending request. */
++ if ((req_coz || req_com1 || req_cop1))
++ return;
++ else if (lp_status)
++ /* No pending request but LP status was not reverted to
++ * not updated.
++ */
++ return;
++
++#ifdef NEW_ALGORITHM_TRAIN_TX
++ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) {
++ if (tx_c->pre_inc) {
++ inst->ld_update = INCREMENT << COM1_SHIFT;
++ ld_coe_update(inst);
++ return;
++ }
++
++ if (status_cop1 != COE_MAX) {
++ median_gaink2 = get_median_gaink2(inst->reg_base);
++ if (median_gaink2 == 0xf) {
++ tx_c->post_inc = 1;
++ } else {
++ /* Gaink2 median lower than "F" */
++ tx_c->bin_m1_stop = true;
++ tx_c->bin_long_stop = true;
++ goto recheck;
++ }
++ } else {
++ /* C1 MAX */
++ tx_c->bin_m1_stop = true;
++ tx_c->bin_long_stop = true;
++ goto recheck;
++ }
++
++ if (tx_c->post_inc) {
++ inst->ld_update = INCREMENT << COP1_SHIFT;
++ ld_coe_update(inst);
++ return;
++ }
++ }
++#endif
++
++ /* snapshot and select bin */
++ bin_m1_early = is_bin_early(BIN_M1, inst->reg_base);
++ bin_long_early = is_bin_early(BIN_LONG, inst->reg_base);
++
++ if (!tx_c->bin_m1_stop && !tx_c->bin_m1_late_early && bin_m1_early) {
++ tx_c->bin_m1_stop = true;
++ goto recheck;
++ }
++
++ if (!tx_c->bin_long_stop &&
++ tx_c->bin_long_late_early && !bin_long_early) {
++ tx_c->bin_long_stop = true;
++ goto recheck;
++ }
++
++ /* IEEE802.3-2008, 72.6.10.2.3.3
++ * We only request coefficient updates when no PRESET/INITIALIZE is
++ * pending. We also only request coefficient updates when the
++ * corresponding status is NOT UPDATED and nothing is pending.
++ */
++ if (!(inst->ld_update & (PRESET_MASK | INIT_MASK))) {
++ if (!tx_c->bin_long_stop) {
++ /* BinM1 correction means changing COM1 */
++ if (!status_com1 && !(inst->ld_update & COM1_MASK)) {
++ /* Avoid BinM1Late by requesting an
++ * immediate decrement.
++ */
++ if (!bin_m1_early) {
++ /* request decrement c(-1) */
++ temp = DECREMENT << COM1_SHIFT;
++ inst->ld_update = temp;
++ ld_coe_update(inst);
++ tx_c->bin_m1_late_early = bin_m1_early;
++ return;
++ }
++ }
++
++ /* BinLong correction means changing COP1 */
++ if (!status_cop1 && !(inst->ld_update & COP1_MASK)) {
++ /* Locate BinLong transition point (if any)
++ * while avoiding BinM1Late.
++ */
++ if (bin_long_early) {
++ /* request increment c(1) */
++ temp = INCREMENT << COP1_SHIFT;
++ inst->ld_update = temp;
++ } else {
++ /* request decrement c(1) */
++ temp = DECREMENT << COP1_SHIFT;
++ inst->ld_update = temp;
++ }
++
++ ld_coe_update(inst);
++ tx_c->bin_long_late_early = bin_long_early;
++ }
++ /* We try to finish BinLong before we do BinM1 */
++ return;
++ }
++
++ if (!tx_c->bin_m1_stop) {
++ /* BinM1 correction means changing COM1 */
++ if (!status_com1 && !(inst->ld_update & COM1_MASK)) {
++ /* Locate BinM1 transition point (if any) */
++ if (bin_m1_early) {
++ /* request increment c(-1) */
++ temp = INCREMENT << COM1_SHIFT;
++ inst->ld_update = temp;
++ } else {
++ /* request decrement c(-1) */
++ temp = DECREMENT << COM1_SHIFT;
++ inst->ld_update = temp;
++ }
++
++ ld_coe_update(inst);
++ tx_c->bin_m1_late_early = bin_m1_early;
++ }
++ }
++ }
++}
++
++static int is_link_up(struct phy_device *phydev)
++{
++ int val;
++
++ phy_read_mmd(phydev, MDIO_MMD_PCS, FSL_XFI_PCS_10GR_SR1);
++ val = phy_read_mmd(phydev, MDIO_MMD_PCS, FSL_XFI_PCS_10GR_SR1);
++
++ return (val & FSL_KR_RX_LINK_STAT_MASK) ? 1 : 0;
++}
++
++static int is_link_training_fail(struct phy_device *phydev)
++{
++ int val;
++ int timeout = 100;
++
++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_PMD_STATUS);
++ if (!(val & TRAIN_FAIL) && (val & RX_STAT)) {
++ /* check LNK_STAT for sure */
++ while (timeout--) {
++ if (is_link_up(phydev))
++ return 0;
++
++ usleep_range(100, 500);
++ }
++ }
++
++ return 1;
++}
++
++static int check_rx(struct phy_device *phydev)
++{
++ return phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_STATUS) &
++ RX_READY_MASK;
++}
++
++/* Coefficient values have hardware restrictions */
++static int is_ld_valid(struct fsl_xgkr_inst *inst)
++{
++ u32 ratio_pst1q = inst->ratio_pst1q;
++ u32 adpt_eq = inst->adpt_eq;
++ u32 ratio_preq = inst->ratio_preq;
++
++ if ((ratio_pst1q + adpt_eq + ratio_preq) > 48)
++ return 0;
++
++ if (((ratio_pst1q + adpt_eq + ratio_preq) * 4) >=
++ ((adpt_eq - ratio_pst1q - ratio_preq) * 17))
++ return 0;
++
++ if (ratio_preq > ratio_pst1q)
++ return 0;
++
++ if (ratio_preq > 8)
++ return 0;
++
++ if (adpt_eq < 26)
++ return 0;
++
++ if (ratio_pst1q > 16)
++ return 0;
++
++ return 1;
++}
++
++static int is_value_allowed(const u32 *val_table, u32 val)
++{
++ int i;
++
++ for (i = 0;; i++) {
++ if (*(val_table + i) == VAL_INVALID)
++ return 0;
++ if (*(val_table + i) == val)
++ return 1;
++ }
++}
++
++static int inc_dec(struct fsl_xgkr_inst *inst, int field, int request)
++{
++ u32 ld_limit[3], ld_coe[3], step[3];
++
++ ld_coe[0] = inst->ratio_pst1q;
++ ld_coe[1] = inst->adpt_eq;
++ ld_coe[2] = inst->ratio_preq;
++
++ /* Information specific to the Freescale SerDes for 10GBase-KR:
++ * Incrementing C(+1) means *decrementing* RATIO_PST1Q
++ * Incrementing C(0) means incrementing ADPT_EQ
++ * Incrementing C(-1) means *decrementing* RATIO_PREQ
++ */
++ step[0] = -1;
++ step[1] = 1;
++ step[2] = -1;
++
++ switch (request) {
++ case INCREMENT:
++ ld_limit[0] = POST_COE_MAX;
++ ld_limit[1] = ZERO_COE_MAX;
++ ld_limit[2] = PRE_COE_MAX;
++ if (ld_coe[field] != ld_limit[field])
++ ld_coe[field] += step[field];
++ else
++ /* MAX */
++ return 2;
++ break;
++ case DECREMENT:
++ ld_limit[0] = POST_COE_MIN;
++ ld_limit[1] = ZERO_COE_MIN;
++ ld_limit[2] = PRE_COE_MIN;
++ if (ld_coe[field] != ld_limit[field])
++ ld_coe[field] -= step[field];
++ else
++ /* MIN */
++ return 1;
++ break;
++ default:
++ break;
++ }
++
++ if (is_ld_valid(inst)) {
++ /* accept new ld */
++ inst->ratio_pst1q = ld_coe[0];
++ inst->adpt_eq = ld_coe[1];
++ inst->ratio_preq = ld_coe[2];
++ /* only some values for preq and pst1q can be used.
++ * for preq: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xc.
++ * for pst1q: 0x0, 0x1, 0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf, 0x10.
++ */
++ if (!is_value_allowed((const u32 *)&preq_table, ld_coe[2])) {
++ dev_dbg(&inst->phydev->mdio.dev,
++ "preq skipped value: %d\n", ld_coe[2]);
++ return 0;
++ }
++
++ if (!is_value_allowed((const u32 *)&pst1q_table, ld_coe[0])) {
++ dev_dbg(&inst->phydev->mdio.dev,
++ "pst1q skipped value: %d\n", ld_coe[0]);
++ return 0;
++ }
++
++ tune_tecr0(inst);
++ } else {
++ if (request == DECREMENT)
++ /* MIN */
++ return 1;
++ if (request == INCREMENT)
++ /* MAX */
++ return 2;
++ }
++
++ return 0;
++}
++
++static void min_max_updated(struct fsl_xgkr_inst *inst, int field, int new_ld)
++{
++ u32 ld_coe[] = {COE_UPDATED, COE_MIN, COE_MAX};
++ u32 mask, val;
++
++ switch (field) {
++ case COE_COP1:
++ mask = COP1_MASK;
++ val = ld_coe[new_ld] << COP1_SHIFT;
++ break;
++ case COE_COZ:
++ mask = COZ_MASK;
++ val = ld_coe[new_ld] << COZ_SHIFT;
++ break;
++ case COE_COM:
++ mask = COM1_MASK;
++ val = ld_coe[new_ld] << COM1_SHIFT;
++ break;
++ default:
++ return;
++ }
++
++ inst->ld_status &= ~mask;
++ inst->ld_status |= val;
++}
++
++static void check_request(struct fsl_xgkr_inst *inst, int request)
++{
++ int cop1_req, coz_req, com_req;
++ int old_status, new_ld_sta;
++
++ cop1_req = (request & COP1_MASK) >> COP1_SHIFT;
++ coz_req = (request & COZ_MASK) >> COZ_SHIFT;
++ com_req = (request & COM1_MASK) >> COM1_SHIFT;
++
++ /* IEEE802.3-2008, 72.6.10.2.5
++ * Ensure we only act on INCREMENT/DECREMENT when we are in NOT UPDATED
++ */
++ old_status = inst->ld_status;
++
++ if (cop1_req && !(inst->ld_status & COP1_MASK)) {
++ new_ld_sta = inc_dec(inst, COE_COP1, cop1_req);
++ min_max_updated(inst, COE_COP1, new_ld_sta);
++ }
++
++ if (coz_req && !(inst->ld_status & COZ_MASK)) {
++ new_ld_sta = inc_dec(inst, COE_COZ, coz_req);
++ min_max_updated(inst, COE_COZ, new_ld_sta);
++ }
++
++ if (com_req && !(inst->ld_status & COM1_MASK)) {
++ new_ld_sta = inc_dec(inst, COE_COM, com_req);
++ min_max_updated(inst, COE_COM, new_ld_sta);
++ }
++
++ if (old_status != inst->ld_status)
++ ld_coe_status(inst);
++}
++
++static void preset(struct fsl_xgkr_inst *inst)
++{
++ /* These are all MAX values from the IEEE802.3 perspective. */
++ inst->ratio_pst1q = POST_COE_MAX;
++ inst->adpt_eq = ZERO_COE_MAX;
++ inst->ratio_preq = PRE_COE_MAX;
++
++ tune_tecr0(inst);
++ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK);
++ inst->ld_status |= COE_MAX << COP1_SHIFT |
++ COE_MAX << COZ_SHIFT |
++ COE_MAX << COM1_SHIFT;
++ ld_coe_status(inst);
++}
++
++static void initialize(struct fsl_xgkr_inst *inst)
++{
++ inst->ratio_preq = RATIO_PREQ;
++ inst->ratio_pst1q = RATIO_PST1Q;
++ inst->adpt_eq = RATIO_EQ;
++
++ tune_tecr0(inst);
++ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK);
++ inst->ld_status |= COE_UPDATED << COP1_SHIFT |
++ COE_UPDATED << COZ_SHIFT |
++ COE_UPDATED << COM1_SHIFT;
++ ld_coe_status(inst);
++}
++
++static void train_rx(struct fsl_xgkr_inst *inst)
++{
++ struct phy_device *phydev = inst->phydev;
++ int request, old_ld_status;
++
++ /* get request from LP */
++ request = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, FSL_KR_LP_CU) &
++ (LD_ALL_MASK);
++ old_ld_status = inst->ld_status;
++
++ /* IEEE802.3-2008, 72.6.10.2.5
++ * Ensure we always go to NOT UDPATED for status reporting in
++ * response to HOLD requests.
++ * IEEE802.3-2008, 72.6.10.2.3.1/2
++ * ... but only if PRESET/INITIALIZE are not active to ensure
++ * we keep status until they are released.
++ */
++ if (!(request & (PRESET_MASK | INIT_MASK))) {
++ if (!(request & COP1_MASK))
++ inst->ld_status &= ~COP1_MASK;
++
++ if (!(request & COZ_MASK))
++ inst->ld_status &= ~COZ_MASK;
++
++ if (!(request & COM1_MASK))
++ inst->ld_status &= ~COM1_MASK;
++
++ if (old_ld_status != inst->ld_status)
++ ld_coe_status(inst);
++ }
++
++ /* As soon as the LP shows ready, no need to do any more updates. */
++ if (check_rx(phydev)) {
++ /* LP receiver is ready */
++ if (inst->ld_status & (COP1_MASK | COZ_MASK | COM1_MASK)) {
++ inst->ld_status &= ~(COP1_MASK | COZ_MASK | COM1_MASK);
++ ld_coe_status(inst);
++ }
++ } else {
++ /* IEEE802.3-2008, 72.6.10.2.3.1/2
++ * only act on PRESET/INITIALIZE if all status is NOT UPDATED.
++ */
++ if (request & (PRESET_MASK | INIT_MASK)) {
++ if (!(inst->ld_status &
++ (COP1_MASK | COZ_MASK | COM1_MASK))) {
++ if (request & PRESET_MASK)
++ preset(inst);
++
++ if (request & INIT_MASK)
++ initialize(inst);
++ }
++ }
++
++ /* LP Coefficient are not in HOLD */
++ if (request & REQUEST_MASK)
++ check_request(inst, request & REQUEST_MASK);
++ }
++}
++
++static void xgkr_start_train(struct phy_device *phydev)
++{
++ struct fsl_xgkr_inst *inst = phydev->priv;
++ struct tx_condition *tx_c = &inst->tx_c;
++ int val = 0, i;
++ int lt_state;
++ unsigned long dead_line;
++ int rx_ok, tx_ok;
++
++ init_inst(inst, 0);
++ start_lt(phydev);
++
++ for (i = 0; i < 2;) {
++ dead_line = jiffies + msecs_to_jiffies(500);
++ while (time_before(jiffies, dead_line)) {
++ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
++ FSL_KR_PMD_STATUS);
++ if (val & TRAIN_FAIL) {
++ /* LT failed already, reset lane to avoid
++ * it run into hanging, then start LT again.
++ */
++ reset_gcr0(inst);
++ start_lt(phydev);
++ } else if ((val & PMD_STATUS_SUP_STAT) &&
++ (val & PMD_STATUS_FRAME_LOCK))
++ break;
++ usleep_range(100, 500);
++ }
++
++ if (!((val & PMD_STATUS_FRAME_LOCK) &&
++ (val & PMD_STATUS_SUP_STAT))) {
++ i++;
++ continue;
++ }
++
++ /* init process */
++ rx_ok = false;
++ tx_ok = false;
++ /* the LT should be finished in 500ms, failed or OK. */
++ dead_line = jiffies + msecs_to_jiffies(500);
++
++ while (time_before(jiffies, dead_line)) {
++ /* check if the LT is already failed */
++ lt_state = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
++ FSL_KR_PMD_STATUS);
++ if (lt_state & TRAIN_FAIL) {
++ reset_gcr0(inst);
++ break;
++ }
++
++ rx_ok = check_rx(phydev);
++ tx_ok = tx_c->tx_complete;
++
++ if (rx_ok && tx_ok)
++ break;
++
++ if (!rx_ok)
++ train_rx(inst);
++
++ if (!tx_ok)
++ train_tx(inst);
++
++ usleep_range(100, 500);
++ }
++
++ i++;
++ /* check LT result */
++ if (is_link_training_fail(phydev)) {
++ init_inst(inst, 0);
++ continue;
++ } else {
++ stop_lt(phydev);
++ inst->state = TRAINED;
++ break;
++ }
++ }
++}
++
++static void xgkr_state_machine(struct work_struct *work)
++{
++ struct delayed_work *dwork = to_delayed_work(work);
++ struct fsl_xgkr_inst *inst = container_of(dwork,
++ struct fsl_xgkr_inst,
++ xgkr_wk);
++ struct phy_device *phydev = inst->phydev;
++ int an_state;
++ bool needs_train = false;
++
++ mutex_lock(&phydev->lock);
++
++ switch (inst->state) {
++ case DETECTING_LP:
++ phy_read_mmd(phydev, MDIO_MMD_AN, FSL_AN_BP_STAT);
++ an_state = phy_read_mmd(phydev, MDIO_MMD_AN, FSL_AN_BP_STAT);
++ if ((an_state & KR_AN_MASK))
++ needs_train = true;
++ break;
++ case TRAINED:
++ if (!is_link_up(phydev)) {
++ dev_info(&phydev->mdio.dev,
++ "Detect hotplug, restart training\n");
++ init_inst(inst, 1);
++ start_xgkr_an(phydev);
++ inst->state = DETECTING_LP;
++ }
++ break;
++ }
++
++ if (needs_train)
++ xgkr_start_train(phydev);
++
++ mutex_unlock(&phydev->lock);
++ queue_delayed_work(system_power_efficient_wq, &inst->xgkr_wk,
++ msecs_to_jiffies(XGKR_TIMEOUT));
++}
++
++static int fsl_backplane_probe(struct phy_device *phydev)
++{
++ struct fsl_xgkr_inst *xgkr_inst;
++ struct device_node *phy_node, *lane_node;
++ struct resource res_lane;
++ const char *bm;
++ int ret;
++ int bp_mode;
++ u32 lane[2];
++
++ phy_node = phydev->mdio.dev.of_node;
++ bp_mode = of_property_read_string(phy_node, "backplane-mode", &bm);
++ if (bp_mode < 0)
++ return 0;
++
++ if (!strcasecmp(bm, "1000base-kx")) {
++ bp_mode = PHY_BACKPLANE_1000BASE_KX;
++ } else if (!strcasecmp(bm, "10gbase-kr")) {
++ bp_mode = PHY_BACKPLANE_10GBASE_KR;
++ } else {
++ dev_err(&phydev->mdio.dev, "Unknown backplane-mode\n");
++ return -EINVAL;
++ }
++
++ lane_node = of_parse_phandle(phy_node, "fsl,lane-handle", 0);
++ if (!lane_node) {
++ dev_err(&phydev->mdio.dev, "parse fsl,lane-handle failed\n");
++ return -EINVAL;
++ }
++
++ ret = of_address_to_resource(lane_node, 0, &res_lane);
++ if (ret) {
++ dev_err(&phydev->mdio.dev, "could not obtain memory map\n");
++ return ret;
++ }
++
++ of_node_put(lane_node);
++ ret = of_property_read_u32_array(phy_node, "fsl,lane-reg",
++ (u32 *)&lane, 2);
++ if (ret) {
++ dev_err(&phydev->mdio.dev, "could not get fsl,lane-reg\n");
++ return -EINVAL;
++ }
++
++ phydev->priv = devm_ioremap_nocache(&phydev->mdio.dev,
++ res_lane.start + lane[0],
++ lane[1]);
++ if (!phydev->priv) {
++ dev_err(&phydev->mdio.dev, "ioremap_nocache failed\n");
++ return -ENOMEM;
++ }
++
++ if (bp_mode == PHY_BACKPLANE_1000BASE_KX) {
++ phydev->speed = SPEED_1000;
++ /* configure the lane for 1000BASE-KX */
++ lane_set_1gkx(phydev->priv);
++ return 0;
++ }
++
++ xgkr_inst = devm_kzalloc(&phydev->mdio.dev,
++ sizeof(*xgkr_inst), GFP_KERNEL);
++ if (!xgkr_inst)
++ return -ENOMEM;
++
++ xgkr_inst->reg_base = phydev->priv;
++ xgkr_inst->phydev = phydev;
++ phydev->priv = xgkr_inst;
++
++ if (bp_mode == PHY_BACKPLANE_10GBASE_KR) {
++ phydev->speed = SPEED_10000;
++ INIT_DELAYED_WORK(&xgkr_inst->xgkr_wk, xgkr_state_machine);
++ }
++
++ return 0;
++}
++
++static int fsl_backplane_aneg_done(struct phy_device *phydev)
++{
++ return 1;
++}
++
++static int fsl_backplane_config_aneg(struct phy_device *phydev)
++{
++ if (phydev->speed == SPEED_10000) {
++ phydev->supported |= SUPPORTED_10000baseKR_Full;
++ start_xgkr_an(phydev);
++ } else if (phydev->speed == SPEED_1000) {
++ phydev->supported |= SUPPORTED_1000baseKX_Full;
++ start_1gkx_an(phydev);
++ }
++
++ phydev->advertising = phydev->supported;
++ phydev->duplex = 1;
++
++ return 0;
++}
++
++static int fsl_backplane_suspend(struct phy_device *phydev)
++{
++ if (phydev->speed == SPEED_10000) {
++ struct fsl_xgkr_inst *xgkr_inst = phydev->priv;
++
++ cancel_delayed_work_sync(&xgkr_inst->xgkr_wk);
++ }
++ return 0;
++}
++
++static int fsl_backplane_resume(struct phy_device *phydev)
++{
++ if (phydev->speed == SPEED_10000) {
++ struct fsl_xgkr_inst *xgkr_inst = phydev->priv;
++
++ init_inst(xgkr_inst, 1);
++ queue_delayed_work(system_power_efficient_wq,
++ &xgkr_inst->xgkr_wk,
++ msecs_to_jiffies(XGKR_TIMEOUT));
++ }
++ return 0;
++}
++
++static int fsl_backplane_read_status(struct phy_device *phydev)
++{
++ if (is_link_up(phydev))
++ phydev->link = 1;
++ else
++ phydev->link = 0;
++
++ return 0;
++}
++
++static struct phy_driver fsl_backplane_driver[] = {
++ {
++ .phy_id = FSL_PCS_PHY_ID,
++ .name = "Freescale Backplane",
++ .phy_id_mask = 0xffffffff,
++ .features = SUPPORTED_Backplane | SUPPORTED_Autoneg |
++ SUPPORTED_MII,
++ .probe = fsl_backplane_probe,
++ .aneg_done = fsl_backplane_aneg_done,
++ .config_aneg = fsl_backplane_config_aneg,
++ .read_status = fsl_backplane_read_status,
++ .suspend = fsl_backplane_suspend,
++ .resume = fsl_backplane_resume,
++ },
++};
++
++module_phy_driver(fsl_backplane_driver);
++
++static struct mdio_device_id __maybe_unused freescale_tbl[] = {
++ { FSL_PCS_PHY_ID, 0xffffffff },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, freescale_tbl);
++
++MODULE_DESCRIPTION("Freescale Backplane driver");
++MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
+index f3e64a89..42cdd5b7 100644
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -585,7 +585,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
+ return 0;
+
+ case SIOCSHWTSTAMP:
+- if (phydev->drv->hwtstamp)
++ if (phydev->drv && phydev->drv->hwtstamp)
+ return phydev->drv->hwtstamp(phydev, ifr);
+ /* fall through */
+
+@@ -610,6 +610,9 @@ static int phy_start_aneg_priv(struct phy_device *phydev, bool sync)
+ bool trigger = 0;
+ int err;
+
++ if (!phydev->drv)
++ return -EIO;
++
+ mutex_lock(&phydev->lock);
+
+ if (AUTONEG_DISABLE == phydev->autoneg)
+@@ -1009,7 +1012,7 @@ void phy_state_machine(struct work_struct *work)
+
+ old_state = phydev->state;
+
+- if (phydev->drv->link_change_notify)
++ if (phydev->drv && phydev->drv->link_change_notify)
+ phydev->drv->link_change_notify(phydev);
+
+ switch (phydev->state) {
+@@ -1311,6 +1314,9 @@ EXPORT_SYMBOL(phy_write_mmd_indirect);
+ */
+ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
+ {
++ if (!phydev->drv)
++ return -EIO;
++
+ /* According to 802.3az,the EEE is supported only in full duplex-mode.
+ * Also EEE feature is active when core is operating with MII, GMII
+ * or RGMII (all kinds). Internal PHYs are also allowed to proceed and
+@@ -1388,6 +1394,9 @@ EXPORT_SYMBOL(phy_init_eee);
+ */
+ int phy_get_eee_err(struct phy_device *phydev)
+ {
++ if (!phydev->drv)
++ return -EIO;
++
+ return phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_WK_ERR, MDIO_MMD_PCS);
+ }
+ EXPORT_SYMBOL(phy_get_eee_err);
+@@ -1404,6 +1413,9 @@ int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
+ {
+ int val;
+
++ if (!phydev->drv)
++ return -EIO;
++
+ /* Get Supported EEE */
+ val = phy_read_mmd_indirect(phydev, MDIO_PCS_EEE_ABLE, MDIO_MMD_PCS);
+ if (val < 0)
+@@ -1437,6 +1449,9 @@ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
+ {
+ int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised);
+
++ if (!phydev->drv)
++ return -EIO;
++
+ /* Mask prohibited EEE modes */
+ val &= ~phydev->eee_broken_modes;
+
+@@ -1448,7 +1463,7 @@ EXPORT_SYMBOL(phy_ethtool_set_eee);
+
+ int phy_ethtool_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+ {
+- if (phydev->drv->set_wol)
++ if (phydev->drv && phydev->drv->set_wol)
+ return phydev->drv->set_wol(phydev, wol);
+
+ return -EOPNOTSUPP;
+@@ -1457,7 +1472,7 @@ EXPORT_SYMBOL(phy_ethtool_set_wol);
+
+ void phy_ethtool_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+ {
+- if (phydev->drv->get_wol)
++ if (phydev->drv && phydev->drv->get_wol)
+ phydev->drv->get_wol(phydev, wol);
+ }
+ EXPORT_SYMBOL(phy_ethtool_get_wol);
+diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
+index 5fdc491e..039f9664 100644
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1046,7 +1046,7 @@ int phy_suspend(struct phy_device *phydev)
+ if (wol.wolopts)
+ return -EBUSY;
+
+- if (phydrv->suspend)
++ if (phydev->drv && phydrv->suspend)
+ ret = phydrv->suspend(phydev);
+
+ if (ret)
+@@ -1063,7 +1063,7 @@ int phy_resume(struct phy_device *phydev)
+ struct phy_driver *phydrv = to_phy_driver(phydev->mdio.dev.driver);
+ int ret = 0;
+
+- if (phydrv->resume)
++ if (phydev->drv && phydrv->resume)
+ ret = phydrv->resume(phydev);
+
+ if (ret)
+@@ -1726,7 +1726,7 @@ static int phy_remove(struct device *dev)
+ phydev->state = PHY_DOWN;
+ mutex_unlock(&phydev->lock);
+
+- if (phydev->drv->remove)
++ if (phydev->drv && phydev->drv->remove)
+ phydev->drv->remove(phydev);
+ phydev->drv = NULL;
+
+diff --git a/drivers/net/phy/swphy.c b/drivers/net/phy/swphy.c
+index 34f58f23..52ddddbe 100644
+--- a/drivers/net/phy/swphy.c
++++ b/drivers/net/phy/swphy.c
+@@ -77,6 +77,7 @@ static const struct swmii_regs duplex[] = {
+ static int swphy_decode_speed(int speed)
+ {
+ switch (speed) {
++ case 10000:
+ case 1000:
+ return SWMII_SPEED_1000;
+ case 100:
+diff --git a/include/linux/phy.h b/include/linux/phy.h
+index 850c8b51..5f253f1a 100644
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -81,6 +81,7 @@ typedef enum {
+ PHY_INTERFACE_MODE_MOCA,
+ PHY_INTERFACE_MODE_QSGMII,
+ PHY_INTERFACE_MODE_TRGMII,
++ PHY_INTERFACE_MODE_SGMII_2500,
+ PHY_INTERFACE_MODE_MAX,
+ } phy_interface_t;
+
+@@ -784,6 +785,9 @@ int phy_stop_interrupts(struct phy_device *phydev);
+
+ static inline int phy_read_status(struct phy_device *phydev)
+ {
++ if (!phydev->drv)
++ return -EIO;
++
+ return phydev->drv->read_status(phydev);
+ }
+
+--
+2.14.1
+