Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/phy/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 168 kB image not shown  

Quelle  micrel.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
/*
 * drivers/net/phy/micrel.c
 *
 * Driver for Micrel PHYs
 *
 * Author: David J. Choi
 *
 * Copyright (c) 2010-2013 Micrel, Inc.
 * Copyright (c) 2014 Johan Hovold <johan@kernel.org>
 *
 * Support : Micrel Phys:
 * Giga phys: ksz9021, ksz9031, ksz9131, lan8841, lan8814
 * 100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041
 *    ksz8021, ksz8031, ksz8051,
 *    ksz8081, ksz8091,
 *    ksz8061,
 * Switch : ksz8873, ksz886x
 *  ksz9477, lan8804
 */


#include <linux/bitfield.h>
#include <linux/ethtool_netlink.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/micrel_phy.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/ptp_clock.h>
#include <linux/ptp_classify.h>
#include <linux/net_tstamp.h>
#include <linux/gpio/consumer.h>

#include "phylib.h"

/* Operation Mode Strap Override */
#define MII_KSZPHY_OMSO    0x16
#define KSZPHY_OMSO_FACTORY_TEST  BIT(15)
#define KSZPHY_OMSO_B_CAST_OFF   BIT(9)
#define KSZPHY_OMSO_NAND_TREE_ON  BIT(5)
#define KSZPHY_OMSO_RMII_OVERRIDE  BIT(1)
#define KSZPHY_OMSO_MII_OVERRIDE  BIT(0)

/* general Interrupt control/status reg in vendor specific block. */
#define MII_KSZPHY_INTCS   0x1B
#define KSZPHY_INTCS_JABBER   BIT(15)
#define KSZPHY_INTCS_RECEIVE_ERR  BIT(14)
#define KSZPHY_INTCS_PAGE_RECEIVE  BIT(13)
#define KSZPHY_INTCS_PARELLEL   BIT(12)
#define KSZPHY_INTCS_LINK_PARTNER_ACK  BIT(11)
#define KSZPHY_INTCS_LINK_DOWN   BIT(10)
#define KSZPHY_INTCS_REMOTE_FAULT  BIT(9)
#define KSZPHY_INTCS_LINK_UP   BIT(8)
#define KSZPHY_INTCS_ALL   (KSZPHY_INTCS_LINK_UP |\
      KSZPHY_INTCS_LINK_DOWN)
#define KSZPHY_INTCS_LINK_DOWN_STATUS  BIT(2)
#define KSZPHY_INTCS_LINK_UP_STATUS  BIT(0)
#define KSZPHY_INTCS_STATUS   (KSZPHY_INTCS_LINK_DOWN_STATUS |\
       KSZPHY_INTCS_LINK_UP_STATUS)

/* LinkMD Control/Status */
#define KSZ8081_LMD    0x1d
#define KSZ8081_LMD_ENABLE_TEST   BIT(15)
#define KSZ8081_LMD_STAT_NORMAL   0
#define KSZ8081_LMD_STAT_OPEN   1
#define KSZ8081_LMD_STAT_SHORT   2
#define KSZ8081_LMD_STAT_FAIL   3
#define KSZ8081_LMD_STAT_MASK   GENMASK(14, 13)
/* Short cable (<10 meter) has been detected by LinkMD */
#define KSZ8081_LMD_SHORT_INDICATOR  BIT(12)
#define KSZ8081_LMD_DELTA_TIME_MASK  GENMASK(8, 0)

#define KSZ9x31_LMD    0x12
#define KSZ9x31_LMD_VCT_EN   BIT(15)
#define KSZ9x31_LMD_VCT_DIS_TX   BIT(14)
#define KSZ9x31_LMD_VCT_PAIR(n)   (((n) & 0x3) << 12)
#define KSZ9x31_LMD_VCT_SEL_RESULT  0
#define KSZ9x31_LMD_VCT_SEL_THRES_HI  BIT(10)
#define KSZ9x31_LMD_VCT_SEL_THRES_LO  BIT(11)
#define KSZ9x31_LMD_VCT_SEL_MASK  GENMASK(11, 10)
#define KSZ9x31_LMD_VCT_ST_NORMAL  0
#define KSZ9x31_LMD_VCT_ST_OPEN   1
#define KSZ9x31_LMD_VCT_ST_SHORT  2
#define KSZ9x31_LMD_VCT_ST_FAIL   3
#define KSZ9x31_LMD_VCT_ST_MASK   GENMASK(9, 8)
#define KSZ9x31_LMD_VCT_DATA_REFLECTED_INVALID BIT(7)
#define KSZ9x31_LMD_VCT_DATA_SIG_WAIT_TOO_LONG BIT(6)
#define KSZ9x31_LMD_VCT_DATA_MASK100  BIT(5)
#define KSZ9x31_LMD_VCT_DATA_NLP_FLP  BIT(4)
#define KSZ9x31_LMD_VCT_DATA_LO_PULSE_MASK GENMASK(3, 2)
#define KSZ9x31_LMD_VCT_DATA_HI_PULSE_MASK GENMASK(1, 0)
#define KSZ9x31_LMD_VCT_DATA_MASK  GENMASK(7, 0)

#define KSZPHY_WIRE_PAIR_MASK   0x3

#define LAN8814_CABLE_DIAG   0x12
#define LAN8814_CABLE_DIAG_STAT_MASK  GENMASK(9, 8)
#define LAN8814_CABLE_DIAG_VCT_DATA_MASK GENMASK(7, 0)
#define LAN8814_PAIR_BIT_SHIFT   12

#define LAN8814_WIRE_PAIR_MASK   0xF

/* Lan8814 general Interrupt control/status reg in GPHY specific block. */
#define LAN8814_INTC    0x18
#define LAN8814_INTS    0x1B

#define LAN8814_INT_LINK_DOWN   BIT(2)
#define LAN8814_INT_LINK_UP   BIT(0)
#define LAN8814_INT_LINK   (LAN8814_INT_LINK_UP |\
       LAN8814_INT_LINK_DOWN)

#define LAN8814_INTR_CTRL_REG   0x34
#define LAN8814_INTR_CTRL_REG_POLARITY  BIT(1)
#define LAN8814_INTR_CTRL_REG_INTR_ENABLE BIT(0)

#define LAN8814_EEE_STATE   0x38
#define LAN8814_EEE_STATE_MASK2P5P  BIT(10)

#define LAN8814_PD_CONTROLS   0x9d
#define LAN8814_PD_CONTROLS_PD_MEAS_TIME_MASK GENMASK(3, 0)
#define LAN8814_PD_CONTROLS_PD_MEAS_TIME_VAL 0xb

/* Represents 1ppm adjustment in 2^32 format with
 * each nsec contains 4 clock cycles.
 * The value is calculated as following: (1/1000000)/((2^-32)/4)
 */

#define LAN8814_1PPM_FORMAT   17179

/* Represents 1ppm adjustment in 2^32 format with
 * each nsec contains 8 clock cycles.
 * The value is calculated as following: (1/1000000)/((2^-32)/8)
 */

#define LAN8841_1PPM_FORMAT   34360

#define PTP_RX_VERSION    0x0248
#define PTP_TX_VERSION    0x0288
#define PTP_MAX_VERSION(x)   (((x) & GENMASK(7, 0)) << 8)
#define PTP_MIN_VERSION(x)   ((x) & GENMASK(7, 0))

#define PTP_RX_MOD    0x024F
#define PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3)
#define PTP_RX_TIMESTAMP_EN   0x024D
#define PTP_TX_TIMESTAMP_EN   0x028D

#define PTP_TIMESTAMP_EN_SYNC_   BIT(0)
#define PTP_TIMESTAMP_EN_DREQ_   BIT(1)
#define PTP_TIMESTAMP_EN_PDREQ_   BIT(2)
#define PTP_TIMESTAMP_EN_PDRES_   BIT(3)

#define PTP_TX_PARSE_L2_ADDR_EN   0x0284
#define PTP_RX_PARSE_L2_ADDR_EN   0x0244

#define PTP_TX_PARSE_IP_ADDR_EN   0x0285
#define PTP_RX_PARSE_IP_ADDR_EN   0x0245
#define LTC_HARD_RESET    0x023F
#define LTC_HARD_RESET_    BIT(0)

#define TSU_HARD_RESET    0x02C1
#define TSU_HARD_RESET_    BIT(0)

#define PTP_CMD_CTL    0x0200
#define PTP_CMD_CTL_PTP_DISABLE_  BIT(0)
#define PTP_CMD_CTL_PTP_ENABLE_   BIT(1)
#define PTP_CMD_CTL_PTP_CLOCK_READ_  BIT(3)
#define PTP_CMD_CTL_PTP_CLOCK_LOAD_  BIT(4)
#define PTP_CMD_CTL_PTP_LTC_STEP_SEC_  BIT(5)
#define PTP_CMD_CTL_PTP_LTC_STEP_NSEC_  BIT(6)

#define PTP_COMMON_INT_ENA   0x0204
#define PTP_COMMON_INT_ENA_GPIO_CAP_EN  BIT(2)

#define PTP_CLOCK_SET_SEC_HI   0x0205
#define PTP_CLOCK_SET_SEC_MID   0x0206
#define PTP_CLOCK_SET_SEC_LO   0x0207
#define PTP_CLOCK_SET_NS_HI   0x0208
#define PTP_CLOCK_SET_NS_LO   0x0209

#define PTP_CLOCK_READ_SEC_HI   0x0229
#define PTP_CLOCK_READ_SEC_MID   0x022A
#define PTP_CLOCK_READ_SEC_LO   0x022B
#define PTP_CLOCK_READ_NS_HI   0x022C
#define PTP_CLOCK_READ_NS_LO   0x022D

#define PTP_GPIO_SEL    0x0230
#define PTP_GPIO_SEL_GPIO_SEL(pin)  ((pin) << 8)
#define PTP_GPIO_CAP_MAP_LO   0x0232

#define PTP_GPIO_CAP_EN    0x0233
#define PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(gpio) BIT(gpio)
#define PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(gpio) (BIT(gpio) << 8)

#define PTP_GPIO_RE_LTC_SEC_HI_CAP  0x0235
#define PTP_GPIO_RE_LTC_SEC_LO_CAP  0x0236
#define PTP_GPIO_RE_LTC_NS_HI_CAP  0x0237
#define PTP_GPIO_RE_LTC_NS_LO_CAP  0x0238
#define PTP_GPIO_FE_LTC_SEC_HI_CAP  0x0239
#define PTP_GPIO_FE_LTC_SEC_LO_CAP  0x023A
#define PTP_GPIO_FE_LTC_NS_HI_CAP  0x023B
#define PTP_GPIO_FE_LTC_NS_LO_CAP  0x023C

#define PTP_GPIO_CAP_STS   0x023D
#define PTP_GPIO_CAP_STS_PTP_GPIO_RE_STS(gpio) BIT(gpio)
#define PTP_GPIO_CAP_STS_PTP_GPIO_FE_STS(gpio) (BIT(gpio) << 8)

#define PTP_OPERATING_MODE   0x0241
#define PTP_OPERATING_MODE_STANDALONE_  BIT(0)

#define PTP_TX_MOD    0x028F
#define PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_ BIT(12)
#define PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3)

#define PTP_RX_PARSE_CONFIG   0x0242
#define PTP_RX_PARSE_CONFIG_LAYER2_EN_  BIT(0)
#define PTP_RX_PARSE_CONFIG_IPV4_EN_  BIT(1)
#define PTP_RX_PARSE_CONFIG_IPV6_EN_  BIT(2)

#define PTP_TX_PARSE_CONFIG   0x0282
#define PTP_TX_PARSE_CONFIG_LAYER2_EN_  BIT(0)
#define PTP_TX_PARSE_CONFIG_IPV4_EN_  BIT(1)
#define PTP_TX_PARSE_CONFIG_IPV6_EN_  BIT(2)

#define PTP_CLOCK_RATE_ADJ_HI   0x020C
#define PTP_CLOCK_RATE_ADJ_LO   0x020D
#define PTP_CLOCK_RATE_ADJ_DIR_   BIT(15)

#define PTP_LTC_STEP_ADJ_HI   0x0212
#define PTP_LTC_STEP_ADJ_LO   0x0213
#define PTP_LTC_STEP_ADJ_DIR_   BIT(15)

#define LAN8814_INTR_STS_REG   0x0033
#define LAN8814_INTR_STS_REG_1588_TSU0_  BIT(0)
#define LAN8814_INTR_STS_REG_1588_TSU1_  BIT(1)
#define LAN8814_INTR_STS_REG_1588_TSU2_  BIT(2)
#define LAN8814_INTR_STS_REG_1588_TSU3_  BIT(3)

#define PTP_CAP_INFO    0x022A
#define PTP_CAP_INFO_TX_TS_CNT_GET_(reg_val) (((reg_val) & 0x0f00) >> 8)
#define PTP_CAP_INFO_RX_TS_CNT_GET_(reg_val) ((reg_val) & 0x000f)

#define PTP_TX_EGRESS_SEC_HI   0x0296
#define PTP_TX_EGRESS_SEC_LO   0x0297
#define PTP_TX_EGRESS_NS_HI   0x0294
#define PTP_TX_EGRESS_NS_LO   0x0295
#define PTP_TX_MSG_HEADER2   0x0299

#define PTP_RX_INGRESS_SEC_HI   0x0256
#define PTP_RX_INGRESS_SEC_LO   0x0257
#define PTP_RX_INGRESS_NS_HI   0x0254
#define PTP_RX_INGRESS_NS_LO   0x0255
#define PTP_RX_MSG_HEADER2   0x0259

#define PTP_TSU_INT_EN    0x0200
#define PTP_TSU_INT_EN_PTP_TX_TS_OVRFL_EN_ BIT(3)
#define PTP_TSU_INT_EN_PTP_TX_TS_EN_  BIT(2)
#define PTP_TSU_INT_EN_PTP_RX_TS_OVRFL_EN_ BIT(1)
#define PTP_TSU_INT_EN_PTP_RX_TS_EN_  BIT(0)

#define PTP_TSU_INT_STS    0x0201
#define PTP_TSU_INT_STS_PTP_TX_TS_OVRFL_INT_ BIT(3)
#define PTP_TSU_INT_STS_PTP_TX_TS_EN_  BIT(2)
#define PTP_TSU_INT_STS_PTP_RX_TS_OVRFL_INT_ BIT(1)
#define PTP_TSU_INT_STS_PTP_RX_TS_EN_  BIT(0)

#define LAN8814_LED_CTRL_1   0x0
#define LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_ BIT(6)

/* PHY Control 1 */
#define MII_KSZPHY_CTRL_1   0x1e
#define KSZ8081_CTRL1_MDIX_STAT   BIT(4)

/* PHY Control 2 / PHY Control (if no PHY Control 1) */
#define MII_KSZPHY_CTRL_2   0x1f
#define MII_KSZPHY_CTRL    MII_KSZPHY_CTRL_2
/* bitmap of PHY register to set interrupt mode */
#define KSZ8081_CTRL2_HP_MDIX   BIT(15)
#define KSZ8081_CTRL2_MDI_MDI_X_SELECT  BIT(14)
#define KSZ8081_CTRL2_DISABLE_AUTO_MDIX  BIT(13)
#define KSZ8081_CTRL2_FORCE_LINK  BIT(11)
#define KSZ8081_CTRL2_POWER_SAVING  BIT(10)
#define KSZPHY_CTRL_INT_ACTIVE_HIGH  BIT(9)
#define KSZPHY_RMII_REF_CLK_SEL   BIT(7)

/* Write/read to/from extended registers */
#define MII_KSZPHY_EXTREG   0x0b
#define KSZPHY_EXTREG_WRITE   0x8000

#define MII_KSZPHY_EXTREG_WRITE   0x0c
#define MII_KSZPHY_EXTREG_READ   0x0d

/* Extended registers */
#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW  0x104
#define MII_KSZPHY_RX_DATA_PAD_SKEW  0x105
#define MII_KSZPHY_TX_DATA_PAD_SKEW  0x106

#define PS_TO_REG    200
#define FIFO_SIZE    8

#define LAN8814_PTP_GPIO_NUM   24
#define LAN8814_PTP_PEROUT_NUM   2
#define LAN8814_PTP_EXTTS_NUM   3

#define LAN8814_BUFFER_TIME   2

#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_200MS 13
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100MS 12
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50MS 11
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10MS 10
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5MS 9
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1MS 8
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500US 7
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100US 6
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50US 5
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10US 4
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5US 3
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1US 2
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500NS 1
#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100NS 0

#define LAN8814_GPIO_EN1   0x20
#define LAN8814_GPIO_EN2   0x21
#define LAN8814_GPIO_DIR1   0x22
#define LAN8814_GPIO_DIR2   0x23
#define LAN8814_GPIO_BUF1   0x24
#define LAN8814_GPIO_BUF2   0x25

#define LAN8814_GPIO_EN_ADDR(pin) \
 ((pin) > 15 ? LAN8814_GPIO_EN1 : LAN8814_GPIO_EN2)
#define LAN8814_GPIO_EN_BIT(pin)  BIT(pin)
#define LAN8814_GPIO_DIR_ADDR(pin) \
 ((pin) > 15 ? LAN8814_GPIO_DIR1 : LAN8814_GPIO_DIR2)
#define LAN8814_GPIO_DIR_BIT(pin)  BIT(pin)
#define LAN8814_GPIO_BUF_ADDR(pin) \
 ((pin) > 15 ? LAN8814_GPIO_BUF1 : LAN8814_GPIO_BUF2)
#define LAN8814_GPIO_BUF_BIT(pin)  BIT(pin)

#define LAN8814_EVENT_A    0
#define LAN8814_EVENT_B    1

#define LAN8814_PTP_GENERAL_CONFIG  0x0201
#define LAN8814_PTP_GENERAL_CONFIG_LTC_EVENT_MASK(event) \
 ((event) ? GENMASK(11, 8) : GENMASK(7, 4))
#define LAN8814_PTP_GENERAL_CONFIG_LTC_EVENT_SET(event, value) \
 (((value) & GENMASK(3, 0)) << (4 + ((event) << 2)))
#define LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event) \
 ((event) ? BIT(2) : BIT(0))
#define LAN8814_PTP_GENERAL_CONFIG_POLARITY_X(event) \
 ((event) ? BIT(3) : BIT(1))

#define LAN8814_PTP_CLOCK_TARGET_SEC_HI(event) ((event) ? 0x21F : 0x215)
#define LAN8814_PTP_CLOCK_TARGET_SEC_LO(event) ((event) ? 0x220 : 0x216)
#define LAN8814_PTP_CLOCK_TARGET_NS_HI(event) ((event) ? 0x221 : 0x217)
#define LAN8814_PTP_CLOCK_TARGET_NS_LO(event) ((event) ? 0x222 : 0x218)

#define LAN8814_PTP_CLOCK_TARGET_RELOAD_SEC_HI(event) ((event) ? 0x223 : 0x219)
#define LAN8814_PTP_CLOCK_TARGET_RELOAD_SEC_LO(event) ((event) ? 0x224 : 0x21A)
#define LAN8814_PTP_CLOCK_TARGET_RELOAD_NS_HI(event) ((event) ? 0x225 : 0x21B)
#define LAN8814_PTP_CLOCK_TARGET_RELOAD_NS_LO(event) ((event) ? 0x226 : 0x21C)

/* Delay used to get the second part from the LTC */
#define LAN8841_GET_SEC_LTC_DELAY  (500 * NSEC_PER_MSEC)

struct kszphy_hw_stat {
 const char *string;
 u8 reg;
 u8 bits;
};

static struct kszphy_hw_stat kszphy_hw_stats[] = {
 { "phy_receive_errors", 21, 16},
 { "phy_idle_errors", 10, 8 },
};

struct kszphy_type {
 u32 led_mode_reg;
 u16 interrupt_level_mask;
 u16 cable_diag_reg;
 unsigned long pair_mask;
 u16 disable_dll_tx_bit;
 u16 disable_dll_rx_bit;
 u16 disable_dll_mask;
 bool has_broadcast_disable;
 bool has_nand_tree_disable;
 bool has_rmii_ref_clk_sel;
};

/* Shared structure between the PHYs of the same package. */
struct lan8814_shared_priv {
 struct phy_device *phydev;
 struct ptp_clock *ptp_clock;
 struct ptp_clock_info ptp_clock_info;
 struct ptp_pin_desc *pin_config;

 /* Lock for ptp_clock */
 struct mutex shared_lock;
};

struct lan8814_ptp_rx_ts {
 struct list_head list;
 u32 seconds;
 u32 nsec;
 u16 seq_id;
};

struct kszphy_ptp_priv {
 struct mii_timestamper mii_ts;
 struct phy_device *phydev;

 struct sk_buff_head tx_queue;
 struct sk_buff_head rx_queue;

 struct list_head rx_ts_list;
 /* Lock for Rx ts fifo */
 spinlock_t rx_ts_lock;

 int hwts_tx_type;
 enum hwtstamp_rx_filters rx_filter;
 int layer;
 int version;

 struct ptp_clock *ptp_clock;
 struct ptp_clock_info ptp_clock_info;
 /* Lock for ptp_clock */
 struct mutex ptp_lock;
 struct ptp_pin_desc *pin_config;

 s64 seconds;
 /* Lock for accessing seconds */
 spinlock_t seconds_lock;
};

struct kszphy_phy_stats {
 u64 rx_err_pkt_cnt;
};

struct kszphy_priv {
 struct kszphy_ptp_priv ptp_priv;
 const struct kszphy_type *type;
 struct clk *clk;
 int led_mode;
 u16 vct_ctrl1000;
 bool rmii_ref_clk_sel;
 bool rmii_ref_clk_sel_val;
 bool clk_enable;
 u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
 struct kszphy_phy_stats phy_stats;
};

static const struct kszphy_type lan8814_type = {
 .led_mode_reg  = ~LAN8814_LED_CTRL_1,
 .cable_diag_reg  = LAN8814_CABLE_DIAG,
 .pair_mask  = LAN8814_WIRE_PAIR_MASK,
};

static const struct kszphy_type ksz886x_type = {
 .cable_diag_reg  = KSZ8081_LMD,
 .pair_mask  = KSZPHY_WIRE_PAIR_MASK,
};

static const struct kszphy_type ksz8021_type = {
 .led_mode_reg  = MII_KSZPHY_CTRL_2,
 .has_broadcast_disable = true,
 .has_nand_tree_disable = true,
 .has_rmii_ref_clk_sel = true,
};

static const struct kszphy_type ksz8041_type = {
 .led_mode_reg  = MII_KSZPHY_CTRL_1,
};

static const struct kszphy_type ksz8051_type = {
 .led_mode_reg  = MII_KSZPHY_CTRL_2,
 .has_nand_tree_disable = true,
};

static const struct kszphy_type ksz8081_type = {
 .led_mode_reg  = MII_KSZPHY_CTRL_2,
 .cable_diag_reg  = KSZ8081_LMD,
 .pair_mask  = KSZPHY_WIRE_PAIR_MASK,
 .has_broadcast_disable = true,
 .has_nand_tree_disable = true,
 .has_rmii_ref_clk_sel = true,
};

static const struct kszphy_type ks8737_type = {
 .interrupt_level_mask = BIT(14),
};

static const struct kszphy_type ksz9021_type = {
 .interrupt_level_mask = BIT(14),
};

static const struct kszphy_type ksz9131_type = {
 .interrupt_level_mask = BIT(14),
 .disable_dll_tx_bit = BIT(12),
 .disable_dll_rx_bit = BIT(12),
 .disable_dll_mask = BIT_MASK(12),
};

static const struct kszphy_type lan8841_type = {
 .disable_dll_tx_bit = BIT(14),
 .disable_dll_rx_bit = BIT(14),
 .disable_dll_mask = BIT_MASK(14),
 .cable_diag_reg  = LAN8814_CABLE_DIAG,
 .pair_mask  = LAN8814_WIRE_PAIR_MASK,
};

static int kszphy_extended_write(struct phy_device *phydev,
    u32 regnum, u16 val)
{
 phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum);
 return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val);
}

static int kszphy_extended_read(struct phy_device *phydev,
    u32 regnum)
{
 phy_write(phydev, MII_KSZPHY_EXTREG, regnum);
 return phy_read(phydev, MII_KSZPHY_EXTREG_READ);
}

static int kszphy_ack_interrupt(struct phy_device *phydev)
{
 /* bit[7..0] int status, which is a read and clear register. */
 int rc;

 rc = phy_read(phydev, MII_KSZPHY_INTCS);

 return (rc < 0) ? rc : 0;
}

static int kszphy_config_intr(struct phy_device *phydev)
{
 const struct kszphy_type *type = phydev->drv->driver_data;
 int temp, err;
 u16 mask;

 if (type && type->interrupt_level_mask)
  mask = type->interrupt_level_mask;
 else
  mask = KSZPHY_CTRL_INT_ACTIVE_HIGH;

 /* set the interrupt pin active low */
 temp = phy_read(phydev, MII_KSZPHY_CTRL);
 if (temp < 0)
  return temp;
 temp &= ~mask;
 phy_write(phydev, MII_KSZPHY_CTRL, temp);

 /* enable / disable interrupts */
 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
  err = kszphy_ack_interrupt(phydev);
  if (err)
   return err;

  err = phy_write(phydev, MII_KSZPHY_INTCS, KSZPHY_INTCS_ALL);
 } else {
  err = phy_write(phydev, MII_KSZPHY_INTCS, 0);
  if (err)
   return err;

  err = kszphy_ack_interrupt(phydev);
 }

 return err;
}

static irqreturn_t kszphy_handle_interrupt(struct phy_device *phydev)
{
 int irq_status;

 irq_status = phy_read(phydev, MII_KSZPHY_INTCS);
 if (irq_status < 0) {
  phy_error(phydev);
  return IRQ_NONE;
 }

 if (!(irq_status & KSZPHY_INTCS_STATUS))
  return IRQ_NONE;

 phy_trigger_machine(phydev);

 return IRQ_HANDLED;
}

static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val)
{
 int ctrl;

 ctrl = phy_read(phydev, MII_KSZPHY_CTRL);
 if (ctrl < 0)
  return ctrl;

 if (val)
  ctrl |= KSZPHY_RMII_REF_CLK_SEL;
 else
  ctrl &= ~KSZPHY_RMII_REF_CLK_SEL;

 return phy_write(phydev, MII_KSZPHY_CTRL, ctrl);
}

static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val)
{
 int rc, temp, shift;

 switch (reg) {
 case MII_KSZPHY_CTRL_1:
  shift = 14;
  break;
 case MII_KSZPHY_CTRL_2:
  shift = 4;
  break;
 default:
  return -EINVAL;
 }

 temp = phy_read(phydev, reg);
 if (temp < 0) {
  rc = temp;
  goto out;
 }

 temp &= ~(3 << shift);
 temp |= val << shift;
 rc = phy_write(phydev, reg, temp);
out:
 if (rc < 0)
  phydev_err(phydev, "failed to set led mode\n");

 return rc;
}

/* Disable PHY address 0 as the broadcast address, so that it can be used as a
 * unique (non-broadcast) address on a shared bus.
 */

static int kszphy_broadcast_disable(struct phy_device *phydev)
{
 int ret;

 ret = phy_read(phydev, MII_KSZPHY_OMSO);
 if (ret < 0)
  goto out;

 ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF);
out:
 if (ret)
  phydev_err(phydev, "failed to disable broadcast address\n");

 return ret;
}

static int kszphy_nand_tree_disable(struct phy_device *phydev)
{
 int ret;

 ret = phy_read(phydev, MII_KSZPHY_OMSO);
 if (ret < 0)
  goto out;

 if (!(ret & KSZPHY_OMSO_NAND_TREE_ON))
  return 0;

 ret = phy_write(phydev, MII_KSZPHY_OMSO,
   ret & ~KSZPHY_OMSO_NAND_TREE_ON);
out:
 if (ret)
  phydev_err(phydev, "failed to disable NAND tree mode\n");

 return ret;
}

/* Some config bits need to be set again on resume, handle them here. */
static int kszphy_config_reset(struct phy_device *phydev)
{
 struct kszphy_priv *priv = phydev->priv;
 int ret;

 if (priv->rmii_ref_clk_sel) {
  ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val);
  if (ret) {
   phydev_err(phydev,
       "failed to set rmii reference clock\n");
   return ret;
  }
 }

 if (priv->type && priv->led_mode >= 0)
  kszphy_setup_led(phydev, priv->type->led_mode_reg, priv->led_mode);

 return 0;
}

static int kszphy_config_init(struct phy_device *phydev)
{
 struct kszphy_priv *priv = phydev->priv;
 const struct kszphy_type *type;

 if (!priv)
  return 0;

 type = priv->type;

 if (type && type->has_broadcast_disable)
  kszphy_broadcast_disable(phydev);

 if (type && type->has_nand_tree_disable)
  kszphy_nand_tree_disable(phydev);

 return kszphy_config_reset(phydev);
}

static int ksz8041_fiber_mode(struct phy_device *phydev)
{
 struct device_node *of_node = phydev->mdio.dev.of_node;

 return of_property_read_bool(of_node, "micrel,fiber-mode");
}

static int ksz8041_config_init(struct phy_device *phydev)
{
 __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };

 /* Limit supported and advertised modes in fiber mode */
 if (ksz8041_fiber_mode(phydev)) {
  phydev->dev_flags |= MICREL_PHY_FXEN;
  linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask);
  linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask);

  linkmode_and(phydev->supported, phydev->supported, mask);
  linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
     phydev->supported);
  linkmode_and(phydev->advertising, phydev->advertising, mask);
  linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT,
     phydev->advertising);
  phydev->autoneg = AUTONEG_DISABLE;
 }

 return kszphy_config_init(phydev);
}

static int ksz8041_config_aneg(struct phy_device *phydev)
{
 /* Skip auto-negotiation in fiber mode */
 if (phydev->dev_flags & MICREL_PHY_FXEN) {
  phydev->speed = SPEED_100;
  return 0;
 }

 return genphy_config_aneg(phydev);
}

static int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev,
         const bool ksz_8051)
{
 int ret;

 if (!phy_id_compare(phydev->phy_id, PHY_ID_KSZ8051, MICREL_PHY_ID_MASK))
  return 0;

 ret = phy_read(phydev, MII_BMSR);
 if (ret < 0)
  return ret;

 /* KSZ8051 PHY and KSZ8794/KSZ8795/KSZ8765 switch share the same
 * exact PHY ID. However, they can be told apart by the extended
 * capability registers presence. The KSZ8051 PHY has them while
 * the switch does not.
 */

 ret &= BMSR_ERCAP;
 if (ksz_8051)
  return ret;
 else
  return !ret;
}

static int ksz8051_match_phy_device(struct phy_device *phydev,
        const struct phy_driver *phydrv)
{
 return ksz8051_ksz8795_match_phy_device(phydev, true);
}

static int ksz8081_config_init(struct phy_device *phydev)
{
 /* KSZPHY_OMSO_FACTORY_TEST is set at de-assertion of the reset line
 * based on the RXER (KSZ8081RNA/RND) or TXC (KSZ8081MNX/RNB) pin. If a
 * pull-down is missing, the factory test mode should be cleared by
 * manually writing a 0.
 */

 phy_clear_bits(phydev, MII_KSZPHY_OMSO, KSZPHY_OMSO_FACTORY_TEST);

 return kszphy_config_init(phydev);
}

static int ksz8081_config_mdix(struct phy_device *phydev, u8 ctrl)
{
 u16 val;

 switch (ctrl) {
 case ETH_TP_MDI:
  val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX;
  break;
 case ETH_TP_MDI_X:
  val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX |
   KSZ8081_CTRL2_MDI_MDI_X_SELECT;
  break;
 case ETH_TP_MDI_AUTO:
  val = 0;
  break;
 default:
  return 0;
 }

 return phy_modify(phydev, MII_KSZPHY_CTRL_2,
     KSZ8081_CTRL2_HP_MDIX |
     KSZ8081_CTRL2_MDI_MDI_X_SELECT |
     KSZ8081_CTRL2_DISABLE_AUTO_MDIX,
     KSZ8081_CTRL2_HP_MDIX | val);
}

static int ksz8081_config_aneg(struct phy_device *phydev)
{
 int ret;

 ret = genphy_config_aneg(phydev);
 if (ret)
  return ret;

 /* The MDI-X configuration is automatically changed by the PHY after
 * switching from autoneg off to on. So, take MDI-X configuration under
 * own control and set it after autoneg configuration was done.
 */

 return ksz8081_config_mdix(phydev, phydev->mdix_ctrl);
}

static int ksz8081_mdix_update(struct phy_device *phydev)
{
 int ret;

 ret = phy_read(phydev, MII_KSZPHY_CTRL_2);
 if (ret < 0)
  return ret;

 if (ret & KSZ8081_CTRL2_DISABLE_AUTO_MDIX) {
  if (ret & KSZ8081_CTRL2_MDI_MDI_X_SELECT)
   phydev->mdix_ctrl = ETH_TP_MDI_X;
  else
   phydev->mdix_ctrl = ETH_TP_MDI;
 } else {
  phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
 }

 ret = phy_read(phydev, MII_KSZPHY_CTRL_1);
 if (ret < 0)
  return ret;

 if (ret & KSZ8081_CTRL1_MDIX_STAT)
  phydev->mdix = ETH_TP_MDI;
 else
  phydev->mdix = ETH_TP_MDI_X;

 return 0;
}

static int ksz8081_read_status(struct phy_device *phydev)
{
 int ret;

 ret = ksz8081_mdix_update(phydev);
 if (ret < 0)
  return ret;

 return genphy_read_status(phydev);
}

static int ksz8061_config_init(struct phy_device *phydev)
{
 int ret;

 /* Chip can be powered down by the bootstrap code. */
 ret = phy_read(phydev, MII_BMCR);
 if (ret < 0)
  return ret;
 if (ret & BMCR_PDOWN) {
  ret = phy_write(phydev, MII_BMCR, ret & ~BMCR_PDOWN);
  if (ret < 0)
   return ret;
  usleep_range(1000, 2000);
 }

 ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A);
 if (ret)
  return ret;

 return kszphy_config_init(phydev);
}

static int ksz8795_match_phy_device(struct phy_device *phydev,
        const struct phy_driver *phydrv)
{
 return ksz8051_ksz8795_match_phy_device(phydev, false);
}

static int ksz9021_load_values_from_of(struct phy_device *phydev,
           const struct device_node *of_node,
           u16 reg,
           const char *field1, const char *field2,
           const char *field3, const char *field4)
{
 int val1 = -1;
 int val2 = -2;
 int val3 = -3;
 int val4 = -4;
 int newval;
 int matches = 0;

 if (!of_property_read_u32(of_node, field1, &val1))
  matches++;

 if (!of_property_read_u32(of_node, field2, &val2))
  matches++;

 if (!of_property_read_u32(of_node, field3, &val3))
  matches++;

 if (!of_property_read_u32(of_node, field4, &val4))
  matches++;

 if (!matches)
  return 0;

 if (matches < 4)
  newval = kszphy_extended_read(phydev, reg);
 else
  newval = 0;

 if (val1 != -1)
  newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0);

 if (val2 != -2)
  newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4);

 if (val3 != -3)
  newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8);

 if (val4 != -4)
  newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12);

 return kszphy_extended_write(phydev, reg, newval);
}

static int ksz9021_config_init(struct phy_device *phydev)
{
 const struct device_node *of_node;
 const struct device *dev_walker;

 /* The Micrel driver has a deprecated option to place phy OF
 * properties in the MAC node. Walk up the tree of devices to
 * find a device with an OF node.
 */

 dev_walker = &phydev->mdio.dev;
 do {
  of_node = dev_walker->of_node;
  dev_walker = dev_walker->parent;

 } while (!of_node && dev_walker);

 if (of_node) {
  ksz9021_load_values_from_of(phydev, of_node,
        MII_KSZPHY_CLK_CONTROL_PAD_SKEW,
        "txen-skew-ps""txc-skew-ps",
        "rxdv-skew-ps""rxc-skew-ps");
  ksz9021_load_values_from_of(phydev, of_node,
        MII_KSZPHY_RX_DATA_PAD_SKEW,
        "rxd0-skew-ps""rxd1-skew-ps",
        "rxd2-skew-ps""rxd3-skew-ps");
  ksz9021_load_values_from_of(phydev, of_node,
        MII_KSZPHY_TX_DATA_PAD_SKEW,
        "txd0-skew-ps""txd1-skew-ps",
        "txd2-skew-ps""txd3-skew-ps");
 }
 return 0;
}

#define KSZ9031_PS_TO_REG  60

/* Extended registers */
/* MMD Address 0x0 */
#define MII_KSZ9031RN_FLP_BURST_TX_LO 3
#define MII_KSZ9031RN_FLP_BURST_TX_HI 4

/* MMD Address 0x2 */
#define MII_KSZ9031RN_CONTROL_PAD_SKEW 4
#define MII_KSZ9031RN_RX_CTL_M  GENMASK(7, 4)
#define MII_KSZ9031RN_TX_CTL_M  GENMASK(3, 0)

#define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5
#define MII_KSZ9031RN_RXD3  GENMASK(15, 12)
#define MII_KSZ9031RN_RXD2  GENMASK(11, 8)
#define MII_KSZ9031RN_RXD1  GENMASK(7, 4)
#define MII_KSZ9031RN_RXD0  GENMASK(3, 0)

#define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6
#define MII_KSZ9031RN_TXD3  GENMASK(15, 12)
#define MII_KSZ9031RN_TXD2  GENMASK(11, 8)
#define MII_KSZ9031RN_TXD1  GENMASK(7, 4)
#define MII_KSZ9031RN_TXD0  GENMASK(3, 0)

#define MII_KSZ9031RN_CLK_PAD_SKEW 8
#define MII_KSZ9031RN_GTX_CLK  GENMASK(9, 5)
#define MII_KSZ9031RN_RX_CLK  GENMASK(4, 0)

/* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To
 * provide different RGMII options we need to configure delay offset
 * for each pad relative to build in delay.
 */

/* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of
 * 1.80ns
 */

#define RX_ID    0x7
#define RX_CLK_ID   0x19

/* set rx to +0.30ns and rx_clk to -0.90ns to compensate the
 * internal 1.2ns delay.
 */

#define RX_ND    0xc
#define RX_CLK_ND   0x0

/* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */
#define TX_ID    0x0
#define TX_CLK_ID   0x1f

/* set tx and tx_clk to "No delay adjustment" to keep 0ns
 * dealy
 */

#define TX_ND    0x7
#define TX_CLK_ND   0xf

/* MMD Address 0x1C */
#define MII_KSZ9031RN_EDPD  0x23
#define MII_KSZ9031RN_EDPD_ENABLE BIT(0)

static int ksz9031_set_loopback(struct phy_device *phydev, bool enable,
    int speed)
{
 u16 ctl = BMCR_LOOPBACK;
 int val;

 if (!enable)
  return genphy_loopback(phydev, enable, 0);

 if (speed == SPEED_10 || speed == SPEED_100 || speed == SPEED_1000)
  phydev->speed = speed;
 else if (speed)
  return -EINVAL;
 phydev->duplex = DUPLEX_FULL;

 ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);

 phy_write(phydev, MII_BMCR, ctl);

 return phy_read_poll_timeout(phydev, MII_BMSR, val, val & BMSR_LSTATUS,
         5000, 500000, true);
}

static int ksz9031_of_load_skew_values(struct phy_device *phydev,
           const struct device_node *of_node,
           u16 reg, size_t field_sz,
           const char *field[], u8 numfields,
           bool *update)
{
 int val[4] = {-1, -2, -3, -4};
 int matches = 0;
 u16 mask;
 u16 maxval;
 u16 newval;
 int i;

 for (i = 0; i < numfields; i++)
  if (!of_property_read_u32(of_node, field[i], val + i))
   matches++;

 if (!matches)
  return 0;

 *update |= true;

 if (matches < numfields)
  newval = phy_read_mmd(phydev, 2, reg);
 else
  newval = 0;

 maxval = (field_sz == 4) ? 0xf : 0x1f;
 for (i = 0; i < numfields; i++)
  if (val[i] != -(i + 1)) {
   mask = 0xffff;
   mask ^= maxval << (field_sz * i);
   newval = (newval & mask) |
    (((val[i] / KSZ9031_PS_TO_REG) & maxval)
     << (field_sz * i));
  }

 return phy_write_mmd(phydev, 2, reg, newval);
}

/* Center KSZ9031RNX FLP timing at 16ms. */
static int ksz9031_center_flp_timing(struct phy_device *phydev)
{
 int result;

 result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI,
          0x0006);
 if (result)
  return result;

 result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO,
          0x1A80);
 if (result)
  return result;

 return genphy_restart_aneg(phydev);
}

/* Enable energy-detect power-down mode */
static int ksz9031_enable_edpd(struct phy_device *phydev)
{
 int reg;

 reg = phy_read_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD);
 if (reg < 0)
  return reg;
 return phy_write_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD,
        reg | MII_KSZ9031RN_EDPD_ENABLE);
}

static int ksz9031_config_rgmii_delay(struct phy_device *phydev)
{
 u16 rx, tx, rx_clk, tx_clk;
 int ret;

 switch (phydev->interface) {
 case PHY_INTERFACE_MODE_RGMII:
  tx = TX_ND;
  tx_clk = TX_CLK_ND;
  rx = RX_ND;
  rx_clk = RX_CLK_ND;
  break;
 case PHY_INTERFACE_MODE_RGMII_ID:
  tx = TX_ID;
  tx_clk = TX_CLK_ID;
  rx = RX_ID;
  rx_clk = RX_CLK_ID;
  break;
 case PHY_INTERFACE_MODE_RGMII_RXID:
  tx = TX_ND;
  tx_clk = TX_CLK_ND;
  rx = RX_ID;
  rx_clk = RX_CLK_ID;
  break;
 case PHY_INTERFACE_MODE_RGMII_TXID:
  tx = TX_ID;
  tx_clk = TX_CLK_ID;
  rx = RX_ND;
  rx_clk = RX_CLK_ND;
  break;
 default:
  return 0;
 }

 ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_CONTROL_PAD_SKEW,
       FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) |
       FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx));
 if (ret < 0)
  return ret;

 ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_RX_DATA_PAD_SKEW,
       FIELD_PREP(MII_KSZ9031RN_RXD3, rx) |
       FIELD_PREP(MII_KSZ9031RN_RXD2, rx) |
       FIELD_PREP(MII_KSZ9031RN_RXD1, rx) |
       FIELD_PREP(MII_KSZ9031RN_RXD0, rx));
 if (ret < 0)
  return ret;

 ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_TX_DATA_PAD_SKEW,
       FIELD_PREP(MII_KSZ9031RN_TXD3, tx) |
       FIELD_PREP(MII_KSZ9031RN_TXD2, tx) |
       FIELD_PREP(MII_KSZ9031RN_TXD1, tx) |
       FIELD_PREP(MII_KSZ9031RN_TXD0, tx));
 if (ret < 0)
  return ret;

 return phy_write_mmd(phydev, 2, MII_KSZ9031RN_CLK_PAD_SKEW,
        FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) |
        FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk));
}

static int ksz9031_config_init(struct phy_device *phydev)
{
 const struct device_node *of_node;
 static const char *clk_skews[2] = {"rxc-skew-ps""txc-skew-ps"};
 static const char *rx_data_skews[4] = {
  "rxd0-skew-ps""rxd1-skew-ps",
  "rxd2-skew-ps""rxd3-skew-ps"
 };
 static const char *tx_data_skews[4] = {
  "txd0-skew-ps""txd1-skew-ps",
  "txd2-skew-ps""txd3-skew-ps"
 };
 static const char *control_skews[2] = {"txen-skew-ps""rxdv-skew-ps"};
 const struct device *dev_walker;
 int result;

 result = ksz9031_enable_edpd(phydev);
 if (result < 0)
  return result;

 /* The Micrel driver has a deprecated option to place phy OF
 * properties in the MAC node. Walk up the tree of devices to
 * find a device with an OF node.
 */

 dev_walker = &phydev->mdio.dev;
 do {
  of_node = dev_walker->of_node;
  dev_walker = dev_walker->parent;
 } while (!of_node && dev_walker);

 if (of_node) {
  bool update = false;

  if (phy_interface_is_rgmii(phydev)) {
   result = ksz9031_config_rgmii_delay(phydev);
   if (result < 0)
    return result;
  }

  ksz9031_of_load_skew_values(phydev, of_node,
    MII_KSZ9031RN_CLK_PAD_SKEW, 5,
    clk_skews, 2, &update);

  ksz9031_of_load_skew_values(phydev, of_node,
    MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
    control_skews, 2, &update);

  ksz9031_of_load_skew_values(phydev, of_node,
    MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
    rx_data_skews, 4, &update);

  ksz9031_of_load_skew_values(phydev, of_node,
    MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
    tx_data_skews, 4, &update);

  if (update && !phy_interface_is_rgmii(phydev))
   phydev_warn(phydev,
        "*-skew-ps values should be used only with RGMII PHY modes\n");

  /* Silicon Errata Sheet (DS80000691D or DS80000692D):
 * When the device links in the 1000BASE-T slave mode only,
 * the optional 125MHz reference output clock (CLK125_NDO)
 * has wide duty cycle variation.
 *
 * The optional CLK125_NDO clock does not meet the RGMII
 * 45/55 percent (min/max) duty cycle requirement and therefore
 * cannot be used directly by the MAC side for clocking
 * applications that have setup/hold time requirements on
 * rising and falling clock edges.
 *
 * Workaround:
 * Force the phy to be the master to receive a stable clock
 * which meets the duty cycle requirement.
 */

  if (of_property_read_bool(of_node, "micrel,force-master")) {
   result = phy_read(phydev, MII_CTRL1000);
   if (result < 0)
    goto err_force_master;

   /* enable master mode, config & prefer master */
   result |= CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER;
   result = phy_write(phydev, MII_CTRL1000, result);
   if (result < 0)
    goto err_force_master;
  }
 }

 return ksz9031_center_flp_timing(phydev);

err_force_master:
 phydev_err(phydev, "failed to force the phy to master mode\n");
 return result;
}

#define KSZ9131_SKEW_5BIT_MAX 2400
#define KSZ9131_SKEW_4BIT_MAX 800
#define KSZ9131_OFFSET  700
#define KSZ9131_STEP  100

static int ksz9131_of_load_skew_values(struct phy_device *phydev,
           struct device_node *of_node,
           u16 reg, size_t field_sz,
           char *field[], u8 numfields)
{
 int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET),
        -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)};
 int skewval, skewmax = 0;
 int matches = 0;
 u16 maxval;
 u16 newval;
 u16 mask;
 int i;

 /* psec properties in dts should mean x pico seconds */
 if (field_sz == 5)
  skewmax = KSZ9131_SKEW_5BIT_MAX;
 else
  skewmax = KSZ9131_SKEW_4BIT_MAX;

 for (i = 0; i < numfields; i++)
  if (!of_property_read_s32(of_node, field[i], &skewval)) {
   if (skewval < -KSZ9131_OFFSET)
    skewval = -KSZ9131_OFFSET;
   else if (skewval > skewmax)
    skewval = skewmax;

   val[i] = skewval + KSZ9131_OFFSET;
   matches++;
  }

 if (!matches)
  return 0;

 if (matches < numfields)
  newval = phy_read_mmd(phydev, 2, reg);
 else
  newval = 0;

 maxval = (field_sz == 4) ? 0xf : 0x1f;
 for (i = 0; i < numfields; i++)
  if (val[i] != -(i + 1 + KSZ9131_OFFSET)) {
   mask = 0xffff;
   mask ^= maxval << (field_sz * i);
   newval = (newval & mask) |
    (((val[i] / KSZ9131_STEP) & maxval)
     << (field_sz * i));
  }

 return phy_write_mmd(phydev, 2, reg, newval);
}

#define KSZ9131RN_MMD_COMMON_CTRL_REG 2
#define KSZ9131RN_RXC_DLL_CTRL  76
#define KSZ9131RN_TXC_DLL_CTRL  77
#define KSZ9131RN_DLL_ENABLE_DELAY 0

static int ksz9131_config_rgmii_delay(struct phy_device *phydev)
{
 const struct kszphy_type *type = phydev->drv->driver_data;
 u16 rxcdll_val, txcdll_val;
 int ret;

 switch (phydev->interface) {
 case PHY_INTERFACE_MODE_RGMII:
  rxcdll_val = type->disable_dll_rx_bit;
  txcdll_val = type->disable_dll_tx_bit;
  break;
 case PHY_INTERFACE_MODE_RGMII_ID:
  rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
  txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
  break;
 case PHY_INTERFACE_MODE_RGMII_RXID:
  rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
  txcdll_val = type->disable_dll_tx_bit;
  break;
 case PHY_INTERFACE_MODE_RGMII_TXID:
  rxcdll_val = type->disable_dll_rx_bit;
  txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY;
  break;
 default:
  return 0;
 }

 ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
        KSZ9131RN_RXC_DLL_CTRL, type->disable_dll_mask,
        rxcdll_val);
 if (ret < 0)
  return ret;

 return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
         KSZ9131RN_TXC_DLL_CTRL, type->disable_dll_mask,
         txcdll_val);
}

/* Silicon Errata DS80000693B
 *
 * When LEDs are configured in Individual Mode, LED1 is ON in a no-link
 * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves
 * according to the datasheet (off if there is no link).
 */

static int ksz9131_led_errata(struct phy_device *phydev)
{
 int reg;

 reg = phy_read_mmd(phydev, 2, 0);
 if (reg < 0)
  return reg;

 if (!(reg & BIT(4)))
  return 0;

 return phy_set_bits(phydev, 0x1e, BIT(9));
}

static int ksz9131_config_init(struct phy_device *phydev)
{
 struct device_node *of_node;
 char *clk_skews[2] = {"rxc-skew-psec""txc-skew-psec"};
 char *rx_data_skews[4] = {
  "rxd0-skew-psec""rxd1-skew-psec",
  "rxd2-skew-psec""rxd3-skew-psec"
 };
 char *tx_data_skews[4] = {
  "txd0-skew-psec""txd1-skew-psec",
  "txd2-skew-psec""txd3-skew-psec"
 };
 char *control_skews[2] = {"txen-skew-psec""rxdv-skew-psec"};
 const struct device *dev_walker;
 int ret;

 phydev->mdix_ctrl = ETH_TP_MDI_AUTO;

 dev_walker = &phydev->mdio.dev;
 do {
  of_node = dev_walker->of_node;
  dev_walker = dev_walker->parent;
 } while (!of_node && dev_walker);

 if (!of_node)
  return 0;

 if (phy_interface_is_rgmii(phydev)) {
  ret = ksz9131_config_rgmii_delay(phydev);
  if (ret < 0)
   return ret;
 }

 ret = ksz9131_of_load_skew_values(phydev, of_node,
       MII_KSZ9031RN_CLK_PAD_SKEW, 5,
       clk_skews, 2);
 if (ret < 0)
  return ret;

 ret = ksz9131_of_load_skew_values(phydev, of_node,
       MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
       control_skews, 2);
 if (ret < 0)
  return ret;

 ret = ksz9131_of_load_skew_values(phydev, of_node,
       MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
       rx_data_skews, 4);
 if (ret < 0)
  return ret;

 ret = ksz9131_of_load_skew_values(phydev, of_node,
       MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
       tx_data_skews, 4);
 if (ret < 0)
  return ret;

 ret = ksz9131_led_errata(phydev);
 if (ret < 0)
  return ret;

 return 0;
}

#define MII_KSZ9131_AUTO_MDIX  0x1C
#define MII_KSZ9131_AUTO_MDI_SET BIT(7)
#define MII_KSZ9131_AUTO_MDIX_SWAP_OFF BIT(6)
#define MII_KSZ9131_DIG_AXAN_STS 0x14
#define MII_KSZ9131_DIG_AXAN_STS_LINK_DET BIT(14)
#define MII_KSZ9131_DIG_AXAN_STS_A_SELECT BIT(12)

static int ksz9131_mdix_update(struct phy_device *phydev)
{
 int ret;

 if (phydev->mdix_ctrl != ETH_TP_MDI_AUTO) {
  phydev->mdix = phydev->mdix_ctrl;
 } else {
  ret = phy_read(phydev, MII_KSZ9131_DIG_AXAN_STS);
  if (ret < 0)
   return ret;

  if (ret & MII_KSZ9131_DIG_AXAN_STS_LINK_DET) {
   if (ret & MII_KSZ9131_DIG_AXAN_STS_A_SELECT)
    phydev->mdix = ETH_TP_MDI;
   else
    phydev->mdix = ETH_TP_MDI_X;
  } else {
   phydev->mdix = ETH_TP_MDI_INVALID;
  }
 }

 return 0;
}

static int ksz9131_config_mdix(struct phy_device *phydev, u8 ctrl)
{
 u16 val;

 switch (ctrl) {
 case ETH_TP_MDI:
  val = MII_KSZ9131_AUTO_MDIX_SWAP_OFF |
        MII_KSZ9131_AUTO_MDI_SET;
  break;
 case ETH_TP_MDI_X:
  val = MII_KSZ9131_AUTO_MDIX_SWAP_OFF;
  break;
 case ETH_TP_MDI_AUTO:
  val = 0;
  break;
 default:
  return 0;
 }

 return phy_modify(phydev, MII_KSZ9131_AUTO_MDIX,
     MII_KSZ9131_AUTO_MDIX_SWAP_OFF |
     MII_KSZ9131_AUTO_MDI_SET, val);
}

static int ksz9131_read_status(struct phy_device *phydev)
{
 int ret;

 ret = ksz9131_mdix_update(phydev);
 if (ret < 0)
  return ret;

 return genphy_read_status(phydev);
}

static int ksz9131_config_aneg(struct phy_device *phydev)
{
 int ret;

 ret = ksz9131_config_mdix(phydev, phydev->mdix_ctrl);
 if (ret)
  return ret;

 return genphy_config_aneg(phydev);
}

static int ksz9477_get_features(struct phy_device *phydev)
{
 int ret;

 ret = genphy_read_abilities(phydev);
 if (ret)
  return ret;

 /* The "EEE control and capability 1" (Register 3.20) seems to be
 * influenced by the "EEE advertisement 1" (Register 7.60). Changes
 * on the 7.60 will affect 3.20. So, we need to construct our own list
 * of caps.
 * KSZ8563R should have 100BaseTX/Full only.
 */

 linkmode_and(phydev->supported_eee, phydev->supported,
       PHY_EEE_CAP1_FEATURES);

 return 0;
}

#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06
#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6)
#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4)
static int ksz8873mll_read_status(struct phy_device *phydev)
{
 int regval;

 /* dummy read */
 regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);

 regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);

 if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX)
  phydev->duplex = DUPLEX_HALF;
 else
  phydev->duplex = DUPLEX_FULL;

 if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED)
  phydev->speed = SPEED_10;
 else
  phydev->speed = SPEED_100;

 phydev->link = 1;
 phydev->pause = phydev->asym_pause = 0;

 return 0;
}

static int ksz9031_get_features(struct phy_device *phydev)
{
 int ret;

 ret = genphy_read_abilities(phydev);
 if (ret < 0)
  return ret;

 /* Silicon Errata Sheet (DS80000691D or DS80000692D):
 * Whenever the device's Asymmetric Pause capability is set to 1,
 * link-up may fail after a link-up to link-down transition.
 *
 * The Errata Sheet is for ksz9031, but ksz9021 has the same issue
 *
 * Workaround:
 * Do not enable the Asymmetric Pause capability bit.
 */

 linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);

 /* We force setting the Pause capability as the core will force the
 * Asymmetric Pause capability to 1 otherwise.
 */

 linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);

 return 0;
}

static int ksz9031_read_status(struct phy_device *phydev)
{
 int err;
 int regval;

 err = genphy_read_status(phydev);
 if (err)
  return err;

 /* Make sure the PHY is not broken. Read idle error count,
 * and reset the PHY if it is maxed out.
 */

 regval = phy_read(phydev, MII_STAT1000);
 if ((regval & 0xFF) == 0xFF) {
  phy_init_hw(phydev);
  phydev->link = 0;
  if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev))
   phydev->drv->config_intr(phydev);
  return genphy_config_aneg(phydev);
 }

 return 0;
}

static int ksz9x31_cable_test_start(struct phy_device *phydev)
{
 struct kszphy_priv *priv = phydev->priv;
 int ret;

 /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
 * Prior to running the cable diagnostics, Auto-negotiation should
 * be disabled, full duplex set and the link speed set to 1000Mbps
 * via the Basic Control Register.
 */

 ret = phy_modify(phydev, MII_BMCR,
    BMCR_SPEED1000 | BMCR_FULLDPLX |
    BMCR_ANENABLE | BMCR_SPEED100,
    BMCR_SPEED1000 | BMCR_FULLDPLX);
 if (ret)
  return ret;

 /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
 * The Master-Slave configuration should be set to Slave by writing
 * a value of 0x1000 to the Auto-Negotiation Master Slave Control
 * Register.
 */

 ret = phy_read(phydev, MII_CTRL1000);
 if (ret < 0)
  return ret;

 /* Cache these bits, they need to be restored once LinkMD finishes. */
 priv->vct_ctrl1000 = ret & (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
 ret &= ~(CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
 ret |= CTL1000_ENABLE_MASTER;

 return phy_write(phydev, MII_CTRL1000, ret);
}

static int ksz9x31_cable_test_result_trans(u16 status)
{
 switch (FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status)) {
 case KSZ9x31_LMD_VCT_ST_NORMAL:
  return ETHTOOL_A_CABLE_RESULT_CODE_OK;
 case KSZ9x31_LMD_VCT_ST_OPEN:
  return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
 case KSZ9x31_LMD_VCT_ST_SHORT:
  return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
 case KSZ9x31_LMD_VCT_ST_FAIL:
  fallthrough;
 default:
  return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
 }
}

static bool ksz9x31_cable_test_failed(u16 status)
{
 int stat = FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status);

 return stat == KSZ9x31_LMD_VCT_ST_FAIL;
}

static bool ksz9x31_cable_test_fault_length_valid(u16 status)
{
 switch (FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status)) {
 case KSZ9x31_LMD_VCT_ST_OPEN:
  fallthrough;
 case KSZ9x31_LMD_VCT_ST_SHORT:
  return true;
 }
 return false;
}

static int ksz9x31_cable_test_fault_length(struct phy_device *phydev, u16 stat)
{
 int dt = FIELD_GET(KSZ9x31_LMD_VCT_DATA_MASK, stat);

 /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
 *
 * distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity
 */

 if (phydev_id_compare(phydev, PHY_ID_KSZ9131) ||
     phydev_id_compare(phydev, PHY_ID_KSZ9477))
  dt = clamp(dt - 22, 0, 255);

 return (dt * 400) / 10;
}

static int ksz9x31_cable_test_wait_for_completion(struct phy_device *phydev)
{
 int val, ret;

 ret = phy_read_poll_timeout(phydev, KSZ9x31_LMD, val,
        !(val & KSZ9x31_LMD_VCT_EN),
        30000, 100000, true);

 return ret < 0 ? ret : 0;
}

static int ksz9x31_cable_test_get_pair(int pair)
{
 static const int ethtool_pair[] = {
  ETHTOOL_A_CABLE_PAIR_A,
  ETHTOOL_A_CABLE_PAIR_B,
  ETHTOOL_A_CABLE_PAIR_C,
  ETHTOOL_A_CABLE_PAIR_D,
 };

 return ethtool_pair[pair];
}

static int ksz9x31_cable_test_one_pair(struct phy_device *phydev, int pair)
{
 int ret, val;

 /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic
 * To test each individual cable pair, set the cable pair in the Cable
 * Diagnostics Test Pair (VCT_PAIR[1:0]) field of the LinkMD Cable
 * Diagnostic Register, along with setting the Cable Diagnostics Test
 * Enable (VCT_EN) bit. The Cable Diagnostics Test Enable (VCT_EN) bit
 * will self clear when the test is concluded.
 */

 ret = phy_write(phydev, KSZ9x31_LMD,
   KSZ9x31_LMD_VCT_EN | KSZ9x31_LMD_VCT_PAIR(pair));
 if (ret)
  return ret;

 ret = ksz9x31_cable_test_wait_for_completion(phydev);
 if (ret)
  return ret;

 val = phy_read(phydev, KSZ9x31_LMD);
 if (val < 0)
  return val;

 if (ksz9x31_cable_test_failed(val))
  return -EAGAIN;

 ret = ethnl_cable_test_result(phydev,
          ksz9x31_cable_test_get_pair(pair),
          ksz9x31_cable_test_result_trans(val));
 if (ret)
  return ret;

 if (!ksz9x31_cable_test_fault_length_valid(val))
  return 0;

 return ethnl_cable_test_fault_length(phydev,
          ksz9x31_cable_test_get_pair(pair),
          ksz9x31_cable_test_fault_length(phydev, val));
}

static int ksz9x31_cable_test_get_status(struct phy_device *phydev,
      bool *finished)
{
 struct kszphy_priv *priv = phydev->priv;
 unsigned long pair_mask;
 int retries = 20;
 int pair, ret, rv;

 *finished = false;

 if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
         phydev->supported) ||
     linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
         phydev->supported))
  pair_mask = 0xf; /* All pairs */
 else
  pair_mask = 0x3; /* Pairs A and B only */

 /* Try harder if link partner is active */
 while (pair_mask && retries--) {
  for_each_set_bit(pair, &pair_mask, 4) {
   ret = ksz9x31_cable_test_one_pair(phydev, pair);
   if (ret == -EAGAIN)
    continue;
   if (ret < 0)
    return ret;
   clear_bit(pair, &pair_mask);
  }
  /* If link partner is in autonegotiation mode it will send 2ms
 * of FLPs with at least 6ms of silence.
 * Add 2ms sleep to have better chances to hit this silence.
 */

  if (pair_mask)
   usleep_range(2000, 3000);
 }

 /* Report remaining unfinished pair result as unknown. */
 for_each_set_bit(pair, &pair_mask, 4) {
  ret = ethnl_cable_test_result(phydev,
           ksz9x31_cable_test_get_pair(pair),
           ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC);
 }

 *finished = true;

 /* Restore cached bits from before LinkMD got started. */
 rv = phy_modify(phydev, MII_CTRL1000,
   CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER,
   priv->vct_ctrl1000);
 if (rv)
  return rv;

 return ret;
}

static int ksz8873mll_config_aneg(struct phy_device *phydev)
{
 return 0;
}

static int ksz886x_config_mdix(struct phy_device *phydev, u8 ctrl)
{
 u16 val;

 switch (ctrl) {
 case ETH_TP_MDI:
  val = KSZ886X_BMCR_DISABLE_AUTO_MDIX;
  break;
 case ETH_TP_MDI_X:
  /* Note: The naming of the bit KSZ886X_BMCR_FORCE_MDI is bit
 * counter intuitive, the "-X" in "1 = Force MDI" in the data
 * sheet seems to be missing:
 * 1 = Force MDI (sic!) (transmit on RX+/RX- pins)
 * 0 = Normal operation (transmit on TX+/TX- pins)
 */

  val = KSZ886X_BMCR_DISABLE_AUTO_MDIX | KSZ886X_BMCR_FORCE_MDI;
  break;
 case ETH_TP_MDI_AUTO:
  val = 0;
  break;
 default:
  return 0;
 }

 return phy_modify(phydev, MII_BMCR,
     KSZ886X_BMCR_HP_MDIX | KSZ886X_BMCR_FORCE_MDI |
     KSZ886X_BMCR_DISABLE_AUTO_MDIX,
     KSZ886X_BMCR_HP_MDIX | val);
}

static int ksz886x_config_aneg(struct phy_device *phydev)
{
 int ret;

 ret = genphy_config_aneg(phydev);
 if (ret)
  return ret;

 if (phydev->autoneg != AUTONEG_ENABLE) {
  /* When autonegotation is disabled, we need to manually force
 * the link state. If we don't do this, the PHY will keep
 * sending Fast Link Pulses (FLPs) which are part of the
 * autonegotiation process. This is not desired when
 * autonegotiation is off.
 */

  ret = phy_set_bits(phydev, MII_KSZPHY_CTRL,
       KSZ886X_CTRL_FORCE_LINK);
  if (ret)
   return ret;
 } else {
  /* If we had previously forced the link state, we need to
 * clear KSZ886X_CTRL_FORCE_LINK bit now. Otherwise, the PHY
 * will not perform autonegotiation.
 */

  ret = phy_clear_bits(phydev, MII_KSZPHY_CTRL,
         KSZ886X_CTRL_FORCE_LINK);
  if (ret)
   return ret;
 }

 /* The MDI-X configuration is automatically changed by the PHY after
 * switching from autoneg off to on. So, take MDI-X configuration under
 * own control and set it after autoneg configuration was done.
 */

 return ksz886x_config_mdix(phydev, phydev->mdix_ctrl);
}

static int ksz886x_mdix_update(struct phy_device *phydev)
{
 int ret;

 ret = phy_read(phydev, MII_BMCR);
 if (ret < 0)
  return ret;

 if (ret & KSZ886X_BMCR_DISABLE_AUTO_MDIX) {
  if (ret & KSZ886X_BMCR_FORCE_MDI)
   phydev->mdix_ctrl = ETH_TP_MDI_X;
  else
   phydev->mdix_ctrl = ETH_TP_MDI;
 } else {
  phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
 }

 ret = phy_read(phydev, MII_KSZPHY_CTRL);
 if (ret < 0)
  return ret;

 /* Same reverse logic as KSZ886X_BMCR_FORCE_MDI */
 if (ret & KSZ886X_CTRL_MDIX_STAT)
  phydev->mdix = ETH_TP_MDI_X;
 else
  phydev->mdix = ETH_TP_MDI;

 return 0;
}

static int ksz886x_read_status(struct phy_device *phydev)
{
 int ret;

 ret = ksz886x_mdix_update(phydev);
 if (ret < 0)
  return ret;

 return genphy_read_status(phydev);
}

static int ksz9477_mdix_update(struct phy_device *phydev)
{
 if (phydev->mdix_ctrl != ETH_TP_MDI_AUTO)
  phydev->mdix = phydev->mdix_ctrl;
 else
  phydev->mdix = ETH_TP_MDI_INVALID;

 return 0;
}

static int ksz9477_read_mdix_ctrl(struct phy_device *phydev)
{
 int val;

 val = phy_read(phydev, MII_KSZ9131_AUTO_MDIX);
 if (val < 0)
  return val;

 if (!(val & MII_KSZ9131_AUTO_MDIX_SWAP_OFF))
  phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
 else if (val & MII_KSZ9131_AUTO_MDI_SET)
  phydev->mdix_ctrl = ETH_TP_MDI;
 else
  phydev->mdix_ctrl = ETH_TP_MDI_X;

 return 0;
}

static int ksz9477_read_status(struct phy_device *phydev)
{
 int ret;

 ret = ksz9477_mdix_update(phydev);
 if (ret)
  return ret;

 return genphy_read_status(phydev);
}

static int ksz9477_config_aneg(struct phy_device *phydev)
{
 int ret;

 ret = ksz9131_config_mdix(phydev, phydev->mdix_ctrl);
 if (ret)
  return ret;

 return genphy_config_aneg(phydev);
}

struct ksz9477_errata_write {
 u8 dev_addr;
 u8 reg_addr;
 u16 val;
};

static const struct ksz9477_errata_write ksz9477_errata_writes[] = {
  /* Register settings are needed to improve PHY receive performance */
 {0x01, 0x6f, 0xdd0b},
 {0x01, 0x8f, 0x6032},
 {0x01, 0x9d, 0x248c},
 {0x01, 0x75, 0x0060},
 {0x01, 0xd3, 0x7777},
 {0x1c, 0x06, 0x3008},
 {0x1c, 0x08, 0x2000},

 /* Transmit waveform amplitude can be improved (1000BASE-T, 100BASE-TX, 10BASE-Te) */
 {0x1c, 0x04, 0x00d0},

 /* Register settings are required to meet data sheet supply current specifications */
 {0x1c, 0x13, 0x6eff},
 {0x1c, 0x14, 0xe6ff},
 {0x1c, 0x15, 0x6eff},
 {0x1c, 0x16, 0xe6ff},
 {0x1c, 0x17, 0x00ff},
 {0x1c, 0x18, 0x43ff},
 {0x1c, 0x19, 0xc3ff},
 {0x1c, 0x1a, 0x6fff},
 {0x1c, 0x1b, 0x07ff},
 {0x1c, 0x1c, 0x0fff},
 {0x1c, 0x1d, 0xe7ff},
 {0x1c, 0x1e, 0xefff},
 {0x1c, 0x20, 0xeeee},
};

static int ksz9477_phy_errata(struct phy_device *phydev)
{
 int err;
 int i;

 /* Apply PHY settings to address errata listed in
 * KSZ9477, KSZ9897, KSZ9896, KSZ9567, KSZ8565
 * Silicon Errata and Data Sheet Clarification documents.
 *
 * Document notes: Before configuring the PHY MMD registers, it is
 * necessary to set the PHY to 100 Mbps speed with auto-negotiation
 * disabled by writing to register 0xN100-0xN101. After writing the
 * MMD registers, and after all errata workarounds that involve PHY
 * register settings, write register 0xN100-0xN101 again to enable
 * and restart auto-negotiation.
 */

 err = phy_write(phydev, MII_BMCR, BMCR_SPEED100 | BMCR_FULLDPLX);
 if (err)
  return err;

 for (i = 0; i < ARRAY_SIZE(ksz9477_errata_writes); ++i) {
  const struct ksz9477_errata_write *errata = &ksz9477_errata_writes[i];

  err = phy_write_mmd(phydev, errata->dev_addr, errata->reg_addr, errata->val);
  if (err)
   return err;
 }

 err = genphy_restart_aneg(phydev);
 if (err)
  return err;

 return err;
}

static int ksz9477_config_init(struct phy_device *phydev)
{
 int err;

 /* Only KSZ9897 family of switches needs this fix. */
 if ((phydev->phy_id & 0xf) == 1) {
  err = ksz9477_phy_errata(phydev);
  if (err)
   return err;
 }

 /* Read initial MDI-X config state. So, we do not need to poll it
 * later on.
 */

 err = ksz9477_read_mdix_ctrl(phydev);
 if (err)
  return err;

 return kszphy_config_init(phydev);
}

static int kszphy_get_sset_count(struct phy_device *phydev)
{
 return ARRAY_SIZE(kszphy_hw_stats);
}

static void kszphy_get_strings(struct phy_device *phydev, u8 *data)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++)
  ethtool_puts(&data, kszphy_hw_stats[i].string);
}

static u64 kszphy_get_stat(struct phy_device *phydev, int i)
{
 struct kszphy_hw_stat stat = kszphy_hw_stats[i];
 struct kszphy_priv *priv = phydev->priv;
 int val;
 u64 ret;

 val = phy_read(phydev, stat.reg);
 if (val < 0) {
  ret = U64_MAX;
 } else {
  val = val & ((1 << stat.bits) - 1);
  priv->stats[i] += val;
  ret = priv->stats[i];
 }

 return ret;
}

static void kszphy_get_stats(struct phy_device *phydev,
        struct ethtool_stats *stats, u64 *data)
{
 int i;

 for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++)
  data[i] = kszphy_get_stat(phydev, i);
}

/* KSZ9477 PHY RXER Counter. Probably supported by other PHYs like KSZ9313,
 * etc. The counter is incremented when the PHY receives a frame with one or
 * more symbol errors. The counter is cleared when the register is read.
 */

#define MII_KSZ9477_PHY_RXER_COUNTER 0x15

static int kszphy_update_stats(struct phy_device *phydev)
{
 struct kszphy_priv *priv = phydev->priv;
 int ret;

 ret = phy_read(phydev, MII_KSZ9477_PHY_RXER_COUNTER);
 if (ret < 0)
  return ret;

 priv->phy_stats.rx_err_pkt_cnt += ret;

 return 0;
}

static void kszphy_get_phy_stats(struct phy_device *phydev,
     struct ethtool_eth_phy_stats *eth_stats,
     struct ethtool_phy_stats *stats)
{
 struct kszphy_priv *priv = phydev->priv;

 stats->rx_errors = priv->phy_stats.rx_err_pkt_cnt;
}

/* Base register for Signal Quality Indicator (SQI) - Channel A
 *
 * MMD Address: MDIO_MMD_PMAPMD (0x01)
 * Register:    0xAC (Channel A)
 * Each channel (pair) has its own register:
 *   Channel A: 0xAC
 *   Channel B: 0xAD
 *   Channel C: 0xAE
 *   Channel D: 0xAF
 */

#define KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A 0xac

/* SQI field mask for bits [14:8]
 *
 * SQI indicates relative quality of the signal.
 * A lower value indicates better signal quality.
 */

#define KSZ9477_MMD_SQI_MASK   GENMASK(14, 8)

#define KSZ9477_MAX_CHANNELS   4
#define KSZ9477_SQI_MAX    7

/* Number of SQI samples to average for a stable result.
 *
 * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
 * For noisy environments, a minimum of 30–50 readings is recommended.
 */

#define KSZ9477_SQI_SAMPLE_COUNT  40

/* The hardware SQI register provides a raw value from 0-127, where a lower
 * value indicates better signal quality. However, empirical testing has
 * shown that only the 0-7 range is relevant for a functional link. A raw
 * value of 8 or higher was measured directly before link drop. This aligns
 * with the OPEN Alliance recommendation that SQI=0 should represent the
 * pre-failure state.
 *
 * This table provides a non-linear mapping from the useful raw hardware
 * values (0-7) to the standard 0-7 SQI scale, where higher is better.
 */

static const u8 ksz_sqi_mapping[] = {
 7, /* raw 0 -> SQI 7 */
 7, /* raw 1 -> SQI 7 */
 6, /* raw 2 -> SQI 6 */
 5, /* raw 3 -> SQI 5 */
 4, /* raw 4 -> SQI 4 */
 3, /* raw 5 -> SQI 3 */
 2, /* raw 6 -> SQI 2 */
 1, /* raw 7 -> SQI 1 */
};

/**
 * kszphy_get_sqi - Read, average, and map Signal Quality Index (SQI)
 * @phydev: the PHY device
 *
 * This function reads and processes the raw Signal Quality Index from the
 * PHY. Based on empirical testing, a raw value of 8 or higher indicates a
 * pre-failure state and is mapped to SQI 0. Raw values from 0-7 are
 * mapped to the standard 0-7 SQI scale via a lookup table.
 *
 * Return: SQI value (0–7), or a negative errno on failure.
 */

static int kszphy_get_sqi(struct phy_device *phydev)
{
 int sum[KSZ9477_MAX_CHANNELS] = { 0 };
 int worst_sqi = KSZ9477_SQI_MAX;
 int i, val, raw_sqi, ch;
 u8 channels;

 /* Determine applicable channels based on link speed */
 if (phydev->speed == SPEED_1000)
  channels = 4;
 else if (phydev->speed == SPEED_100)
  channels = 1;
 else
  return -EOPNOTSUPP;

 /* Sample and accumulate SQI readings for each pair (currently only one).
 *
 * Reference: KSZ9477S Datasheet DS00002392C, Section 4.1.11 (page 26)
 * - The SQI register is updated every 2 µs.
 * - Values may fluctuate significantly, even in low-noise environments.
 * - For reliable estimation, average a minimum of 30–50 samples
 *   (recommended for noisy environments)
 * - In noisy environments, individual readings are highly unreliable.
 *
 * We use 40 samples per pair with a delay of 3 µs between each
 * read to ensure new values are captured (2 µs update interval).
 */

 for (i = 0; i < KSZ9477_SQI_SAMPLE_COUNT; i++) {
  for (ch = 0; ch < channels; ch++) {
   val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
        KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A + ch);
   if (val < 0)
    return val;

   raw_sqi = FIELD_GET(KSZ9477_MMD_SQI_MASK, val);
   sum[ch] += raw_sqi;

   /* We communicate with the PHY via MDIO via SPI or
 * I2C, which is relatively slow. At least slower than
 * the update interval of the SQI register.
 * So, we can skip the delay between reads.
 */

  }
 }

 /* Calculate average for each channel and find the worst SQI */
 for (ch = 0; ch < channels; ch++) {
  int avg_raw_sqi = sum[ch] / KSZ9477_SQI_SAMPLE_COUNT;
  int mapped_sqi;

  /* Handle the pre-fail/failed state first. */
  if (avg_raw_sqi >= ARRAY_SIZE(ksz_sqi_mapping))
   mapped_sqi = 0;
  else
   /* Use the lookup table for the good signal range. */
   mapped_sqi = ksz_sqi_mapping[avg_raw_sqi];

  if (mapped_sqi < worst_sqi)
   worst_sqi = mapped_sqi;
 }

 return worst_sqi;
}

static int kszphy_get_sqi_max(struct phy_device *phydev)
{
 return KSZ9477_SQI_MAX;
}

static void kszphy_enable_clk(struct phy_device *phydev)
{
 struct kszphy_priv *priv = phydev->priv;

 if (!priv->clk_enable && priv->clk) {
  clk_prepare_enable(priv->clk);
  priv->clk_enable = true;
 }
}

static void kszphy_disable_clk(struct phy_device *phydev)
{
 struct kszphy_priv *priv = phydev->priv;

 if (priv->clk_enable && priv->clk) {
  clk_disable_unprepare(priv->clk);
  priv->clk_enable = false;
 }
}

static int kszphy_generic_resume(struct phy_device *phydev)
{
 kszphy_enable_clk(phydev);

 return genphy_resume(phydev);
}

static int kszphy_generic_suspend(struct phy_device *phydev)
{
 int ret;

 ret = genphy_suspend(phydev);
 if (ret)
  return ret;

 kszphy_disable_clk(phydev);

 return 0;
}

static int kszphy_suspend(struct phy_device *phydev)
{
 /* Disable PHY Interrupts */
 if (phy_interrupt_is_valid(phydev)) {
  phydev->interrupts = PHY_INTERRUPT_DISABLED;
  if (phydev->drv->config_intr)
   phydev->drv->config_intr(phydev);
 }

 return kszphy_generic_suspend(phydev);
}

static void kszphy_parse_led_mode(struct phy_device *phydev)
{
 const struct kszphy_type *type = phydev->drv->driver_data;
 const struct device_node *np = phydev->mdio.dev.of_node;
 struct kszphy_priv *priv = phydev->priv;
 int ret;

 if (type && type->led_mode_reg) {
  ret = of_property_read_u32(np, "micrel,led-mode",
        &priv->led_mode);

  if (ret)
   priv->led_mode = -1;

  if (priv->led_mode > 3) {
   phydev_err(phydev, "invalid led mode: 0x%02x\n",
       priv->led_mode);
   priv->led_mode = -1;
  }
 } else {
  priv->led_mode = -1;
 }
}

static int kszphy_resume(struct phy_device *phydev)
{
 int ret;

 ret = kszphy_generic_resume(phydev);
 if (ret)
  return ret;

 /* After switching from power-down to normal mode, an internal global
 * reset is automatically generated. Wait a minimum of 1 ms before
 * read/write access to the PHY registers.
 */

 usleep_range(1000, 2000);

 ret = kszphy_config_reset(phydev);
 if (ret)
  return ret;

 /* Enable PHY Interrupts */
 if (phy_interrupt_is_valid(phydev)) {
  phydev->interrupts = PHY_INTERRUPT_ENABLED;
  if (phydev->drv->config_intr)
   phydev->drv->config_intr(phydev);
 }

 return 0;
}

/* Because of errata DS80000700A, receiver error following software
 * power down. Suspend and resume callbacks only disable and enable
 * external rmii reference clock.
 */

static int ksz8041_resume(struct phy_device *phydev)
{
 kszphy_enable_clk(phydev);

 return 0;
}

static int ksz8041_suspend(struct phy_device *phydev)
{
 kszphy_disable_clk(phydev);

 return 0;
}

static int ksz9477_resume(struct phy_device *phydev)
{
 int ret;

 /* No need to initialize registers if not powered down. */
 ret = phy_read(phydev, MII_BMCR);
 if (ret < 0)
  return ret;
 if (!(ret & BMCR_PDOWN))
  return 0;

 genphy_resume(phydev);

 /* After switching from power-down to normal mode, an internal global
 * reset is automatically generated. Wait a minimum of 1 ms before
 * read/write access to the PHY registers.
 */

 usleep_range(1000, 2000);

 /* Only KSZ9897 family of switches needs this fix. */
 if ((phydev->phy_id & 0xf) == 1) {
  ret = ksz9477_phy_errata(phydev);
  if (ret)
   return ret;
 }

 /* Enable PHY Interrupts */
 if (phy_interrupt_is_valid(phydev)) {
  phydev->interrupts = PHY_INTERRUPT_ENABLED;
  if (phydev->drv->config_intr)
   phydev->drv->config_intr(phydev);
 }

 return 0;
}

static int ksz8061_resume(struct phy_device *phydev)
{
 int ret;

 /* This function can be called twice when the Ethernet device is on. */
 ret = phy_read(phydev, MII_BMCR);
 if (ret < 0)
  return ret;
 if (!(ret & BMCR_PDOWN))
  return 0;

 ret = kszphy_generic_resume(phydev);
 if (ret)
  return ret;

 usleep_range(1000, 2000);

 /* Re-program the value after chip is reset. */
 ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A);
 if (ret)
  return ret;

 /* Enable PHY Interrupts */
 if (phy_interrupt_is_valid(phydev)) {
  phydev->interrupts = PHY_INTERRUPT_ENABLED;
  if (phydev->drv->config_intr)
   phydev->drv->config_intr(phydev);
 }

 return 0;
}

static int ksz8061_suspend(struct phy_device *phydev)
{
 return kszphy_suspend(phydev);
}

static int kszphy_probe(struct phy_device *phydev)
{
 const struct kszphy_type *type = phydev->drv->driver_data;
 const struct device_node *np = phydev->mdio.dev.of_node;
 struct kszphy_priv *priv;
 struct clk *clk;

 priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
 if (!priv)
  return -ENOMEM;

 phydev->priv = priv;

 priv->type = type;

 kszphy_parse_led_mode(phydev);

 clk = devm_clk_get_optional_enabled(&phydev->mdio.dev, "rmii-ref");
 /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
 if (!IS_ERR_OR_NULL(clk)) {
  unsigned long rate = clk_get_rate(clk);
  bool rmii_ref_clk_sel_25_mhz;

  if (type)
   priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel;
  rmii_ref_clk_sel_25_mhz = of_property_read_bool(np,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.