162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/net/phy/micrel.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Driver for Micrel PHYs 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: David J. Choi 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2010-2013 Micrel, Inc. 1062306a36Sopenharmony_ci * Copyright (c) 2014 Johan Hovold <johan@kernel.org> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Support : Micrel Phys: 1362306a36Sopenharmony_ci * Giga phys: ksz9021, ksz9031, ksz9131, lan8841, lan8814 1462306a36Sopenharmony_ci * 100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041 1562306a36Sopenharmony_ci * ksz8021, ksz8031, ksz8051, 1662306a36Sopenharmony_ci * ksz8081, ksz8091, 1762306a36Sopenharmony_ci * ksz8061, 1862306a36Sopenharmony_ci * Switch : ksz8873, ksz886x 1962306a36Sopenharmony_ci * ksz9477, lan8804 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/bitfield.h> 2362306a36Sopenharmony_ci#include <linux/ethtool_netlink.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/phy.h> 2762306a36Sopenharmony_ci#include <linux/micrel_phy.h> 2862306a36Sopenharmony_ci#include <linux/of.h> 2962306a36Sopenharmony_ci#include <linux/clk.h> 3062306a36Sopenharmony_ci#include <linux/delay.h> 3162306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 3262306a36Sopenharmony_ci#include <linux/ptp_clock.h> 3362306a36Sopenharmony_ci#include <linux/ptp_classify.h> 3462306a36Sopenharmony_ci#include <linux/net_tstamp.h> 3562306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Operation Mode Strap Override */ 3862306a36Sopenharmony_ci#define MII_KSZPHY_OMSO 0x16 3962306a36Sopenharmony_ci#define KSZPHY_OMSO_FACTORY_TEST BIT(15) 4062306a36Sopenharmony_ci#define KSZPHY_OMSO_B_CAST_OFF BIT(9) 4162306a36Sopenharmony_ci#define KSZPHY_OMSO_NAND_TREE_ON BIT(5) 4262306a36Sopenharmony_ci#define KSZPHY_OMSO_RMII_OVERRIDE BIT(1) 4362306a36Sopenharmony_ci#define KSZPHY_OMSO_MII_OVERRIDE BIT(0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* general Interrupt control/status reg in vendor specific block. */ 4662306a36Sopenharmony_ci#define MII_KSZPHY_INTCS 0x1B 4762306a36Sopenharmony_ci#define KSZPHY_INTCS_JABBER BIT(15) 4862306a36Sopenharmony_ci#define KSZPHY_INTCS_RECEIVE_ERR BIT(14) 4962306a36Sopenharmony_ci#define KSZPHY_INTCS_PAGE_RECEIVE BIT(13) 5062306a36Sopenharmony_ci#define KSZPHY_INTCS_PARELLEL BIT(12) 5162306a36Sopenharmony_ci#define KSZPHY_INTCS_LINK_PARTNER_ACK BIT(11) 5262306a36Sopenharmony_ci#define KSZPHY_INTCS_LINK_DOWN BIT(10) 5362306a36Sopenharmony_ci#define KSZPHY_INTCS_REMOTE_FAULT BIT(9) 5462306a36Sopenharmony_ci#define KSZPHY_INTCS_LINK_UP BIT(8) 5562306a36Sopenharmony_ci#define KSZPHY_INTCS_ALL (KSZPHY_INTCS_LINK_UP |\ 5662306a36Sopenharmony_ci KSZPHY_INTCS_LINK_DOWN) 5762306a36Sopenharmony_ci#define KSZPHY_INTCS_LINK_DOWN_STATUS BIT(2) 5862306a36Sopenharmony_ci#define KSZPHY_INTCS_LINK_UP_STATUS BIT(0) 5962306a36Sopenharmony_ci#define KSZPHY_INTCS_STATUS (KSZPHY_INTCS_LINK_DOWN_STATUS |\ 6062306a36Sopenharmony_ci KSZPHY_INTCS_LINK_UP_STATUS) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* LinkMD Control/Status */ 6362306a36Sopenharmony_ci#define KSZ8081_LMD 0x1d 6462306a36Sopenharmony_ci#define KSZ8081_LMD_ENABLE_TEST BIT(15) 6562306a36Sopenharmony_ci#define KSZ8081_LMD_STAT_NORMAL 0 6662306a36Sopenharmony_ci#define KSZ8081_LMD_STAT_OPEN 1 6762306a36Sopenharmony_ci#define KSZ8081_LMD_STAT_SHORT 2 6862306a36Sopenharmony_ci#define KSZ8081_LMD_STAT_FAIL 3 6962306a36Sopenharmony_ci#define KSZ8081_LMD_STAT_MASK GENMASK(14, 13) 7062306a36Sopenharmony_ci/* Short cable (<10 meter) has been detected by LinkMD */ 7162306a36Sopenharmony_ci#define KSZ8081_LMD_SHORT_INDICATOR BIT(12) 7262306a36Sopenharmony_ci#define KSZ8081_LMD_DELTA_TIME_MASK GENMASK(8, 0) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define KSZ9x31_LMD 0x12 7562306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_EN BIT(15) 7662306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DIS_TX BIT(14) 7762306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_PAIR(n) (((n) & 0x3) << 12) 7862306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_SEL_RESULT 0 7962306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_SEL_THRES_HI BIT(10) 8062306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_SEL_THRES_LO BIT(11) 8162306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_SEL_MASK GENMASK(11, 10) 8262306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_ST_NORMAL 0 8362306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_ST_OPEN 1 8462306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_ST_SHORT 2 8562306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_ST_FAIL 3 8662306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_ST_MASK GENMASK(9, 8) 8762306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DATA_REFLECTED_INVALID BIT(7) 8862306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DATA_SIG_WAIT_TOO_LONG BIT(6) 8962306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DATA_MASK100 BIT(5) 9062306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DATA_NLP_FLP BIT(4) 9162306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DATA_LO_PULSE_MASK GENMASK(3, 2) 9262306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DATA_HI_PULSE_MASK GENMASK(1, 0) 9362306a36Sopenharmony_ci#define KSZ9x31_LMD_VCT_DATA_MASK GENMASK(7, 0) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define KSZPHY_WIRE_PAIR_MASK 0x3 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define LAN8814_CABLE_DIAG 0x12 9862306a36Sopenharmony_ci#define LAN8814_CABLE_DIAG_STAT_MASK GENMASK(9, 8) 9962306a36Sopenharmony_ci#define LAN8814_CABLE_DIAG_VCT_DATA_MASK GENMASK(7, 0) 10062306a36Sopenharmony_ci#define LAN8814_PAIR_BIT_SHIFT 12 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define LAN8814_WIRE_PAIR_MASK 0xF 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* Lan8814 general Interrupt control/status reg in GPHY specific block. */ 10562306a36Sopenharmony_ci#define LAN8814_INTC 0x18 10662306a36Sopenharmony_ci#define LAN8814_INTS 0x1B 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define LAN8814_INT_LINK_DOWN BIT(2) 10962306a36Sopenharmony_ci#define LAN8814_INT_LINK_UP BIT(0) 11062306a36Sopenharmony_ci#define LAN8814_INT_LINK (LAN8814_INT_LINK_UP |\ 11162306a36Sopenharmony_ci LAN8814_INT_LINK_DOWN) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define LAN8814_INTR_CTRL_REG 0x34 11462306a36Sopenharmony_ci#define LAN8814_INTR_CTRL_REG_POLARITY BIT(1) 11562306a36Sopenharmony_ci#define LAN8814_INTR_CTRL_REG_INTR_ENABLE BIT(0) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* Represents 1ppm adjustment in 2^32 format with 11862306a36Sopenharmony_ci * each nsec contains 4 clock cycles. 11962306a36Sopenharmony_ci * The value is calculated as following: (1/1000000)/((2^-32)/4) 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci#define LAN8814_1PPM_FORMAT 17179 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define PTP_RX_VERSION 0x0248 12462306a36Sopenharmony_ci#define PTP_TX_VERSION 0x0288 12562306a36Sopenharmony_ci#define PTP_MAX_VERSION(x) (((x) & GENMASK(7, 0)) << 8) 12662306a36Sopenharmony_ci#define PTP_MIN_VERSION(x) ((x) & GENMASK(7, 0)) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define PTP_RX_MOD 0x024F 12962306a36Sopenharmony_ci#define PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3) 13062306a36Sopenharmony_ci#define PTP_RX_TIMESTAMP_EN 0x024D 13162306a36Sopenharmony_ci#define PTP_TX_TIMESTAMP_EN 0x028D 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define PTP_TIMESTAMP_EN_SYNC_ BIT(0) 13462306a36Sopenharmony_ci#define PTP_TIMESTAMP_EN_DREQ_ BIT(1) 13562306a36Sopenharmony_ci#define PTP_TIMESTAMP_EN_PDREQ_ BIT(2) 13662306a36Sopenharmony_ci#define PTP_TIMESTAMP_EN_PDRES_ BIT(3) 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#define PTP_TX_PARSE_L2_ADDR_EN 0x0284 13962306a36Sopenharmony_ci#define PTP_RX_PARSE_L2_ADDR_EN 0x0244 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define PTP_TX_PARSE_IP_ADDR_EN 0x0285 14262306a36Sopenharmony_ci#define PTP_RX_PARSE_IP_ADDR_EN 0x0245 14362306a36Sopenharmony_ci#define LTC_HARD_RESET 0x023F 14462306a36Sopenharmony_ci#define LTC_HARD_RESET_ BIT(0) 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define TSU_HARD_RESET 0x02C1 14762306a36Sopenharmony_ci#define TSU_HARD_RESET_ BIT(0) 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#define PTP_CMD_CTL 0x0200 15062306a36Sopenharmony_ci#define PTP_CMD_CTL_PTP_DISABLE_ BIT(0) 15162306a36Sopenharmony_ci#define PTP_CMD_CTL_PTP_ENABLE_ BIT(1) 15262306a36Sopenharmony_ci#define PTP_CMD_CTL_PTP_CLOCK_READ_ BIT(3) 15362306a36Sopenharmony_ci#define PTP_CMD_CTL_PTP_CLOCK_LOAD_ BIT(4) 15462306a36Sopenharmony_ci#define PTP_CMD_CTL_PTP_LTC_STEP_SEC_ BIT(5) 15562306a36Sopenharmony_ci#define PTP_CMD_CTL_PTP_LTC_STEP_NSEC_ BIT(6) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#define PTP_CLOCK_SET_SEC_MID 0x0206 15862306a36Sopenharmony_ci#define PTP_CLOCK_SET_SEC_LO 0x0207 15962306a36Sopenharmony_ci#define PTP_CLOCK_SET_NS_HI 0x0208 16062306a36Sopenharmony_ci#define PTP_CLOCK_SET_NS_LO 0x0209 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#define PTP_CLOCK_READ_SEC_MID 0x022A 16362306a36Sopenharmony_ci#define PTP_CLOCK_READ_SEC_LO 0x022B 16462306a36Sopenharmony_ci#define PTP_CLOCK_READ_NS_HI 0x022C 16562306a36Sopenharmony_ci#define PTP_CLOCK_READ_NS_LO 0x022D 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define PTP_OPERATING_MODE 0x0241 16862306a36Sopenharmony_ci#define PTP_OPERATING_MODE_STANDALONE_ BIT(0) 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci#define PTP_TX_MOD 0x028F 17162306a36Sopenharmony_ci#define PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_ BIT(12) 17262306a36Sopenharmony_ci#define PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3) 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define PTP_RX_PARSE_CONFIG 0x0242 17562306a36Sopenharmony_ci#define PTP_RX_PARSE_CONFIG_LAYER2_EN_ BIT(0) 17662306a36Sopenharmony_ci#define PTP_RX_PARSE_CONFIG_IPV4_EN_ BIT(1) 17762306a36Sopenharmony_ci#define PTP_RX_PARSE_CONFIG_IPV6_EN_ BIT(2) 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#define PTP_TX_PARSE_CONFIG 0x0282 18062306a36Sopenharmony_ci#define PTP_TX_PARSE_CONFIG_LAYER2_EN_ BIT(0) 18162306a36Sopenharmony_ci#define PTP_TX_PARSE_CONFIG_IPV4_EN_ BIT(1) 18262306a36Sopenharmony_ci#define PTP_TX_PARSE_CONFIG_IPV6_EN_ BIT(2) 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#define PTP_CLOCK_RATE_ADJ_HI 0x020C 18562306a36Sopenharmony_ci#define PTP_CLOCK_RATE_ADJ_LO 0x020D 18662306a36Sopenharmony_ci#define PTP_CLOCK_RATE_ADJ_DIR_ BIT(15) 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci#define PTP_LTC_STEP_ADJ_HI 0x0212 18962306a36Sopenharmony_ci#define PTP_LTC_STEP_ADJ_LO 0x0213 19062306a36Sopenharmony_ci#define PTP_LTC_STEP_ADJ_DIR_ BIT(15) 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci#define LAN8814_INTR_STS_REG 0x0033 19362306a36Sopenharmony_ci#define LAN8814_INTR_STS_REG_1588_TSU0_ BIT(0) 19462306a36Sopenharmony_ci#define LAN8814_INTR_STS_REG_1588_TSU1_ BIT(1) 19562306a36Sopenharmony_ci#define LAN8814_INTR_STS_REG_1588_TSU2_ BIT(2) 19662306a36Sopenharmony_ci#define LAN8814_INTR_STS_REG_1588_TSU3_ BIT(3) 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci#define PTP_CAP_INFO 0x022A 19962306a36Sopenharmony_ci#define PTP_CAP_INFO_TX_TS_CNT_GET_(reg_val) (((reg_val) & 0x0f00) >> 8) 20062306a36Sopenharmony_ci#define PTP_CAP_INFO_RX_TS_CNT_GET_(reg_val) ((reg_val) & 0x000f) 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci#define PTP_TX_EGRESS_SEC_HI 0x0296 20362306a36Sopenharmony_ci#define PTP_TX_EGRESS_SEC_LO 0x0297 20462306a36Sopenharmony_ci#define PTP_TX_EGRESS_NS_HI 0x0294 20562306a36Sopenharmony_ci#define PTP_TX_EGRESS_NS_LO 0x0295 20662306a36Sopenharmony_ci#define PTP_TX_MSG_HEADER2 0x0299 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci#define PTP_RX_INGRESS_SEC_HI 0x0256 20962306a36Sopenharmony_ci#define PTP_RX_INGRESS_SEC_LO 0x0257 21062306a36Sopenharmony_ci#define PTP_RX_INGRESS_NS_HI 0x0254 21162306a36Sopenharmony_ci#define PTP_RX_INGRESS_NS_LO 0x0255 21262306a36Sopenharmony_ci#define PTP_RX_MSG_HEADER2 0x0259 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#define PTP_TSU_INT_EN 0x0200 21562306a36Sopenharmony_ci#define PTP_TSU_INT_EN_PTP_TX_TS_OVRFL_EN_ BIT(3) 21662306a36Sopenharmony_ci#define PTP_TSU_INT_EN_PTP_TX_TS_EN_ BIT(2) 21762306a36Sopenharmony_ci#define PTP_TSU_INT_EN_PTP_RX_TS_OVRFL_EN_ BIT(1) 21862306a36Sopenharmony_ci#define PTP_TSU_INT_EN_PTP_RX_TS_EN_ BIT(0) 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci#define PTP_TSU_INT_STS 0x0201 22162306a36Sopenharmony_ci#define PTP_TSU_INT_STS_PTP_TX_TS_OVRFL_INT_ BIT(3) 22262306a36Sopenharmony_ci#define PTP_TSU_INT_STS_PTP_TX_TS_EN_ BIT(2) 22362306a36Sopenharmony_ci#define PTP_TSU_INT_STS_PTP_RX_TS_OVRFL_INT_ BIT(1) 22462306a36Sopenharmony_ci#define PTP_TSU_INT_STS_PTP_RX_TS_EN_ BIT(0) 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci#define LAN8814_LED_CTRL_1 0x0 22762306a36Sopenharmony_ci#define LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_ BIT(6) 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* PHY Control 1 */ 23062306a36Sopenharmony_ci#define MII_KSZPHY_CTRL_1 0x1e 23162306a36Sopenharmony_ci#define KSZ8081_CTRL1_MDIX_STAT BIT(4) 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* PHY Control 2 / PHY Control (if no PHY Control 1) */ 23462306a36Sopenharmony_ci#define MII_KSZPHY_CTRL_2 0x1f 23562306a36Sopenharmony_ci#define MII_KSZPHY_CTRL MII_KSZPHY_CTRL_2 23662306a36Sopenharmony_ci/* bitmap of PHY register to set interrupt mode */ 23762306a36Sopenharmony_ci#define KSZ8081_CTRL2_HP_MDIX BIT(15) 23862306a36Sopenharmony_ci#define KSZ8081_CTRL2_MDI_MDI_X_SELECT BIT(14) 23962306a36Sopenharmony_ci#define KSZ8081_CTRL2_DISABLE_AUTO_MDIX BIT(13) 24062306a36Sopenharmony_ci#define KSZ8081_CTRL2_FORCE_LINK BIT(11) 24162306a36Sopenharmony_ci#define KSZ8081_CTRL2_POWER_SAVING BIT(10) 24262306a36Sopenharmony_ci#define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9) 24362306a36Sopenharmony_ci#define KSZPHY_RMII_REF_CLK_SEL BIT(7) 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* Write/read to/from extended registers */ 24662306a36Sopenharmony_ci#define MII_KSZPHY_EXTREG 0x0b 24762306a36Sopenharmony_ci#define KSZPHY_EXTREG_WRITE 0x8000 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci#define MII_KSZPHY_EXTREG_WRITE 0x0c 25062306a36Sopenharmony_ci#define MII_KSZPHY_EXTREG_READ 0x0d 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* Extended registers */ 25362306a36Sopenharmony_ci#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104 25462306a36Sopenharmony_ci#define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105 25562306a36Sopenharmony_ci#define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci#define PS_TO_REG 200 25862306a36Sopenharmony_ci#define FIFO_SIZE 8 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* Delay used to get the second part from the LTC */ 26162306a36Sopenharmony_ci#define LAN8841_GET_SEC_LTC_DELAY (500 * NSEC_PER_MSEC) 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistruct kszphy_hw_stat { 26462306a36Sopenharmony_ci const char *string; 26562306a36Sopenharmony_ci u8 reg; 26662306a36Sopenharmony_ci u8 bits; 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic struct kszphy_hw_stat kszphy_hw_stats[] = { 27062306a36Sopenharmony_ci { "phy_receive_errors", 21, 16}, 27162306a36Sopenharmony_ci { "phy_idle_errors", 10, 8 }, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistruct kszphy_type { 27562306a36Sopenharmony_ci u32 led_mode_reg; 27662306a36Sopenharmony_ci u16 interrupt_level_mask; 27762306a36Sopenharmony_ci u16 cable_diag_reg; 27862306a36Sopenharmony_ci unsigned long pair_mask; 27962306a36Sopenharmony_ci u16 disable_dll_tx_bit; 28062306a36Sopenharmony_ci u16 disable_dll_rx_bit; 28162306a36Sopenharmony_ci u16 disable_dll_mask; 28262306a36Sopenharmony_ci bool has_broadcast_disable; 28362306a36Sopenharmony_ci bool has_nand_tree_disable; 28462306a36Sopenharmony_ci bool has_rmii_ref_clk_sel; 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/* Shared structure between the PHYs of the same package. */ 28862306a36Sopenharmony_cistruct lan8814_shared_priv { 28962306a36Sopenharmony_ci struct phy_device *phydev; 29062306a36Sopenharmony_ci struct ptp_clock *ptp_clock; 29162306a36Sopenharmony_ci struct ptp_clock_info ptp_clock_info; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Reference counter to how many ports in the package are enabling the 29462306a36Sopenharmony_ci * timestamping 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_ci u8 ref; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Lock for ptp_clock and ref */ 29962306a36Sopenharmony_ci struct mutex shared_lock; 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistruct lan8814_ptp_rx_ts { 30362306a36Sopenharmony_ci struct list_head list; 30462306a36Sopenharmony_ci u32 seconds; 30562306a36Sopenharmony_ci u32 nsec; 30662306a36Sopenharmony_ci u16 seq_id; 30762306a36Sopenharmony_ci}; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistruct kszphy_ptp_priv { 31062306a36Sopenharmony_ci struct mii_timestamper mii_ts; 31162306a36Sopenharmony_ci struct phy_device *phydev; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci struct sk_buff_head tx_queue; 31462306a36Sopenharmony_ci struct sk_buff_head rx_queue; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci struct list_head rx_ts_list; 31762306a36Sopenharmony_ci /* Lock for Rx ts fifo */ 31862306a36Sopenharmony_ci spinlock_t rx_ts_lock; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci int hwts_tx_type; 32162306a36Sopenharmony_ci enum hwtstamp_rx_filters rx_filter; 32262306a36Sopenharmony_ci int layer; 32362306a36Sopenharmony_ci int version; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci struct ptp_clock *ptp_clock; 32662306a36Sopenharmony_ci struct ptp_clock_info ptp_clock_info; 32762306a36Sopenharmony_ci /* Lock for ptp_clock */ 32862306a36Sopenharmony_ci struct mutex ptp_lock; 32962306a36Sopenharmony_ci struct ptp_pin_desc *pin_config; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci s64 seconds; 33262306a36Sopenharmony_ci /* Lock for accessing seconds */ 33362306a36Sopenharmony_ci spinlock_t seconds_lock; 33462306a36Sopenharmony_ci}; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistruct kszphy_priv { 33762306a36Sopenharmony_ci struct kszphy_ptp_priv ptp_priv; 33862306a36Sopenharmony_ci const struct kszphy_type *type; 33962306a36Sopenharmony_ci int led_mode; 34062306a36Sopenharmony_ci u16 vct_ctrl1000; 34162306a36Sopenharmony_ci bool rmii_ref_clk_sel; 34262306a36Sopenharmony_ci bool rmii_ref_clk_sel_val; 34362306a36Sopenharmony_ci u64 stats[ARRAY_SIZE(kszphy_hw_stats)]; 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct kszphy_type lan8814_type = { 34762306a36Sopenharmony_ci .led_mode_reg = ~LAN8814_LED_CTRL_1, 34862306a36Sopenharmony_ci .cable_diag_reg = LAN8814_CABLE_DIAG, 34962306a36Sopenharmony_ci .pair_mask = LAN8814_WIRE_PAIR_MASK, 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic const struct kszphy_type ksz886x_type = { 35362306a36Sopenharmony_ci .cable_diag_reg = KSZ8081_LMD, 35462306a36Sopenharmony_ci .pair_mask = KSZPHY_WIRE_PAIR_MASK, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic const struct kszphy_type ksz8021_type = { 35862306a36Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_2, 35962306a36Sopenharmony_ci .has_broadcast_disable = true, 36062306a36Sopenharmony_ci .has_nand_tree_disable = true, 36162306a36Sopenharmony_ci .has_rmii_ref_clk_sel = true, 36262306a36Sopenharmony_ci}; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic const struct kszphy_type ksz8041_type = { 36562306a36Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_1, 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic const struct kszphy_type ksz8051_type = { 36962306a36Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_2, 37062306a36Sopenharmony_ci .has_nand_tree_disable = true, 37162306a36Sopenharmony_ci}; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic const struct kszphy_type ksz8081_type = { 37462306a36Sopenharmony_ci .led_mode_reg = MII_KSZPHY_CTRL_2, 37562306a36Sopenharmony_ci .has_broadcast_disable = true, 37662306a36Sopenharmony_ci .has_nand_tree_disable = true, 37762306a36Sopenharmony_ci .has_rmii_ref_clk_sel = true, 37862306a36Sopenharmony_ci}; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic const struct kszphy_type ks8737_type = { 38162306a36Sopenharmony_ci .interrupt_level_mask = BIT(14), 38262306a36Sopenharmony_ci}; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic const struct kszphy_type ksz9021_type = { 38562306a36Sopenharmony_ci .interrupt_level_mask = BIT(14), 38662306a36Sopenharmony_ci}; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic const struct kszphy_type ksz9131_type = { 38962306a36Sopenharmony_ci .interrupt_level_mask = BIT(14), 39062306a36Sopenharmony_ci .disable_dll_tx_bit = BIT(12), 39162306a36Sopenharmony_ci .disable_dll_rx_bit = BIT(12), 39262306a36Sopenharmony_ci .disable_dll_mask = BIT_MASK(12), 39362306a36Sopenharmony_ci}; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic const struct kszphy_type lan8841_type = { 39662306a36Sopenharmony_ci .disable_dll_tx_bit = BIT(14), 39762306a36Sopenharmony_ci .disable_dll_rx_bit = BIT(14), 39862306a36Sopenharmony_ci .disable_dll_mask = BIT_MASK(14), 39962306a36Sopenharmony_ci .cable_diag_reg = LAN8814_CABLE_DIAG, 40062306a36Sopenharmony_ci .pair_mask = LAN8814_WIRE_PAIR_MASK, 40162306a36Sopenharmony_ci}; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int kszphy_extended_write(struct phy_device *phydev, 40462306a36Sopenharmony_ci u32 regnum, u16 val) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum); 40762306a36Sopenharmony_ci return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int kszphy_extended_read(struct phy_device *phydev, 41162306a36Sopenharmony_ci u32 regnum) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci phy_write(phydev, MII_KSZPHY_EXTREG, regnum); 41462306a36Sopenharmony_ci return phy_read(phydev, MII_KSZPHY_EXTREG_READ); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int kszphy_ack_interrupt(struct phy_device *phydev) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci /* bit[7..0] int status, which is a read and clear register. */ 42062306a36Sopenharmony_ci int rc; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci rc = phy_read(phydev, MII_KSZPHY_INTCS); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return (rc < 0) ? rc : 0; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int kszphy_config_intr(struct phy_device *phydev) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 43062306a36Sopenharmony_ci int temp, err; 43162306a36Sopenharmony_ci u16 mask; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (type && type->interrupt_level_mask) 43462306a36Sopenharmony_ci mask = type->interrupt_level_mask; 43562306a36Sopenharmony_ci else 43662306a36Sopenharmony_ci mask = KSZPHY_CTRL_INT_ACTIVE_HIGH; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* set the interrupt pin active low */ 43962306a36Sopenharmony_ci temp = phy_read(phydev, MII_KSZPHY_CTRL); 44062306a36Sopenharmony_ci if (temp < 0) 44162306a36Sopenharmony_ci return temp; 44262306a36Sopenharmony_ci temp &= ~mask; 44362306a36Sopenharmony_ci phy_write(phydev, MII_KSZPHY_CTRL, temp); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* enable / disable interrupts */ 44662306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 44762306a36Sopenharmony_ci err = kszphy_ack_interrupt(phydev); 44862306a36Sopenharmony_ci if (err) 44962306a36Sopenharmony_ci return err; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci err = phy_write(phydev, MII_KSZPHY_INTCS, KSZPHY_INTCS_ALL); 45262306a36Sopenharmony_ci } else { 45362306a36Sopenharmony_ci err = phy_write(phydev, MII_KSZPHY_INTCS, 0); 45462306a36Sopenharmony_ci if (err) 45562306a36Sopenharmony_ci return err; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci err = kszphy_ack_interrupt(phydev); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return err; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic irqreturn_t kszphy_handle_interrupt(struct phy_device *phydev) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci int irq_status; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci irq_status = phy_read(phydev, MII_KSZPHY_INTCS); 46862306a36Sopenharmony_ci if (irq_status < 0) { 46962306a36Sopenharmony_ci phy_error(phydev); 47062306a36Sopenharmony_ci return IRQ_NONE; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!(irq_status & KSZPHY_INTCS_STATUS)) 47462306a36Sopenharmony_ci return IRQ_NONE; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci phy_trigger_machine(phydev); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return IRQ_HANDLED; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci int ctrl; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci ctrl = phy_read(phydev, MII_KSZPHY_CTRL); 48662306a36Sopenharmony_ci if (ctrl < 0) 48762306a36Sopenharmony_ci return ctrl; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (val) 49062306a36Sopenharmony_ci ctrl |= KSZPHY_RMII_REF_CLK_SEL; 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci ctrl &= ~KSZPHY_RMII_REF_CLK_SEL; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return phy_write(phydev, MII_KSZPHY_CTRL, ctrl); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci int rc, temp, shift; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci switch (reg) { 50262306a36Sopenharmony_ci case MII_KSZPHY_CTRL_1: 50362306a36Sopenharmony_ci shift = 14; 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case MII_KSZPHY_CTRL_2: 50662306a36Sopenharmony_ci shift = 4; 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci default: 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci temp = phy_read(phydev, reg); 51362306a36Sopenharmony_ci if (temp < 0) { 51462306a36Sopenharmony_ci rc = temp; 51562306a36Sopenharmony_ci goto out; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci temp &= ~(3 << shift); 51962306a36Sopenharmony_ci temp |= val << shift; 52062306a36Sopenharmony_ci rc = phy_write(phydev, reg, temp); 52162306a36Sopenharmony_ciout: 52262306a36Sopenharmony_ci if (rc < 0) 52362306a36Sopenharmony_ci phydev_err(phydev, "failed to set led mode\n"); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return rc; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/* Disable PHY address 0 as the broadcast address, so that it can be used as a 52962306a36Sopenharmony_ci * unique (non-broadcast) address on a shared bus. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic int kszphy_broadcast_disable(struct phy_device *phydev) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci int ret; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ret = phy_read(phydev, MII_KSZPHY_OMSO); 53662306a36Sopenharmony_ci if (ret < 0) 53762306a36Sopenharmony_ci goto out; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF); 54062306a36Sopenharmony_ciout: 54162306a36Sopenharmony_ci if (ret) 54262306a36Sopenharmony_ci phydev_err(phydev, "failed to disable broadcast address\n"); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return ret; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int kszphy_nand_tree_disable(struct phy_device *phydev) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int ret; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci ret = phy_read(phydev, MII_KSZPHY_OMSO); 55262306a36Sopenharmony_ci if (ret < 0) 55362306a36Sopenharmony_ci goto out; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (!(ret & KSZPHY_OMSO_NAND_TREE_ON)) 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci ret = phy_write(phydev, MII_KSZPHY_OMSO, 55962306a36Sopenharmony_ci ret & ~KSZPHY_OMSO_NAND_TREE_ON); 56062306a36Sopenharmony_ciout: 56162306a36Sopenharmony_ci if (ret) 56262306a36Sopenharmony_ci phydev_err(phydev, "failed to disable NAND tree mode\n"); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/* Some config bits need to be set again on resume, handle them here. */ 56862306a36Sopenharmony_cistatic int kszphy_config_reset(struct phy_device *phydev) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 57162306a36Sopenharmony_ci int ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (priv->rmii_ref_clk_sel) { 57462306a36Sopenharmony_ci ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val); 57562306a36Sopenharmony_ci if (ret) { 57662306a36Sopenharmony_ci phydev_err(phydev, 57762306a36Sopenharmony_ci "failed to set rmii reference clock\n"); 57862306a36Sopenharmony_ci return ret; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (priv->type && priv->led_mode >= 0) 58362306a36Sopenharmony_ci kszphy_setup_led(phydev, priv->type->led_mode_reg, priv->led_mode); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int kszphy_config_init(struct phy_device *phydev) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 59162306a36Sopenharmony_ci const struct kszphy_type *type; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!priv) 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci type = priv->type; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (type && type->has_broadcast_disable) 59962306a36Sopenharmony_ci kszphy_broadcast_disable(phydev); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (type && type->has_nand_tree_disable) 60262306a36Sopenharmony_ci kszphy_nand_tree_disable(phydev); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return kszphy_config_reset(phydev); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int ksz8041_fiber_mode(struct phy_device *phydev) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct device_node *of_node = phydev->mdio.dev.of_node; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return of_property_read_bool(of_node, "micrel,fiber-mode"); 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int ksz8041_config_init(struct phy_device *phydev) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* Limit supported and advertised modes in fiber mode */ 61962306a36Sopenharmony_ci if (ksz8041_fiber_mode(phydev)) { 62062306a36Sopenharmony_ci phydev->dev_flags |= MICREL_PHY_FXEN; 62162306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, mask); 62262306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, mask); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci linkmode_and(phydev->supported, phydev->supported, mask); 62562306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 62662306a36Sopenharmony_ci phydev->supported); 62762306a36Sopenharmony_ci linkmode_and(phydev->advertising, phydev->advertising, mask); 62862306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, 62962306a36Sopenharmony_ci phydev->advertising); 63062306a36Sopenharmony_ci phydev->autoneg = AUTONEG_DISABLE; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return kszphy_config_init(phydev); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int ksz8041_config_aneg(struct phy_device *phydev) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci /* Skip auto-negotiation in fiber mode */ 63962306a36Sopenharmony_ci if (phydev->dev_flags & MICREL_PHY_FXEN) { 64062306a36Sopenharmony_ci phydev->speed = SPEED_100; 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return genphy_config_aneg(phydev); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int ksz8051_ksz8795_match_phy_device(struct phy_device *phydev, 64862306a36Sopenharmony_ci const bool ksz_8051) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci int ret; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (!phy_id_compare(phydev->phy_id, PHY_ID_KSZ8051, MICREL_PHY_ID_MASK)) 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci ret = phy_read(phydev, MII_BMSR); 65662306a36Sopenharmony_ci if (ret < 0) 65762306a36Sopenharmony_ci return ret; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* KSZ8051 PHY and KSZ8794/KSZ8795/KSZ8765 switch share the same 66062306a36Sopenharmony_ci * exact PHY ID. However, they can be told apart by the extended 66162306a36Sopenharmony_ci * capability registers presence. The KSZ8051 PHY has them while 66262306a36Sopenharmony_ci * the switch does not. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci ret &= BMSR_ERCAP; 66562306a36Sopenharmony_ci if (ksz_8051) 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci else 66862306a36Sopenharmony_ci return !ret; 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic int ksz8051_match_phy_device(struct phy_device *phydev) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci return ksz8051_ksz8795_match_phy_device(phydev, true); 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic int ksz8081_config_init(struct phy_device *phydev) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci /* KSZPHY_OMSO_FACTORY_TEST is set at de-assertion of the reset line 67962306a36Sopenharmony_ci * based on the RXER (KSZ8081RNA/RND) or TXC (KSZ8081MNX/RNB) pin. If a 68062306a36Sopenharmony_ci * pull-down is missing, the factory test mode should be cleared by 68162306a36Sopenharmony_ci * manually writing a 0. 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_ci phy_clear_bits(phydev, MII_KSZPHY_OMSO, KSZPHY_OMSO_FACTORY_TEST); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return kszphy_config_init(phydev); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int ksz8081_config_mdix(struct phy_device *phydev, u8 ctrl) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci u16 val; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci switch (ctrl) { 69362306a36Sopenharmony_ci case ETH_TP_MDI: 69462306a36Sopenharmony_ci val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX; 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci case ETH_TP_MDI_X: 69762306a36Sopenharmony_ci val = KSZ8081_CTRL2_DISABLE_AUTO_MDIX | 69862306a36Sopenharmony_ci KSZ8081_CTRL2_MDI_MDI_X_SELECT; 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci case ETH_TP_MDI_AUTO: 70162306a36Sopenharmony_ci val = 0; 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci default: 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return phy_modify(phydev, MII_KSZPHY_CTRL_2, 70862306a36Sopenharmony_ci KSZ8081_CTRL2_HP_MDIX | 70962306a36Sopenharmony_ci KSZ8081_CTRL2_MDI_MDI_X_SELECT | 71062306a36Sopenharmony_ci KSZ8081_CTRL2_DISABLE_AUTO_MDIX, 71162306a36Sopenharmony_ci KSZ8081_CTRL2_HP_MDIX | val); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic int ksz8081_config_aneg(struct phy_device *phydev) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci int ret; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ret = genphy_config_aneg(phydev); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* The MDI-X configuration is automatically changed by the PHY after 72362306a36Sopenharmony_ci * switching from autoneg off to on. So, take MDI-X configuration under 72462306a36Sopenharmony_ci * own control and set it after autoneg configuration was done. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci return ksz8081_config_mdix(phydev, phydev->mdix_ctrl); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic int ksz8081_mdix_update(struct phy_device *phydev) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci int ret; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci ret = phy_read(phydev, MII_KSZPHY_CTRL_2); 73462306a36Sopenharmony_ci if (ret < 0) 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (ret & KSZ8081_CTRL2_DISABLE_AUTO_MDIX) { 73862306a36Sopenharmony_ci if (ret & KSZ8081_CTRL2_MDI_MDI_X_SELECT) 73962306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI_X; 74062306a36Sopenharmony_ci else 74162306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI; 74262306a36Sopenharmony_ci } else { 74362306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI_AUTO; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci ret = phy_read(phydev, MII_KSZPHY_CTRL_1); 74762306a36Sopenharmony_ci if (ret < 0) 74862306a36Sopenharmony_ci return ret; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (ret & KSZ8081_CTRL1_MDIX_STAT) 75162306a36Sopenharmony_ci phydev->mdix = ETH_TP_MDI; 75262306a36Sopenharmony_ci else 75362306a36Sopenharmony_ci phydev->mdix = ETH_TP_MDI_X; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return 0; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int ksz8081_read_status(struct phy_device *phydev) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci int ret; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci ret = ksz8081_mdix_update(phydev); 76362306a36Sopenharmony_ci if (ret < 0) 76462306a36Sopenharmony_ci return ret; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return genphy_read_status(phydev); 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int ksz8061_config_init(struct phy_device *phydev) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci int ret; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_DEVID1, 0xB61A); 77462306a36Sopenharmony_ci if (ret) 77562306a36Sopenharmony_ci return ret; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return kszphy_config_init(phydev); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int ksz8795_match_phy_device(struct phy_device *phydev) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci return ksz8051_ksz8795_match_phy_device(phydev, false); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic int ksz9021_load_values_from_of(struct phy_device *phydev, 78662306a36Sopenharmony_ci const struct device_node *of_node, 78762306a36Sopenharmony_ci u16 reg, 78862306a36Sopenharmony_ci const char *field1, const char *field2, 78962306a36Sopenharmony_ci const char *field3, const char *field4) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci int val1 = -1; 79262306a36Sopenharmony_ci int val2 = -2; 79362306a36Sopenharmony_ci int val3 = -3; 79462306a36Sopenharmony_ci int val4 = -4; 79562306a36Sopenharmony_ci int newval; 79662306a36Sopenharmony_ci int matches = 0; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (!of_property_read_u32(of_node, field1, &val1)) 79962306a36Sopenharmony_ci matches++; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (!of_property_read_u32(of_node, field2, &val2)) 80262306a36Sopenharmony_ci matches++; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if (!of_property_read_u32(of_node, field3, &val3)) 80562306a36Sopenharmony_ci matches++; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (!of_property_read_u32(of_node, field4, &val4)) 80862306a36Sopenharmony_ci matches++; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (!matches) 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (matches < 4) 81462306a36Sopenharmony_ci newval = kszphy_extended_read(phydev, reg); 81562306a36Sopenharmony_ci else 81662306a36Sopenharmony_ci newval = 0; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (val1 != -1) 81962306a36Sopenharmony_ci newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (val2 != -2) 82262306a36Sopenharmony_ci newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (val3 != -3) 82562306a36Sopenharmony_ci newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (val4 != -4) 82862306a36Sopenharmony_ci newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return kszphy_extended_write(phydev, reg, newval); 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic int ksz9021_config_init(struct phy_device *phydev) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci const struct device_node *of_node; 83662306a36Sopenharmony_ci const struct device *dev_walker; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* The Micrel driver has a deprecated option to place phy OF 83962306a36Sopenharmony_ci * properties in the MAC node. Walk up the tree of devices to 84062306a36Sopenharmony_ci * find a device with an OF node. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci dev_walker = &phydev->mdio.dev; 84362306a36Sopenharmony_ci do { 84462306a36Sopenharmony_ci of_node = dev_walker->of_node; 84562306a36Sopenharmony_ci dev_walker = dev_walker->parent; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci } while (!of_node && dev_walker); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (of_node) { 85062306a36Sopenharmony_ci ksz9021_load_values_from_of(phydev, of_node, 85162306a36Sopenharmony_ci MII_KSZPHY_CLK_CONTROL_PAD_SKEW, 85262306a36Sopenharmony_ci "txen-skew-ps", "txc-skew-ps", 85362306a36Sopenharmony_ci "rxdv-skew-ps", "rxc-skew-ps"); 85462306a36Sopenharmony_ci ksz9021_load_values_from_of(phydev, of_node, 85562306a36Sopenharmony_ci MII_KSZPHY_RX_DATA_PAD_SKEW, 85662306a36Sopenharmony_ci "rxd0-skew-ps", "rxd1-skew-ps", 85762306a36Sopenharmony_ci "rxd2-skew-ps", "rxd3-skew-ps"); 85862306a36Sopenharmony_ci ksz9021_load_values_from_of(phydev, of_node, 85962306a36Sopenharmony_ci MII_KSZPHY_TX_DATA_PAD_SKEW, 86062306a36Sopenharmony_ci "txd0-skew-ps", "txd1-skew-ps", 86162306a36Sopenharmony_ci "txd2-skew-ps", "txd3-skew-ps"); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci#define KSZ9031_PS_TO_REG 60 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci/* Extended registers */ 86962306a36Sopenharmony_ci/* MMD Address 0x0 */ 87062306a36Sopenharmony_ci#define MII_KSZ9031RN_FLP_BURST_TX_LO 3 87162306a36Sopenharmony_ci#define MII_KSZ9031RN_FLP_BURST_TX_HI 4 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/* MMD Address 0x2 */ 87462306a36Sopenharmony_ci#define MII_KSZ9031RN_CONTROL_PAD_SKEW 4 87562306a36Sopenharmony_ci#define MII_KSZ9031RN_RX_CTL_M GENMASK(7, 4) 87662306a36Sopenharmony_ci#define MII_KSZ9031RN_TX_CTL_M GENMASK(3, 0) 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci#define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5 87962306a36Sopenharmony_ci#define MII_KSZ9031RN_RXD3 GENMASK(15, 12) 88062306a36Sopenharmony_ci#define MII_KSZ9031RN_RXD2 GENMASK(11, 8) 88162306a36Sopenharmony_ci#define MII_KSZ9031RN_RXD1 GENMASK(7, 4) 88262306a36Sopenharmony_ci#define MII_KSZ9031RN_RXD0 GENMASK(3, 0) 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci#define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6 88562306a36Sopenharmony_ci#define MII_KSZ9031RN_TXD3 GENMASK(15, 12) 88662306a36Sopenharmony_ci#define MII_KSZ9031RN_TXD2 GENMASK(11, 8) 88762306a36Sopenharmony_ci#define MII_KSZ9031RN_TXD1 GENMASK(7, 4) 88862306a36Sopenharmony_ci#define MII_KSZ9031RN_TXD0 GENMASK(3, 0) 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci#define MII_KSZ9031RN_CLK_PAD_SKEW 8 89162306a36Sopenharmony_ci#define MII_KSZ9031RN_GTX_CLK GENMASK(9, 5) 89262306a36Sopenharmony_ci#define MII_KSZ9031RN_RX_CLK GENMASK(4, 0) 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To 89562306a36Sopenharmony_ci * provide different RGMII options we need to configure delay offset 89662306a36Sopenharmony_ci * for each pad relative to build in delay. 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci/* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of 89962306a36Sopenharmony_ci * 1.80ns 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_ci#define RX_ID 0x7 90262306a36Sopenharmony_ci#define RX_CLK_ID 0x19 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci/* set rx to +0.30ns and rx_clk to -0.90ns to compensate the 90562306a36Sopenharmony_ci * internal 1.2ns delay. 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_ci#define RX_ND 0xc 90862306a36Sopenharmony_ci#define RX_CLK_ND 0x0 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci/* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */ 91162306a36Sopenharmony_ci#define TX_ID 0x0 91262306a36Sopenharmony_ci#define TX_CLK_ID 0x1f 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci/* set tx and tx_clk to "No delay adjustment" to keep 0ns 91562306a36Sopenharmony_ci * dealy 91662306a36Sopenharmony_ci */ 91762306a36Sopenharmony_ci#define TX_ND 0x7 91862306a36Sopenharmony_ci#define TX_CLK_ND 0xf 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci/* MMD Address 0x1C */ 92162306a36Sopenharmony_ci#define MII_KSZ9031RN_EDPD 0x23 92262306a36Sopenharmony_ci#define MII_KSZ9031RN_EDPD_ENABLE BIT(0) 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int ksz9031_of_load_skew_values(struct phy_device *phydev, 92562306a36Sopenharmony_ci const struct device_node *of_node, 92662306a36Sopenharmony_ci u16 reg, size_t field_sz, 92762306a36Sopenharmony_ci const char *field[], u8 numfields, 92862306a36Sopenharmony_ci bool *update) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci int val[4] = {-1, -2, -3, -4}; 93162306a36Sopenharmony_ci int matches = 0; 93262306a36Sopenharmony_ci u16 mask; 93362306a36Sopenharmony_ci u16 maxval; 93462306a36Sopenharmony_ci u16 newval; 93562306a36Sopenharmony_ci int i; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci for (i = 0; i < numfields; i++) 93862306a36Sopenharmony_ci if (!of_property_read_u32(of_node, field[i], val + i)) 93962306a36Sopenharmony_ci matches++; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (!matches) 94262306a36Sopenharmony_ci return 0; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci *update |= true; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (matches < numfields) 94762306a36Sopenharmony_ci newval = phy_read_mmd(phydev, 2, reg); 94862306a36Sopenharmony_ci else 94962306a36Sopenharmony_ci newval = 0; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci maxval = (field_sz == 4) ? 0xf : 0x1f; 95262306a36Sopenharmony_ci for (i = 0; i < numfields; i++) 95362306a36Sopenharmony_ci if (val[i] != -(i + 1)) { 95462306a36Sopenharmony_ci mask = 0xffff; 95562306a36Sopenharmony_ci mask ^= maxval << (field_sz * i); 95662306a36Sopenharmony_ci newval = (newval & mask) | 95762306a36Sopenharmony_ci (((val[i] / KSZ9031_PS_TO_REG) & maxval) 95862306a36Sopenharmony_ci << (field_sz * i)); 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci return phy_write_mmd(phydev, 2, reg, newval); 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/* Center KSZ9031RNX FLP timing at 16ms. */ 96562306a36Sopenharmony_cistatic int ksz9031_center_flp_timing(struct phy_device *phydev) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci int result; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI, 97062306a36Sopenharmony_ci 0x0006); 97162306a36Sopenharmony_ci if (result) 97262306a36Sopenharmony_ci return result; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci result = phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO, 97562306a36Sopenharmony_ci 0x1A80); 97662306a36Sopenharmony_ci if (result) 97762306a36Sopenharmony_ci return result; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return genphy_restart_aneg(phydev); 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci/* Enable energy-detect power-down mode */ 98362306a36Sopenharmony_cistatic int ksz9031_enable_edpd(struct phy_device *phydev) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci int reg; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci reg = phy_read_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD); 98862306a36Sopenharmony_ci if (reg < 0) 98962306a36Sopenharmony_ci return reg; 99062306a36Sopenharmony_ci return phy_write_mmd(phydev, 0x1C, MII_KSZ9031RN_EDPD, 99162306a36Sopenharmony_ci reg | MII_KSZ9031RN_EDPD_ENABLE); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int ksz9031_config_rgmii_delay(struct phy_device *phydev) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci u16 rx, tx, rx_clk, tx_clk; 99762306a36Sopenharmony_ci int ret; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci switch (phydev->interface) { 100062306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 100162306a36Sopenharmony_ci tx = TX_ND; 100262306a36Sopenharmony_ci tx_clk = TX_CLK_ND; 100362306a36Sopenharmony_ci rx = RX_ND; 100462306a36Sopenharmony_ci rx_clk = RX_CLK_ND; 100562306a36Sopenharmony_ci break; 100662306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 100762306a36Sopenharmony_ci tx = TX_ID; 100862306a36Sopenharmony_ci tx_clk = TX_CLK_ID; 100962306a36Sopenharmony_ci rx = RX_ID; 101062306a36Sopenharmony_ci rx_clk = RX_CLK_ID; 101162306a36Sopenharmony_ci break; 101262306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 101362306a36Sopenharmony_ci tx = TX_ND; 101462306a36Sopenharmony_ci tx_clk = TX_CLK_ND; 101562306a36Sopenharmony_ci rx = RX_ID; 101662306a36Sopenharmony_ci rx_clk = RX_CLK_ID; 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 101962306a36Sopenharmony_ci tx = TX_ID; 102062306a36Sopenharmony_ci tx_clk = TX_CLK_ID; 102162306a36Sopenharmony_ci rx = RX_ND; 102262306a36Sopenharmony_ci rx_clk = RX_CLK_ND; 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci default: 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_CONTROL_PAD_SKEW, 102962306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) | 103062306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx)); 103162306a36Sopenharmony_ci if (ret < 0) 103262306a36Sopenharmony_ci return ret; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_RX_DATA_PAD_SKEW, 103562306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD3, rx) | 103662306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD2, rx) | 103762306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD1, rx) | 103862306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RXD0, rx)); 103962306a36Sopenharmony_ci if (ret < 0) 104062306a36Sopenharmony_ci return ret; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, MII_KSZ9031RN_TX_DATA_PAD_SKEW, 104362306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD3, tx) | 104462306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD2, tx) | 104562306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD1, tx) | 104662306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_TXD0, tx)); 104762306a36Sopenharmony_ci if (ret < 0) 104862306a36Sopenharmony_ci return ret; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return phy_write_mmd(phydev, 2, MII_KSZ9031RN_CLK_PAD_SKEW, 105162306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) | 105262306a36Sopenharmony_ci FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk)); 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic int ksz9031_config_init(struct phy_device *phydev) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci const struct device_node *of_node; 105862306a36Sopenharmony_ci static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; 105962306a36Sopenharmony_ci static const char *rx_data_skews[4] = { 106062306a36Sopenharmony_ci "rxd0-skew-ps", "rxd1-skew-ps", 106162306a36Sopenharmony_ci "rxd2-skew-ps", "rxd3-skew-ps" 106262306a36Sopenharmony_ci }; 106362306a36Sopenharmony_ci static const char *tx_data_skews[4] = { 106462306a36Sopenharmony_ci "txd0-skew-ps", "txd1-skew-ps", 106562306a36Sopenharmony_ci "txd2-skew-ps", "txd3-skew-ps" 106662306a36Sopenharmony_ci }; 106762306a36Sopenharmony_ci static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; 106862306a36Sopenharmony_ci const struct device *dev_walker; 106962306a36Sopenharmony_ci int result; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci result = ksz9031_enable_edpd(phydev); 107262306a36Sopenharmony_ci if (result < 0) 107362306a36Sopenharmony_ci return result; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* The Micrel driver has a deprecated option to place phy OF 107662306a36Sopenharmony_ci * properties in the MAC node. Walk up the tree of devices to 107762306a36Sopenharmony_ci * find a device with an OF node. 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_ci dev_walker = &phydev->mdio.dev; 108062306a36Sopenharmony_ci do { 108162306a36Sopenharmony_ci of_node = dev_walker->of_node; 108262306a36Sopenharmony_ci dev_walker = dev_walker->parent; 108362306a36Sopenharmony_ci } while (!of_node && dev_walker); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (of_node) { 108662306a36Sopenharmony_ci bool update = false; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (phy_interface_is_rgmii(phydev)) { 108962306a36Sopenharmony_ci result = ksz9031_config_rgmii_delay(phydev); 109062306a36Sopenharmony_ci if (result < 0) 109162306a36Sopenharmony_ci return result; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 109562306a36Sopenharmony_ci MII_KSZ9031RN_CLK_PAD_SKEW, 5, 109662306a36Sopenharmony_ci clk_skews, 2, &update); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 109962306a36Sopenharmony_ci MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, 110062306a36Sopenharmony_ci control_skews, 2, &update); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 110362306a36Sopenharmony_ci MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, 110462306a36Sopenharmony_ci rx_data_skews, 4, &update); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci ksz9031_of_load_skew_values(phydev, of_node, 110762306a36Sopenharmony_ci MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, 110862306a36Sopenharmony_ci tx_data_skews, 4, &update); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci if (update && !phy_interface_is_rgmii(phydev)) 111162306a36Sopenharmony_ci phydev_warn(phydev, 111262306a36Sopenharmony_ci "*-skew-ps values should be used only with RGMII PHY modes\n"); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* Silicon Errata Sheet (DS80000691D or DS80000692D): 111562306a36Sopenharmony_ci * When the device links in the 1000BASE-T slave mode only, 111662306a36Sopenharmony_ci * the optional 125MHz reference output clock (CLK125_NDO) 111762306a36Sopenharmony_ci * has wide duty cycle variation. 111862306a36Sopenharmony_ci * 111962306a36Sopenharmony_ci * The optional CLK125_NDO clock does not meet the RGMII 112062306a36Sopenharmony_ci * 45/55 percent (min/max) duty cycle requirement and therefore 112162306a36Sopenharmony_ci * cannot be used directly by the MAC side for clocking 112262306a36Sopenharmony_ci * applications that have setup/hold time requirements on 112362306a36Sopenharmony_ci * rising and falling clock edges. 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * Workaround: 112662306a36Sopenharmony_ci * Force the phy to be the master to receive a stable clock 112762306a36Sopenharmony_ci * which meets the duty cycle requirement. 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_ci if (of_property_read_bool(of_node, "micrel,force-master")) { 113062306a36Sopenharmony_ci result = phy_read(phydev, MII_CTRL1000); 113162306a36Sopenharmony_ci if (result < 0) 113262306a36Sopenharmony_ci goto err_force_master; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* enable master mode, config & prefer master */ 113562306a36Sopenharmony_ci result |= CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER; 113662306a36Sopenharmony_ci result = phy_write(phydev, MII_CTRL1000, result); 113762306a36Sopenharmony_ci if (result < 0) 113862306a36Sopenharmony_ci goto err_force_master; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci return ksz9031_center_flp_timing(phydev); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cierr_force_master: 114562306a36Sopenharmony_ci phydev_err(phydev, "failed to force the phy to master mode\n"); 114662306a36Sopenharmony_ci return result; 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci#define KSZ9131_SKEW_5BIT_MAX 2400 115062306a36Sopenharmony_ci#define KSZ9131_SKEW_4BIT_MAX 800 115162306a36Sopenharmony_ci#define KSZ9131_OFFSET 700 115262306a36Sopenharmony_ci#define KSZ9131_STEP 100 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic int ksz9131_of_load_skew_values(struct phy_device *phydev, 115562306a36Sopenharmony_ci struct device_node *of_node, 115662306a36Sopenharmony_ci u16 reg, size_t field_sz, 115762306a36Sopenharmony_ci char *field[], u8 numfields) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET), 116062306a36Sopenharmony_ci -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)}; 116162306a36Sopenharmony_ci int skewval, skewmax = 0; 116262306a36Sopenharmony_ci int matches = 0; 116362306a36Sopenharmony_ci u16 maxval; 116462306a36Sopenharmony_ci u16 newval; 116562306a36Sopenharmony_ci u16 mask; 116662306a36Sopenharmony_ci int i; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci /* psec properties in dts should mean x pico seconds */ 116962306a36Sopenharmony_ci if (field_sz == 5) 117062306a36Sopenharmony_ci skewmax = KSZ9131_SKEW_5BIT_MAX; 117162306a36Sopenharmony_ci else 117262306a36Sopenharmony_ci skewmax = KSZ9131_SKEW_4BIT_MAX; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci for (i = 0; i < numfields; i++) 117562306a36Sopenharmony_ci if (!of_property_read_s32(of_node, field[i], &skewval)) { 117662306a36Sopenharmony_ci if (skewval < -KSZ9131_OFFSET) 117762306a36Sopenharmony_ci skewval = -KSZ9131_OFFSET; 117862306a36Sopenharmony_ci else if (skewval > skewmax) 117962306a36Sopenharmony_ci skewval = skewmax; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci val[i] = skewval + KSZ9131_OFFSET; 118262306a36Sopenharmony_ci matches++; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if (!matches) 118662306a36Sopenharmony_ci return 0; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (matches < numfields) 118962306a36Sopenharmony_ci newval = phy_read_mmd(phydev, 2, reg); 119062306a36Sopenharmony_ci else 119162306a36Sopenharmony_ci newval = 0; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci maxval = (field_sz == 4) ? 0xf : 0x1f; 119462306a36Sopenharmony_ci for (i = 0; i < numfields; i++) 119562306a36Sopenharmony_ci if (val[i] != -(i + 1 + KSZ9131_OFFSET)) { 119662306a36Sopenharmony_ci mask = 0xffff; 119762306a36Sopenharmony_ci mask ^= maxval << (field_sz * i); 119862306a36Sopenharmony_ci newval = (newval & mask) | 119962306a36Sopenharmony_ci (((val[i] / KSZ9131_STEP) & maxval) 120062306a36Sopenharmony_ci << (field_sz * i)); 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci return phy_write_mmd(phydev, 2, reg, newval); 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci#define KSZ9131RN_MMD_COMMON_CTRL_REG 2 120762306a36Sopenharmony_ci#define KSZ9131RN_RXC_DLL_CTRL 76 120862306a36Sopenharmony_ci#define KSZ9131RN_TXC_DLL_CTRL 77 120962306a36Sopenharmony_ci#define KSZ9131RN_DLL_ENABLE_DELAY 0 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic int ksz9131_config_rgmii_delay(struct phy_device *phydev) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 121462306a36Sopenharmony_ci u16 rxcdll_val, txcdll_val; 121562306a36Sopenharmony_ci int ret; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci switch (phydev->interface) { 121862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 121962306a36Sopenharmony_ci rxcdll_val = type->disable_dll_rx_bit; 122062306a36Sopenharmony_ci txcdll_val = type->disable_dll_tx_bit; 122162306a36Sopenharmony_ci break; 122262306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 122362306a36Sopenharmony_ci rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 122462306a36Sopenharmony_ci txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 122762306a36Sopenharmony_ci rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 122862306a36Sopenharmony_ci txcdll_val = type->disable_dll_tx_bit; 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 123162306a36Sopenharmony_ci rxcdll_val = type->disable_dll_rx_bit; 123262306a36Sopenharmony_ci txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; 123362306a36Sopenharmony_ci break; 123462306a36Sopenharmony_ci default: 123562306a36Sopenharmony_ci return 0; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 123962306a36Sopenharmony_ci KSZ9131RN_RXC_DLL_CTRL, type->disable_dll_mask, 124062306a36Sopenharmony_ci rxcdll_val); 124162306a36Sopenharmony_ci if (ret < 0) 124262306a36Sopenharmony_ci return ret; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 124562306a36Sopenharmony_ci KSZ9131RN_TXC_DLL_CTRL, type->disable_dll_mask, 124662306a36Sopenharmony_ci txcdll_val); 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci/* Silicon Errata DS80000693B 125062306a36Sopenharmony_ci * 125162306a36Sopenharmony_ci * When LEDs are configured in Individual Mode, LED1 is ON in a no-link 125262306a36Sopenharmony_ci * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves 125362306a36Sopenharmony_ci * according to the datasheet (off if there is no link). 125462306a36Sopenharmony_ci */ 125562306a36Sopenharmony_cistatic int ksz9131_led_errata(struct phy_device *phydev) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci int reg; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci reg = phy_read_mmd(phydev, 2, 0); 126062306a36Sopenharmony_ci if (reg < 0) 126162306a36Sopenharmony_ci return reg; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (!(reg & BIT(4))) 126462306a36Sopenharmony_ci return 0; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci return phy_set_bits(phydev, 0x1e, BIT(9)); 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic int ksz9131_config_init(struct phy_device *phydev) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci struct device_node *of_node; 127262306a36Sopenharmony_ci char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"}; 127362306a36Sopenharmony_ci char *rx_data_skews[4] = { 127462306a36Sopenharmony_ci "rxd0-skew-psec", "rxd1-skew-psec", 127562306a36Sopenharmony_ci "rxd2-skew-psec", "rxd3-skew-psec" 127662306a36Sopenharmony_ci }; 127762306a36Sopenharmony_ci char *tx_data_skews[4] = { 127862306a36Sopenharmony_ci "txd0-skew-psec", "txd1-skew-psec", 127962306a36Sopenharmony_ci "txd2-skew-psec", "txd3-skew-psec" 128062306a36Sopenharmony_ci }; 128162306a36Sopenharmony_ci char *control_skews[2] = {"txen-skew-psec", "rxdv-skew-psec"}; 128262306a36Sopenharmony_ci const struct device *dev_walker; 128362306a36Sopenharmony_ci int ret; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci dev_walker = &phydev->mdio.dev; 128662306a36Sopenharmony_ci do { 128762306a36Sopenharmony_ci of_node = dev_walker->of_node; 128862306a36Sopenharmony_ci dev_walker = dev_walker->parent; 128962306a36Sopenharmony_ci } while (!of_node && dev_walker); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (!of_node) 129262306a36Sopenharmony_ci return 0; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (phy_interface_is_rgmii(phydev)) { 129562306a36Sopenharmony_ci ret = ksz9131_config_rgmii_delay(phydev); 129662306a36Sopenharmony_ci if (ret < 0) 129762306a36Sopenharmony_ci return ret; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 130162306a36Sopenharmony_ci MII_KSZ9031RN_CLK_PAD_SKEW, 5, 130262306a36Sopenharmony_ci clk_skews, 2); 130362306a36Sopenharmony_ci if (ret < 0) 130462306a36Sopenharmony_ci return ret; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 130762306a36Sopenharmony_ci MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, 130862306a36Sopenharmony_ci control_skews, 2); 130962306a36Sopenharmony_ci if (ret < 0) 131062306a36Sopenharmony_ci return ret; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 131362306a36Sopenharmony_ci MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, 131462306a36Sopenharmony_ci rx_data_skews, 4); 131562306a36Sopenharmony_ci if (ret < 0) 131662306a36Sopenharmony_ci return ret; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci ret = ksz9131_of_load_skew_values(phydev, of_node, 131962306a36Sopenharmony_ci MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, 132062306a36Sopenharmony_ci tx_data_skews, 4); 132162306a36Sopenharmony_ci if (ret < 0) 132262306a36Sopenharmony_ci return ret; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci ret = ksz9131_led_errata(phydev); 132562306a36Sopenharmony_ci if (ret < 0) 132662306a36Sopenharmony_ci return ret; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci return 0; 132962306a36Sopenharmony_ci} 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci#define MII_KSZ9131_AUTO_MDIX 0x1C 133262306a36Sopenharmony_ci#define MII_KSZ9131_AUTO_MDI_SET BIT(7) 133362306a36Sopenharmony_ci#define MII_KSZ9131_AUTO_MDIX_SWAP_OFF BIT(6) 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_cistatic int ksz9131_mdix_update(struct phy_device *phydev) 133662306a36Sopenharmony_ci{ 133762306a36Sopenharmony_ci int ret; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci ret = phy_read(phydev, MII_KSZ9131_AUTO_MDIX); 134062306a36Sopenharmony_ci if (ret < 0) 134162306a36Sopenharmony_ci return ret; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (ret & MII_KSZ9131_AUTO_MDIX_SWAP_OFF) { 134462306a36Sopenharmony_ci if (ret & MII_KSZ9131_AUTO_MDI_SET) 134562306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI; 134662306a36Sopenharmony_ci else 134762306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI_X; 134862306a36Sopenharmony_ci } else { 134962306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI_AUTO; 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (ret & MII_KSZ9131_AUTO_MDI_SET) 135362306a36Sopenharmony_ci phydev->mdix = ETH_TP_MDI; 135462306a36Sopenharmony_ci else 135562306a36Sopenharmony_ci phydev->mdix = ETH_TP_MDI_X; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return 0; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_cistatic int ksz9131_config_mdix(struct phy_device *phydev, u8 ctrl) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci u16 val; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci switch (ctrl) { 136562306a36Sopenharmony_ci case ETH_TP_MDI: 136662306a36Sopenharmony_ci val = MII_KSZ9131_AUTO_MDIX_SWAP_OFF | 136762306a36Sopenharmony_ci MII_KSZ9131_AUTO_MDI_SET; 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci case ETH_TP_MDI_X: 137062306a36Sopenharmony_ci val = MII_KSZ9131_AUTO_MDIX_SWAP_OFF; 137162306a36Sopenharmony_ci break; 137262306a36Sopenharmony_ci case ETH_TP_MDI_AUTO: 137362306a36Sopenharmony_ci val = 0; 137462306a36Sopenharmony_ci break; 137562306a36Sopenharmony_ci default: 137662306a36Sopenharmony_ci return 0; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci return phy_modify(phydev, MII_KSZ9131_AUTO_MDIX, 138062306a36Sopenharmony_ci MII_KSZ9131_AUTO_MDIX_SWAP_OFF | 138162306a36Sopenharmony_ci MII_KSZ9131_AUTO_MDI_SET, val); 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic int ksz9131_read_status(struct phy_device *phydev) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci int ret; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci ret = ksz9131_mdix_update(phydev); 138962306a36Sopenharmony_ci if (ret < 0) 139062306a36Sopenharmony_ci return ret; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci return genphy_read_status(phydev); 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic int ksz9131_config_aneg(struct phy_device *phydev) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci int ret; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci ret = ksz9131_config_mdix(phydev, phydev->mdix_ctrl); 140062306a36Sopenharmony_ci if (ret) 140162306a36Sopenharmony_ci return ret; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci return genphy_config_aneg(phydev); 140462306a36Sopenharmony_ci} 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistatic int ksz9477_get_features(struct phy_device *phydev) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci int ret; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci ret = genphy_read_abilities(phydev); 141162306a36Sopenharmony_ci if (ret) 141262306a36Sopenharmony_ci return ret; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* The "EEE control and capability 1" (Register 3.20) seems to be 141562306a36Sopenharmony_ci * influenced by the "EEE advertisement 1" (Register 7.60). Changes 141662306a36Sopenharmony_ci * on the 7.60 will affect 3.20. So, we need to construct our own list 141762306a36Sopenharmony_ci * of caps. 141862306a36Sopenharmony_ci * KSZ8563R should have 100BaseTX/Full only. 141962306a36Sopenharmony_ci */ 142062306a36Sopenharmony_ci linkmode_and(phydev->supported_eee, phydev->supported, 142162306a36Sopenharmony_ci PHY_EEE_CAP1_FEATURES); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci return 0; 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 142762306a36Sopenharmony_ci#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) 142862306a36Sopenharmony_ci#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) 142962306a36Sopenharmony_cistatic int ksz8873mll_read_status(struct phy_device *phydev) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci int regval; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci /* dummy read */ 143462306a36Sopenharmony_ci regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX) 143962306a36Sopenharmony_ci phydev->duplex = DUPLEX_HALF; 144062306a36Sopenharmony_ci else 144162306a36Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED) 144462306a36Sopenharmony_ci phydev->speed = SPEED_10; 144562306a36Sopenharmony_ci else 144662306a36Sopenharmony_ci phydev->speed = SPEED_100; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci phydev->link = 1; 144962306a36Sopenharmony_ci phydev->pause = phydev->asym_pause = 0; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return 0; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic int ksz9031_get_features(struct phy_device *phydev) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci int ret; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci ret = genphy_read_abilities(phydev); 145962306a36Sopenharmony_ci if (ret < 0) 146062306a36Sopenharmony_ci return ret; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci /* Silicon Errata Sheet (DS80000691D or DS80000692D): 146362306a36Sopenharmony_ci * Whenever the device's Asymmetric Pause capability is set to 1, 146462306a36Sopenharmony_ci * link-up may fail after a link-up to link-down transition. 146562306a36Sopenharmony_ci * 146662306a36Sopenharmony_ci * The Errata Sheet is for ksz9031, but ksz9021 has the same issue 146762306a36Sopenharmony_ci * 146862306a36Sopenharmony_ci * Workaround: 146962306a36Sopenharmony_ci * Do not enable the Asymmetric Pause capability bit. 147062306a36Sopenharmony_ci */ 147162306a36Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci /* We force setting the Pause capability as the core will force the 147462306a36Sopenharmony_ci * Asymmetric Pause capability to 1 otherwise. 147562306a36Sopenharmony_ci */ 147662306a36Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci return 0; 147962306a36Sopenharmony_ci} 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_cistatic int ksz9031_read_status(struct phy_device *phydev) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci int err; 148462306a36Sopenharmony_ci int regval; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci err = genphy_read_status(phydev); 148762306a36Sopenharmony_ci if (err) 148862306a36Sopenharmony_ci return err; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* Make sure the PHY is not broken. Read idle error count, 149162306a36Sopenharmony_ci * and reset the PHY if it is maxed out. 149262306a36Sopenharmony_ci */ 149362306a36Sopenharmony_ci regval = phy_read(phydev, MII_STAT1000); 149462306a36Sopenharmony_ci if ((regval & 0xFF) == 0xFF) { 149562306a36Sopenharmony_ci phy_init_hw(phydev); 149662306a36Sopenharmony_ci phydev->link = 0; 149762306a36Sopenharmony_ci if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev)) 149862306a36Sopenharmony_ci phydev->drv->config_intr(phydev); 149962306a36Sopenharmony_ci return genphy_config_aneg(phydev); 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci return 0; 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_cistatic int ksz9x31_cable_test_start(struct phy_device *phydev) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 150862306a36Sopenharmony_ci int ret; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic 151162306a36Sopenharmony_ci * Prior to running the cable diagnostics, Auto-negotiation should 151262306a36Sopenharmony_ci * be disabled, full duplex set and the link speed set to 1000Mbps 151362306a36Sopenharmony_ci * via the Basic Control Register. 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_ci ret = phy_modify(phydev, MII_BMCR, 151662306a36Sopenharmony_ci BMCR_SPEED1000 | BMCR_FULLDPLX | 151762306a36Sopenharmony_ci BMCR_ANENABLE | BMCR_SPEED100, 151862306a36Sopenharmony_ci BMCR_SPEED1000 | BMCR_FULLDPLX); 151962306a36Sopenharmony_ci if (ret) 152062306a36Sopenharmony_ci return ret; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic 152362306a36Sopenharmony_ci * The Master-Slave configuration should be set to Slave by writing 152462306a36Sopenharmony_ci * a value of 0x1000 to the Auto-Negotiation Master Slave Control 152562306a36Sopenharmony_ci * Register. 152662306a36Sopenharmony_ci */ 152762306a36Sopenharmony_ci ret = phy_read(phydev, MII_CTRL1000); 152862306a36Sopenharmony_ci if (ret < 0) 152962306a36Sopenharmony_ci return ret; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci /* Cache these bits, they need to be restored once LinkMD finishes. */ 153262306a36Sopenharmony_ci priv->vct_ctrl1000 = ret & (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER); 153362306a36Sopenharmony_ci ret &= ~(CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER); 153462306a36Sopenharmony_ci ret |= CTL1000_ENABLE_MASTER; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci return phy_write(phydev, MII_CTRL1000, ret); 153762306a36Sopenharmony_ci} 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cistatic int ksz9x31_cable_test_result_trans(u16 status) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci switch (FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status)) { 154262306a36Sopenharmony_ci case KSZ9x31_LMD_VCT_ST_NORMAL: 154362306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OK; 154462306a36Sopenharmony_ci case KSZ9x31_LMD_VCT_ST_OPEN: 154562306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 154662306a36Sopenharmony_ci case KSZ9x31_LMD_VCT_ST_SHORT: 154762306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 154862306a36Sopenharmony_ci case KSZ9x31_LMD_VCT_ST_FAIL: 154962306a36Sopenharmony_ci fallthrough; 155062306a36Sopenharmony_ci default: 155162306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci} 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_cistatic bool ksz9x31_cable_test_failed(u16 status) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci int stat = FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci return stat == KSZ9x31_LMD_VCT_ST_FAIL; 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_cistatic bool ksz9x31_cable_test_fault_length_valid(u16 status) 156362306a36Sopenharmony_ci{ 156462306a36Sopenharmony_ci switch (FIELD_GET(KSZ9x31_LMD_VCT_ST_MASK, status)) { 156562306a36Sopenharmony_ci case KSZ9x31_LMD_VCT_ST_OPEN: 156662306a36Sopenharmony_ci fallthrough; 156762306a36Sopenharmony_ci case KSZ9x31_LMD_VCT_ST_SHORT: 156862306a36Sopenharmony_ci return true; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci return false; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic int ksz9x31_cable_test_fault_length(struct phy_device *phydev, u16 stat) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci int dt = FIELD_GET(KSZ9x31_LMD_VCT_DATA_MASK, stat); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic 157862306a36Sopenharmony_ci * 157962306a36Sopenharmony_ci * distance to fault = (VCT_DATA - 22) * 4 / cable propagation velocity 158062306a36Sopenharmony_ci */ 158162306a36Sopenharmony_ci if (phydev_id_compare(phydev, PHY_ID_KSZ9131)) 158262306a36Sopenharmony_ci dt = clamp(dt - 22, 0, 255); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci return (dt * 400) / 10; 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic int ksz9x31_cable_test_wait_for_completion(struct phy_device *phydev) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci int val, ret; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci ret = phy_read_poll_timeout(phydev, KSZ9x31_LMD, val, 159262306a36Sopenharmony_ci !(val & KSZ9x31_LMD_VCT_EN), 159362306a36Sopenharmony_ci 30000, 100000, true); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci return ret < 0 ? ret : 0; 159662306a36Sopenharmony_ci} 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_cistatic int ksz9x31_cable_test_get_pair(int pair) 159962306a36Sopenharmony_ci{ 160062306a36Sopenharmony_ci static const int ethtool_pair[] = { 160162306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_A, 160262306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_B, 160362306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_C, 160462306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_D, 160562306a36Sopenharmony_ci }; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci return ethtool_pair[pair]; 160862306a36Sopenharmony_ci} 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_cistatic int ksz9x31_cable_test_one_pair(struct phy_device *phydev, int pair) 161162306a36Sopenharmony_ci{ 161262306a36Sopenharmony_ci int ret, val; 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci /* KSZ9131RNX, DS00002841B-page 38, 4.14 LinkMD (R) Cable Diagnostic 161562306a36Sopenharmony_ci * To test each individual cable pair, set the cable pair in the Cable 161662306a36Sopenharmony_ci * Diagnostics Test Pair (VCT_PAIR[1:0]) field of the LinkMD Cable 161762306a36Sopenharmony_ci * Diagnostic Register, along with setting the Cable Diagnostics Test 161862306a36Sopenharmony_ci * Enable (VCT_EN) bit. The Cable Diagnostics Test Enable (VCT_EN) bit 161962306a36Sopenharmony_ci * will self clear when the test is concluded. 162062306a36Sopenharmony_ci */ 162162306a36Sopenharmony_ci ret = phy_write(phydev, KSZ9x31_LMD, 162262306a36Sopenharmony_ci KSZ9x31_LMD_VCT_EN | KSZ9x31_LMD_VCT_PAIR(pair)); 162362306a36Sopenharmony_ci if (ret) 162462306a36Sopenharmony_ci return ret; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci ret = ksz9x31_cable_test_wait_for_completion(phydev); 162762306a36Sopenharmony_ci if (ret) 162862306a36Sopenharmony_ci return ret; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci val = phy_read(phydev, KSZ9x31_LMD); 163162306a36Sopenharmony_ci if (val < 0) 163262306a36Sopenharmony_ci return val; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci if (ksz9x31_cable_test_failed(val)) 163562306a36Sopenharmony_ci return -EAGAIN; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci ret = ethnl_cable_test_result(phydev, 163862306a36Sopenharmony_ci ksz9x31_cable_test_get_pair(pair), 163962306a36Sopenharmony_ci ksz9x31_cable_test_result_trans(val)); 164062306a36Sopenharmony_ci if (ret) 164162306a36Sopenharmony_ci return ret; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (!ksz9x31_cable_test_fault_length_valid(val)) 164462306a36Sopenharmony_ci return 0; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci return ethnl_cable_test_fault_length(phydev, 164762306a36Sopenharmony_ci ksz9x31_cable_test_get_pair(pair), 164862306a36Sopenharmony_ci ksz9x31_cable_test_fault_length(phydev, val)); 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic int ksz9x31_cable_test_get_status(struct phy_device *phydev, 165262306a36Sopenharmony_ci bool *finished) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 165562306a36Sopenharmony_ci unsigned long pair_mask = 0xf; 165662306a36Sopenharmony_ci int retries = 20; 165762306a36Sopenharmony_ci int pair, ret, rv; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci *finished = false; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci /* Try harder if link partner is active */ 166262306a36Sopenharmony_ci while (pair_mask && retries--) { 166362306a36Sopenharmony_ci for_each_set_bit(pair, &pair_mask, 4) { 166462306a36Sopenharmony_ci ret = ksz9x31_cable_test_one_pair(phydev, pair); 166562306a36Sopenharmony_ci if (ret == -EAGAIN) 166662306a36Sopenharmony_ci continue; 166762306a36Sopenharmony_ci if (ret < 0) 166862306a36Sopenharmony_ci return ret; 166962306a36Sopenharmony_ci clear_bit(pair, &pair_mask); 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci /* If link partner is in autonegotiation mode it will send 2ms 167262306a36Sopenharmony_ci * of FLPs with at least 6ms of silence. 167362306a36Sopenharmony_ci * Add 2ms sleep to have better chances to hit this silence. 167462306a36Sopenharmony_ci */ 167562306a36Sopenharmony_ci if (pair_mask) 167662306a36Sopenharmony_ci usleep_range(2000, 3000); 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci /* Report remaining unfinished pair result as unknown. */ 168062306a36Sopenharmony_ci for_each_set_bit(pair, &pair_mask, 4) { 168162306a36Sopenharmony_ci ret = ethnl_cable_test_result(phydev, 168262306a36Sopenharmony_ci ksz9x31_cable_test_get_pair(pair), 168362306a36Sopenharmony_ci ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC); 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci *finished = true; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci /* Restore cached bits from before LinkMD got started. */ 168962306a36Sopenharmony_ci rv = phy_modify(phydev, MII_CTRL1000, 169062306a36Sopenharmony_ci CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER, 169162306a36Sopenharmony_ci priv->vct_ctrl1000); 169262306a36Sopenharmony_ci if (rv) 169362306a36Sopenharmony_ci return rv; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci return ret; 169662306a36Sopenharmony_ci} 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_cistatic int ksz8873mll_config_aneg(struct phy_device *phydev) 169962306a36Sopenharmony_ci{ 170062306a36Sopenharmony_ci return 0; 170162306a36Sopenharmony_ci} 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_cistatic int ksz886x_config_mdix(struct phy_device *phydev, u8 ctrl) 170462306a36Sopenharmony_ci{ 170562306a36Sopenharmony_ci u16 val; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci switch (ctrl) { 170862306a36Sopenharmony_ci case ETH_TP_MDI: 170962306a36Sopenharmony_ci val = KSZ886X_BMCR_DISABLE_AUTO_MDIX; 171062306a36Sopenharmony_ci break; 171162306a36Sopenharmony_ci case ETH_TP_MDI_X: 171262306a36Sopenharmony_ci /* Note: The naming of the bit KSZ886X_BMCR_FORCE_MDI is bit 171362306a36Sopenharmony_ci * counter intuitive, the "-X" in "1 = Force MDI" in the data 171462306a36Sopenharmony_ci * sheet seems to be missing: 171562306a36Sopenharmony_ci * 1 = Force MDI (sic!) (transmit on RX+/RX- pins) 171662306a36Sopenharmony_ci * 0 = Normal operation (transmit on TX+/TX- pins) 171762306a36Sopenharmony_ci */ 171862306a36Sopenharmony_ci val = KSZ886X_BMCR_DISABLE_AUTO_MDIX | KSZ886X_BMCR_FORCE_MDI; 171962306a36Sopenharmony_ci break; 172062306a36Sopenharmony_ci case ETH_TP_MDI_AUTO: 172162306a36Sopenharmony_ci val = 0; 172262306a36Sopenharmony_ci break; 172362306a36Sopenharmony_ci default: 172462306a36Sopenharmony_ci return 0; 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci return phy_modify(phydev, MII_BMCR, 172862306a36Sopenharmony_ci KSZ886X_BMCR_HP_MDIX | KSZ886X_BMCR_FORCE_MDI | 172962306a36Sopenharmony_ci KSZ886X_BMCR_DISABLE_AUTO_MDIX, 173062306a36Sopenharmony_ci KSZ886X_BMCR_HP_MDIX | val); 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_cistatic int ksz886x_config_aneg(struct phy_device *phydev) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci int ret; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci ret = genphy_config_aneg(phydev); 173862306a36Sopenharmony_ci if (ret) 173962306a36Sopenharmony_ci return ret; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci /* The MDI-X configuration is automatically changed by the PHY after 174262306a36Sopenharmony_ci * switching from autoneg off to on. So, take MDI-X configuration under 174362306a36Sopenharmony_ci * own control and set it after autoneg configuration was done. 174462306a36Sopenharmony_ci */ 174562306a36Sopenharmony_ci return ksz886x_config_mdix(phydev, phydev->mdix_ctrl); 174662306a36Sopenharmony_ci} 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_cistatic int ksz886x_mdix_update(struct phy_device *phydev) 174962306a36Sopenharmony_ci{ 175062306a36Sopenharmony_ci int ret; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci ret = phy_read(phydev, MII_BMCR); 175362306a36Sopenharmony_ci if (ret < 0) 175462306a36Sopenharmony_ci return ret; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci if (ret & KSZ886X_BMCR_DISABLE_AUTO_MDIX) { 175762306a36Sopenharmony_ci if (ret & KSZ886X_BMCR_FORCE_MDI) 175862306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI_X; 175962306a36Sopenharmony_ci else 176062306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI; 176162306a36Sopenharmony_ci } else { 176262306a36Sopenharmony_ci phydev->mdix_ctrl = ETH_TP_MDI_AUTO; 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci ret = phy_read(phydev, MII_KSZPHY_CTRL); 176662306a36Sopenharmony_ci if (ret < 0) 176762306a36Sopenharmony_ci return ret; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci /* Same reverse logic as KSZ886X_BMCR_FORCE_MDI */ 177062306a36Sopenharmony_ci if (ret & KSZ886X_CTRL_MDIX_STAT) 177162306a36Sopenharmony_ci phydev->mdix = ETH_TP_MDI_X; 177262306a36Sopenharmony_ci else 177362306a36Sopenharmony_ci phydev->mdix = ETH_TP_MDI; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci return 0; 177662306a36Sopenharmony_ci} 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_cistatic int ksz886x_read_status(struct phy_device *phydev) 177962306a36Sopenharmony_ci{ 178062306a36Sopenharmony_ci int ret; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci ret = ksz886x_mdix_update(phydev); 178362306a36Sopenharmony_ci if (ret < 0) 178462306a36Sopenharmony_ci return ret; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci return genphy_read_status(phydev); 178762306a36Sopenharmony_ci} 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_cistruct ksz9477_errata_write { 179062306a36Sopenharmony_ci u8 dev_addr; 179162306a36Sopenharmony_ci u8 reg_addr; 179262306a36Sopenharmony_ci u16 val; 179362306a36Sopenharmony_ci}; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_cistatic const struct ksz9477_errata_write ksz9477_errata_writes[] = { 179662306a36Sopenharmony_ci /* Register settings are needed to improve PHY receive performance */ 179762306a36Sopenharmony_ci {0x01, 0x6f, 0xdd0b}, 179862306a36Sopenharmony_ci {0x01, 0x8f, 0x6032}, 179962306a36Sopenharmony_ci {0x01, 0x9d, 0x248c}, 180062306a36Sopenharmony_ci {0x01, 0x75, 0x0060}, 180162306a36Sopenharmony_ci {0x01, 0xd3, 0x7777}, 180262306a36Sopenharmony_ci {0x1c, 0x06, 0x3008}, 180362306a36Sopenharmony_ci {0x1c, 0x08, 0x2000}, 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci /* Transmit waveform amplitude can be improved (1000BASE-T, 100BASE-TX, 10BASE-Te) */ 180662306a36Sopenharmony_ci {0x1c, 0x04, 0x00d0}, 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci /* Register settings are required to meet data sheet supply current specifications */ 180962306a36Sopenharmony_ci {0x1c, 0x13, 0x6eff}, 181062306a36Sopenharmony_ci {0x1c, 0x14, 0xe6ff}, 181162306a36Sopenharmony_ci {0x1c, 0x15, 0x6eff}, 181262306a36Sopenharmony_ci {0x1c, 0x16, 0xe6ff}, 181362306a36Sopenharmony_ci {0x1c, 0x17, 0x00ff}, 181462306a36Sopenharmony_ci {0x1c, 0x18, 0x43ff}, 181562306a36Sopenharmony_ci {0x1c, 0x19, 0xc3ff}, 181662306a36Sopenharmony_ci {0x1c, 0x1a, 0x6fff}, 181762306a36Sopenharmony_ci {0x1c, 0x1b, 0x07ff}, 181862306a36Sopenharmony_ci {0x1c, 0x1c, 0x0fff}, 181962306a36Sopenharmony_ci {0x1c, 0x1d, 0xe7ff}, 182062306a36Sopenharmony_ci {0x1c, 0x1e, 0xefff}, 182162306a36Sopenharmony_ci {0x1c, 0x20, 0xeeee}, 182262306a36Sopenharmony_ci}; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cistatic int ksz9477_config_init(struct phy_device *phydev) 182562306a36Sopenharmony_ci{ 182662306a36Sopenharmony_ci int err; 182762306a36Sopenharmony_ci int i; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci /* Apply PHY settings to address errata listed in 183062306a36Sopenharmony_ci * KSZ9477, KSZ9897, KSZ9896, KSZ9567, KSZ8565 183162306a36Sopenharmony_ci * Silicon Errata and Data Sheet Clarification documents. 183262306a36Sopenharmony_ci * 183362306a36Sopenharmony_ci * Document notes: Before configuring the PHY MMD registers, it is 183462306a36Sopenharmony_ci * necessary to set the PHY to 100 Mbps speed with auto-negotiation 183562306a36Sopenharmony_ci * disabled by writing to register 0xN100-0xN101. After writing the 183662306a36Sopenharmony_ci * MMD registers, and after all errata workarounds that involve PHY 183762306a36Sopenharmony_ci * register settings, write register 0xN100-0xN101 again to enable 183862306a36Sopenharmony_ci * and restart auto-negotiation. 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_ci err = phy_write(phydev, MII_BMCR, BMCR_SPEED100 | BMCR_FULLDPLX); 184162306a36Sopenharmony_ci if (err) 184262306a36Sopenharmony_ci return err; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ksz9477_errata_writes); ++i) { 184562306a36Sopenharmony_ci const struct ksz9477_errata_write *errata = &ksz9477_errata_writes[i]; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci err = phy_write_mmd(phydev, errata->dev_addr, errata->reg_addr, errata->val); 184862306a36Sopenharmony_ci if (err) 184962306a36Sopenharmony_ci return err; 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci /* According to KSZ9477 Errata DS80000754C (Module 4) all EEE modes 185362306a36Sopenharmony_ci * in this switch shall be regarded as broken. 185462306a36Sopenharmony_ci */ 185562306a36Sopenharmony_ci if (phydev->dev_flags & MICREL_NO_EEE) 185662306a36Sopenharmony_ci phydev->eee_broken_modes = -1; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci err = genphy_restart_aneg(phydev); 185962306a36Sopenharmony_ci if (err) 186062306a36Sopenharmony_ci return err; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci return kszphy_config_init(phydev); 186362306a36Sopenharmony_ci} 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_cistatic int kszphy_get_sset_count(struct phy_device *phydev) 186662306a36Sopenharmony_ci{ 186762306a36Sopenharmony_ci return ARRAY_SIZE(kszphy_hw_stats); 186862306a36Sopenharmony_ci} 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_cistatic void kszphy_get_strings(struct phy_device *phydev, u8 *data) 187162306a36Sopenharmony_ci{ 187262306a36Sopenharmony_ci int i; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) { 187562306a36Sopenharmony_ci strscpy(data + i * ETH_GSTRING_LEN, 187662306a36Sopenharmony_ci kszphy_hw_stats[i].string, ETH_GSTRING_LEN); 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci} 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_cistatic u64 kszphy_get_stat(struct phy_device *phydev, int i) 188162306a36Sopenharmony_ci{ 188262306a36Sopenharmony_ci struct kszphy_hw_stat stat = kszphy_hw_stats[i]; 188362306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 188462306a36Sopenharmony_ci int val; 188562306a36Sopenharmony_ci u64 ret; 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci val = phy_read(phydev, stat.reg); 188862306a36Sopenharmony_ci if (val < 0) { 188962306a36Sopenharmony_ci ret = U64_MAX; 189062306a36Sopenharmony_ci } else { 189162306a36Sopenharmony_ci val = val & ((1 << stat.bits) - 1); 189262306a36Sopenharmony_ci priv->stats[i] += val; 189362306a36Sopenharmony_ci ret = priv->stats[i]; 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci return ret; 189762306a36Sopenharmony_ci} 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_cistatic void kszphy_get_stats(struct phy_device *phydev, 190062306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci int i; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(kszphy_hw_stats); i++) 190562306a36Sopenharmony_ci data[i] = kszphy_get_stat(phydev, i); 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic int kszphy_suspend(struct phy_device *phydev) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci /* Disable PHY Interrupts */ 191162306a36Sopenharmony_ci if (phy_interrupt_is_valid(phydev)) { 191262306a36Sopenharmony_ci phydev->interrupts = PHY_INTERRUPT_DISABLED; 191362306a36Sopenharmony_ci if (phydev->drv->config_intr) 191462306a36Sopenharmony_ci phydev->drv->config_intr(phydev); 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci return genphy_suspend(phydev); 191862306a36Sopenharmony_ci} 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cistatic void kszphy_parse_led_mode(struct phy_device *phydev) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 192362306a36Sopenharmony_ci const struct device_node *np = phydev->mdio.dev.of_node; 192462306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 192562306a36Sopenharmony_ci int ret; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (type && type->led_mode_reg) { 192862306a36Sopenharmony_ci ret = of_property_read_u32(np, "micrel,led-mode", 192962306a36Sopenharmony_ci &priv->led_mode); 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci if (ret) 193262306a36Sopenharmony_ci priv->led_mode = -1; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci if (priv->led_mode > 3) { 193562306a36Sopenharmony_ci phydev_err(phydev, "invalid led mode: 0x%02x\n", 193662306a36Sopenharmony_ci priv->led_mode); 193762306a36Sopenharmony_ci priv->led_mode = -1; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci } else { 194062306a36Sopenharmony_ci priv->led_mode = -1; 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci} 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_cistatic int kszphy_resume(struct phy_device *phydev) 194562306a36Sopenharmony_ci{ 194662306a36Sopenharmony_ci int ret; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci genphy_resume(phydev); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci /* After switching from power-down to normal mode, an internal global 195162306a36Sopenharmony_ci * reset is automatically generated. Wait a minimum of 1 ms before 195262306a36Sopenharmony_ci * read/write access to the PHY registers. 195362306a36Sopenharmony_ci */ 195462306a36Sopenharmony_ci usleep_range(1000, 2000); 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci ret = kszphy_config_reset(phydev); 195762306a36Sopenharmony_ci if (ret) 195862306a36Sopenharmony_ci return ret; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci /* Enable PHY Interrupts */ 196162306a36Sopenharmony_ci if (phy_interrupt_is_valid(phydev)) { 196262306a36Sopenharmony_ci phydev->interrupts = PHY_INTERRUPT_ENABLED; 196362306a36Sopenharmony_ci if (phydev->drv->config_intr) 196462306a36Sopenharmony_ci phydev->drv->config_intr(phydev); 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci return 0; 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_cistatic int kszphy_probe(struct phy_device *phydev) 197162306a36Sopenharmony_ci{ 197262306a36Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 197362306a36Sopenharmony_ci const struct device_node *np = phydev->mdio.dev.of_node; 197462306a36Sopenharmony_ci struct kszphy_priv *priv; 197562306a36Sopenharmony_ci struct clk *clk; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 197862306a36Sopenharmony_ci if (!priv) 197962306a36Sopenharmony_ci return -ENOMEM; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci phydev->priv = priv; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci priv->type = type; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci kszphy_parse_led_mode(phydev); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref"); 198862306a36Sopenharmony_ci /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ 198962306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(clk)) { 199062306a36Sopenharmony_ci unsigned long rate = clk_get_rate(clk); 199162306a36Sopenharmony_ci bool rmii_ref_clk_sel_25_mhz; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci if (type) 199462306a36Sopenharmony_ci priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel; 199562306a36Sopenharmony_ci rmii_ref_clk_sel_25_mhz = of_property_read_bool(np, 199662306a36Sopenharmony_ci "micrel,rmii-reference-clock-select-25-mhz"); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci if (rate > 24500000 && rate < 25500000) { 199962306a36Sopenharmony_ci priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz; 200062306a36Sopenharmony_ci } else if (rate > 49500000 && rate < 50500000) { 200162306a36Sopenharmony_ci priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz; 200262306a36Sopenharmony_ci } else { 200362306a36Sopenharmony_ci phydev_err(phydev, "Clock rate out of range: %ld\n", 200462306a36Sopenharmony_ci rate); 200562306a36Sopenharmony_ci return -EINVAL; 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci if (ksz8041_fiber_mode(phydev)) 201062306a36Sopenharmony_ci phydev->port = PORT_FIBRE; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci /* Support legacy board-file configuration */ 201362306a36Sopenharmony_ci if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { 201462306a36Sopenharmony_ci priv->rmii_ref_clk_sel = true; 201562306a36Sopenharmony_ci priv->rmii_ref_clk_sel_val = true; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci return 0; 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_cistatic int lan8814_cable_test_start(struct phy_device *phydev) 202262306a36Sopenharmony_ci{ 202362306a36Sopenharmony_ci /* If autoneg is enabled, we won't be able to test cross pair 202462306a36Sopenharmony_ci * short. In this case, the PHY will "detect" a link and 202562306a36Sopenharmony_ci * confuse the internal state machine - disable auto neg here. 202662306a36Sopenharmony_ci * Set the speed to 1000mbit and full duplex. 202762306a36Sopenharmony_ci */ 202862306a36Sopenharmony_ci return phy_modify(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_SPEED100, 202962306a36Sopenharmony_ci BMCR_SPEED1000 | BMCR_FULLDPLX); 203062306a36Sopenharmony_ci} 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_cistatic int ksz886x_cable_test_start(struct phy_device *phydev) 203362306a36Sopenharmony_ci{ 203462306a36Sopenharmony_ci if (phydev->dev_flags & MICREL_KSZ8_P1_ERRATA) 203562306a36Sopenharmony_ci return -EOPNOTSUPP; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci /* If autoneg is enabled, we won't be able to test cross pair 203862306a36Sopenharmony_ci * short. In this case, the PHY will "detect" a link and 203962306a36Sopenharmony_ci * confuse the internal state machine - disable auto neg here. 204062306a36Sopenharmony_ci * If autoneg is disabled, we should set the speed to 10mbit. 204162306a36Sopenharmony_ci */ 204262306a36Sopenharmony_ci return phy_clear_bits(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_SPEED100); 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_cistatic __always_inline int ksz886x_cable_test_result_trans(u16 status, u16 mask) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci switch (FIELD_GET(mask, status)) { 204862306a36Sopenharmony_ci case KSZ8081_LMD_STAT_NORMAL: 204962306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OK; 205062306a36Sopenharmony_ci case KSZ8081_LMD_STAT_SHORT: 205162306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT; 205262306a36Sopenharmony_ci case KSZ8081_LMD_STAT_OPEN: 205362306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_OPEN; 205462306a36Sopenharmony_ci case KSZ8081_LMD_STAT_FAIL: 205562306a36Sopenharmony_ci fallthrough; 205662306a36Sopenharmony_ci default: 205762306a36Sopenharmony_ci return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC; 205862306a36Sopenharmony_ci } 205962306a36Sopenharmony_ci} 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_cistatic __always_inline bool ksz886x_cable_test_failed(u16 status, u16 mask) 206262306a36Sopenharmony_ci{ 206362306a36Sopenharmony_ci return FIELD_GET(mask, status) == 206462306a36Sopenharmony_ci KSZ8081_LMD_STAT_FAIL; 206562306a36Sopenharmony_ci} 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_cistatic __always_inline bool ksz886x_cable_test_fault_length_valid(u16 status, u16 mask) 206862306a36Sopenharmony_ci{ 206962306a36Sopenharmony_ci switch (FIELD_GET(mask, status)) { 207062306a36Sopenharmony_ci case KSZ8081_LMD_STAT_OPEN: 207162306a36Sopenharmony_ci fallthrough; 207262306a36Sopenharmony_ci case KSZ8081_LMD_STAT_SHORT: 207362306a36Sopenharmony_ci return true; 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci return false; 207662306a36Sopenharmony_ci} 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_cistatic __always_inline int ksz886x_cable_test_fault_length(struct phy_device *phydev, 207962306a36Sopenharmony_ci u16 status, u16 data_mask) 208062306a36Sopenharmony_ci{ 208162306a36Sopenharmony_ci int dt; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* According to the data sheet the distance to the fault is 208462306a36Sopenharmony_ci * DELTA_TIME * 0.4 meters for ksz phys. 208562306a36Sopenharmony_ci * (DELTA_TIME - 22) * 0.8 for lan8814 phy. 208662306a36Sopenharmony_ci */ 208762306a36Sopenharmony_ci dt = FIELD_GET(data_mask, status); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci if (phydev_id_compare(phydev, PHY_ID_LAN8814)) 209062306a36Sopenharmony_ci return ((dt - 22) * 800) / 10; 209162306a36Sopenharmony_ci else 209262306a36Sopenharmony_ci return (dt * 400) / 10; 209362306a36Sopenharmony_ci} 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_cistatic int ksz886x_cable_test_wait_for_completion(struct phy_device *phydev) 209662306a36Sopenharmony_ci{ 209762306a36Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 209862306a36Sopenharmony_ci int val, ret; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci ret = phy_read_poll_timeout(phydev, type->cable_diag_reg, val, 210162306a36Sopenharmony_ci !(val & KSZ8081_LMD_ENABLE_TEST), 210262306a36Sopenharmony_ci 30000, 100000, true); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci return ret < 0 ? ret : 0; 210562306a36Sopenharmony_ci} 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_cistatic int lan8814_cable_test_one_pair(struct phy_device *phydev, int pair) 210862306a36Sopenharmony_ci{ 210962306a36Sopenharmony_ci static const int ethtool_pair[] = { ETHTOOL_A_CABLE_PAIR_A, 211062306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_B, 211162306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_C, 211262306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_D, 211362306a36Sopenharmony_ci }; 211462306a36Sopenharmony_ci u32 fault_length; 211562306a36Sopenharmony_ci int ret; 211662306a36Sopenharmony_ci int val; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci val = KSZ8081_LMD_ENABLE_TEST; 211962306a36Sopenharmony_ci val = val | (pair << LAN8814_PAIR_BIT_SHIFT); 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci ret = phy_write(phydev, LAN8814_CABLE_DIAG, val); 212262306a36Sopenharmony_ci if (ret < 0) 212362306a36Sopenharmony_ci return ret; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci ret = ksz886x_cable_test_wait_for_completion(phydev); 212662306a36Sopenharmony_ci if (ret) 212762306a36Sopenharmony_ci return ret; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci val = phy_read(phydev, LAN8814_CABLE_DIAG); 213062306a36Sopenharmony_ci if (val < 0) 213162306a36Sopenharmony_ci return val; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci if (ksz886x_cable_test_failed(val, LAN8814_CABLE_DIAG_STAT_MASK)) 213462306a36Sopenharmony_ci return -EAGAIN; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci ret = ethnl_cable_test_result(phydev, ethtool_pair[pair], 213762306a36Sopenharmony_ci ksz886x_cable_test_result_trans(val, 213862306a36Sopenharmony_ci LAN8814_CABLE_DIAG_STAT_MASK 213962306a36Sopenharmony_ci )); 214062306a36Sopenharmony_ci if (ret) 214162306a36Sopenharmony_ci return ret; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci if (!ksz886x_cable_test_fault_length_valid(val, LAN8814_CABLE_DIAG_STAT_MASK)) 214462306a36Sopenharmony_ci return 0; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci fault_length = ksz886x_cable_test_fault_length(phydev, val, 214762306a36Sopenharmony_ci LAN8814_CABLE_DIAG_VCT_DATA_MASK); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci return ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], fault_length); 215062306a36Sopenharmony_ci} 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_cistatic int ksz886x_cable_test_one_pair(struct phy_device *phydev, int pair) 215362306a36Sopenharmony_ci{ 215462306a36Sopenharmony_ci static const int ethtool_pair[] = { 215562306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_A, 215662306a36Sopenharmony_ci ETHTOOL_A_CABLE_PAIR_B, 215762306a36Sopenharmony_ci }; 215862306a36Sopenharmony_ci int ret, val, mdix; 215962306a36Sopenharmony_ci u32 fault_length; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci /* There is no way to choice the pair, like we do one ksz9031. 216262306a36Sopenharmony_ci * We can workaround this limitation by using the MDI-X functionality. 216362306a36Sopenharmony_ci */ 216462306a36Sopenharmony_ci if (pair == 0) 216562306a36Sopenharmony_ci mdix = ETH_TP_MDI; 216662306a36Sopenharmony_ci else 216762306a36Sopenharmony_ci mdix = ETH_TP_MDI_X; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci switch (phydev->phy_id & MICREL_PHY_ID_MASK) { 217062306a36Sopenharmony_ci case PHY_ID_KSZ8081: 217162306a36Sopenharmony_ci ret = ksz8081_config_mdix(phydev, mdix); 217262306a36Sopenharmony_ci break; 217362306a36Sopenharmony_ci case PHY_ID_KSZ886X: 217462306a36Sopenharmony_ci ret = ksz886x_config_mdix(phydev, mdix); 217562306a36Sopenharmony_ci break; 217662306a36Sopenharmony_ci default: 217762306a36Sopenharmony_ci ret = -ENODEV; 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci if (ret) 218162306a36Sopenharmony_ci return ret; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci /* Now we are ready to fire. This command will send a 100ns pulse 218462306a36Sopenharmony_ci * to the pair. 218562306a36Sopenharmony_ci */ 218662306a36Sopenharmony_ci ret = phy_write(phydev, KSZ8081_LMD, KSZ8081_LMD_ENABLE_TEST); 218762306a36Sopenharmony_ci if (ret) 218862306a36Sopenharmony_ci return ret; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci ret = ksz886x_cable_test_wait_for_completion(phydev); 219162306a36Sopenharmony_ci if (ret) 219262306a36Sopenharmony_ci return ret; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci val = phy_read(phydev, KSZ8081_LMD); 219562306a36Sopenharmony_ci if (val < 0) 219662306a36Sopenharmony_ci return val; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci if (ksz886x_cable_test_failed(val, KSZ8081_LMD_STAT_MASK)) 219962306a36Sopenharmony_ci return -EAGAIN; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci ret = ethnl_cable_test_result(phydev, ethtool_pair[pair], 220262306a36Sopenharmony_ci ksz886x_cable_test_result_trans(val, KSZ8081_LMD_STAT_MASK)); 220362306a36Sopenharmony_ci if (ret) 220462306a36Sopenharmony_ci return ret; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci if (!ksz886x_cable_test_fault_length_valid(val, KSZ8081_LMD_STAT_MASK)) 220762306a36Sopenharmony_ci return 0; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci fault_length = ksz886x_cable_test_fault_length(phydev, val, KSZ8081_LMD_DELTA_TIME_MASK); 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci return ethnl_cable_test_fault_length(phydev, ethtool_pair[pair], fault_length); 221262306a36Sopenharmony_ci} 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_cistatic int ksz886x_cable_test_get_status(struct phy_device *phydev, 221562306a36Sopenharmony_ci bool *finished) 221662306a36Sopenharmony_ci{ 221762306a36Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 221862306a36Sopenharmony_ci unsigned long pair_mask = type->pair_mask; 221962306a36Sopenharmony_ci int retries = 20; 222062306a36Sopenharmony_ci int ret = 0; 222162306a36Sopenharmony_ci int pair; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci *finished = false; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci /* Try harder if link partner is active */ 222662306a36Sopenharmony_ci while (pair_mask && retries--) { 222762306a36Sopenharmony_ci for_each_set_bit(pair, &pair_mask, 4) { 222862306a36Sopenharmony_ci if (type->cable_diag_reg == LAN8814_CABLE_DIAG) 222962306a36Sopenharmony_ci ret = lan8814_cable_test_one_pair(phydev, pair); 223062306a36Sopenharmony_ci else 223162306a36Sopenharmony_ci ret = ksz886x_cable_test_one_pair(phydev, pair); 223262306a36Sopenharmony_ci if (ret == -EAGAIN) 223362306a36Sopenharmony_ci continue; 223462306a36Sopenharmony_ci if (ret < 0) 223562306a36Sopenharmony_ci return ret; 223662306a36Sopenharmony_ci clear_bit(pair, &pair_mask); 223762306a36Sopenharmony_ci } 223862306a36Sopenharmony_ci /* If link partner is in autonegotiation mode it will send 2ms 223962306a36Sopenharmony_ci * of FLPs with at least 6ms of silence. 224062306a36Sopenharmony_ci * Add 2ms sleep to have better chances to hit this silence. 224162306a36Sopenharmony_ci */ 224262306a36Sopenharmony_ci if (pair_mask) 224362306a36Sopenharmony_ci msleep(2); 224462306a36Sopenharmony_ci } 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci *finished = true; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci return ret; 224962306a36Sopenharmony_ci} 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci#define LAN_EXT_PAGE_ACCESS_CONTROL 0x16 225262306a36Sopenharmony_ci#define LAN_EXT_PAGE_ACCESS_ADDRESS_DATA 0x17 225362306a36Sopenharmony_ci#define LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC 0x4000 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci#define LAN8814_QSGMII_SOFT_RESET 0x43 225662306a36Sopenharmony_ci#define LAN8814_QSGMII_SOFT_RESET_BIT BIT(0) 225762306a36Sopenharmony_ci#define LAN8814_QSGMII_PCS1G_ANEG_CONFIG 0x13 225862306a36Sopenharmony_ci#define LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA BIT(3) 225962306a36Sopenharmony_ci#define LAN8814_ALIGN_SWAP 0x4a 226062306a36Sopenharmony_ci#define LAN8814_ALIGN_TX_A_B_SWAP 0x1 226162306a36Sopenharmony_ci#define LAN8814_ALIGN_TX_A_B_SWAP_MASK GENMASK(2, 0) 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci#define LAN8804_ALIGN_SWAP 0x4a 226462306a36Sopenharmony_ci#define LAN8804_ALIGN_TX_A_B_SWAP 0x1 226562306a36Sopenharmony_ci#define LAN8804_ALIGN_TX_A_B_SWAP_MASK GENMASK(2, 0) 226662306a36Sopenharmony_ci#define LAN8814_CLOCK_MANAGEMENT 0xd 226762306a36Sopenharmony_ci#define LAN8814_LINK_QUALITY 0x8e 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_cistatic int lanphy_read_page_reg(struct phy_device *phydev, int page, u32 addr) 227062306a36Sopenharmony_ci{ 227162306a36Sopenharmony_ci int data; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 227462306a36Sopenharmony_ci __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); 227562306a36Sopenharmony_ci __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); 227662306a36Sopenharmony_ci __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, 227762306a36Sopenharmony_ci (page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC)); 227862306a36Sopenharmony_ci data = __phy_read(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA); 227962306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci return data; 228262306a36Sopenharmony_ci} 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_cistatic int lanphy_write_page_reg(struct phy_device *phydev, int page, u16 addr, 228562306a36Sopenharmony_ci u16 val) 228662306a36Sopenharmony_ci{ 228762306a36Sopenharmony_ci phy_lock_mdio_bus(phydev); 228862306a36Sopenharmony_ci __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, page); 228962306a36Sopenharmony_ci __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, addr); 229062306a36Sopenharmony_ci __phy_write(phydev, LAN_EXT_PAGE_ACCESS_CONTROL, 229162306a36Sopenharmony_ci page | LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci val = __phy_write(phydev, LAN_EXT_PAGE_ACCESS_ADDRESS_DATA, val); 229462306a36Sopenharmony_ci if (val != 0) 229562306a36Sopenharmony_ci phydev_err(phydev, "Error: phy_write has returned error %d\n", 229662306a36Sopenharmony_ci val); 229762306a36Sopenharmony_ci phy_unlock_mdio_bus(phydev); 229862306a36Sopenharmony_ci return val; 229962306a36Sopenharmony_ci} 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cistatic int lan8814_config_ts_intr(struct phy_device *phydev, bool enable) 230262306a36Sopenharmony_ci{ 230362306a36Sopenharmony_ci u16 val = 0; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci if (enable) 230662306a36Sopenharmony_ci val = PTP_TSU_INT_EN_PTP_TX_TS_EN_ | 230762306a36Sopenharmony_ci PTP_TSU_INT_EN_PTP_TX_TS_OVRFL_EN_ | 230862306a36Sopenharmony_ci PTP_TSU_INT_EN_PTP_RX_TS_EN_ | 230962306a36Sopenharmony_ci PTP_TSU_INT_EN_PTP_RX_TS_OVRFL_EN_; 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci return lanphy_write_page_reg(phydev, 5, PTP_TSU_INT_EN, val); 231262306a36Sopenharmony_ci} 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_cistatic void lan8814_ptp_rx_ts_get(struct phy_device *phydev, 231562306a36Sopenharmony_ci u32 *seconds, u32 *nano_seconds, u16 *seq_id) 231662306a36Sopenharmony_ci{ 231762306a36Sopenharmony_ci *seconds = lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_HI); 231862306a36Sopenharmony_ci *seconds = (*seconds << 16) | 231962306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_LO); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci *nano_seconds = lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_HI); 232262306a36Sopenharmony_ci *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | 232362306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_LO); 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci *seq_id = lanphy_read_page_reg(phydev, 5, PTP_RX_MSG_HEADER2); 232662306a36Sopenharmony_ci} 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_cistatic void lan8814_ptp_tx_ts_get(struct phy_device *phydev, 232962306a36Sopenharmony_ci u32 *seconds, u32 *nano_seconds, u16 *seq_id) 233062306a36Sopenharmony_ci{ 233162306a36Sopenharmony_ci *seconds = lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_HI); 233262306a36Sopenharmony_ci *seconds = *seconds << 16 | 233362306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_LO); 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci *nano_seconds = lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_HI); 233662306a36Sopenharmony_ci *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | 233762306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_LO); 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci *seq_id = lanphy_read_page_reg(phydev, 5, PTP_TX_MSG_HEADER2); 234062306a36Sopenharmony_ci} 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_cistatic int lan8814_ts_info(struct mii_timestamper *mii_ts, struct ethtool_ts_info *info) 234362306a36Sopenharmony_ci{ 234462306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); 234562306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 234662306a36Sopenharmony_ci struct lan8814_shared_priv *shared = phydev->shared->priv; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 234962306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 235062306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci info->phc_index = ptp_clock_index(shared->ptp_clock); 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci info->tx_types = 235562306a36Sopenharmony_ci (1 << HWTSTAMP_TX_OFF) | 235662306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ON) | 235762306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ONESTEP_SYNC); 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci info->rx_filters = 236062306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_NONE) | 236162306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) | 236262306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | 236362306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | 236462306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci return 0; 236762306a36Sopenharmony_ci} 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_cistatic void lan8814_flush_fifo(struct phy_device *phydev, bool egress) 237062306a36Sopenharmony_ci{ 237162306a36Sopenharmony_ci int i; 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci for (i = 0; i < FIFO_SIZE; ++i) 237462306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 5, 237562306a36Sopenharmony_ci egress ? PTP_TX_MSG_HEADER2 : PTP_RX_MSG_HEADER2); 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci /* Read to clear overflow status bit */ 237862306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 5, PTP_TSU_INT_STS); 237962306a36Sopenharmony_ci} 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_cistatic int lan8814_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) 238262306a36Sopenharmony_ci{ 238362306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = 238462306a36Sopenharmony_ci container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); 238562306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 238662306a36Sopenharmony_ci struct lan8814_shared_priv *shared = phydev->shared->priv; 238762306a36Sopenharmony_ci struct lan8814_ptp_rx_ts *rx_ts, *tmp; 238862306a36Sopenharmony_ci struct hwtstamp_config config; 238962306a36Sopenharmony_ci int txcfg = 0, rxcfg = 0; 239062306a36Sopenharmony_ci int pkt_ts_enable; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 239362306a36Sopenharmony_ci return -EFAULT; 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci ptp_priv->hwts_tx_type = config.tx_type; 239662306a36Sopenharmony_ci ptp_priv->rx_filter = config.rx_filter; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci switch (config.rx_filter) { 239962306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 240062306a36Sopenharmony_ci ptp_priv->layer = 0; 240162306a36Sopenharmony_ci ptp_priv->version = 0; 240262306a36Sopenharmony_ci break; 240362306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 240462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 240562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 240662306a36Sopenharmony_ci ptp_priv->layer = PTP_CLASS_L4; 240762306a36Sopenharmony_ci ptp_priv->version = PTP_CLASS_V2; 240862306a36Sopenharmony_ci break; 240962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 241062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 241162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 241262306a36Sopenharmony_ci ptp_priv->layer = PTP_CLASS_L2; 241362306a36Sopenharmony_ci ptp_priv->version = PTP_CLASS_V2; 241462306a36Sopenharmony_ci break; 241562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 241662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 241762306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 241862306a36Sopenharmony_ci ptp_priv->layer = PTP_CLASS_L4 | PTP_CLASS_L2; 241962306a36Sopenharmony_ci ptp_priv->version = PTP_CLASS_V2; 242062306a36Sopenharmony_ci break; 242162306a36Sopenharmony_ci default: 242262306a36Sopenharmony_ci return -ERANGE; 242362306a36Sopenharmony_ci } 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci if (ptp_priv->layer & PTP_CLASS_L2) { 242662306a36Sopenharmony_ci rxcfg = PTP_RX_PARSE_CONFIG_LAYER2_EN_; 242762306a36Sopenharmony_ci txcfg = PTP_TX_PARSE_CONFIG_LAYER2_EN_; 242862306a36Sopenharmony_ci } else if (ptp_priv->layer & PTP_CLASS_L4) { 242962306a36Sopenharmony_ci rxcfg |= PTP_RX_PARSE_CONFIG_IPV4_EN_ | PTP_RX_PARSE_CONFIG_IPV6_EN_; 243062306a36Sopenharmony_ci txcfg |= PTP_TX_PARSE_CONFIG_IPV4_EN_ | PTP_TX_PARSE_CONFIG_IPV6_EN_; 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_RX_PARSE_CONFIG, rxcfg); 243362306a36Sopenharmony_ci lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_PARSE_CONFIG, txcfg); 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci pkt_ts_enable = PTP_TIMESTAMP_EN_SYNC_ | PTP_TIMESTAMP_EN_DREQ_ | 243662306a36Sopenharmony_ci PTP_TIMESTAMP_EN_PDREQ_ | PTP_TIMESTAMP_EN_PDRES_; 243762306a36Sopenharmony_ci lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_RX_TIMESTAMP_EN, pkt_ts_enable); 243862306a36Sopenharmony_ci lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_TIMESTAMP_EN, pkt_ts_enable); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci if (ptp_priv->hwts_tx_type == HWTSTAMP_TX_ONESTEP_SYNC) 244162306a36Sopenharmony_ci lanphy_write_page_reg(ptp_priv->phydev, 5, PTP_TX_MOD, 244262306a36Sopenharmony_ci PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_); 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci if (config.rx_filter != HWTSTAMP_FILTER_NONE) 244562306a36Sopenharmony_ci lan8814_config_ts_intr(ptp_priv->phydev, true); 244662306a36Sopenharmony_ci else 244762306a36Sopenharmony_ci lan8814_config_ts_intr(ptp_priv->phydev, false); 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci mutex_lock(&shared->shared_lock); 245062306a36Sopenharmony_ci if (config.rx_filter != HWTSTAMP_FILTER_NONE) 245162306a36Sopenharmony_ci shared->ref++; 245262306a36Sopenharmony_ci else 245362306a36Sopenharmony_ci shared->ref--; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci if (shared->ref) 245662306a36Sopenharmony_ci lanphy_write_page_reg(ptp_priv->phydev, 4, PTP_CMD_CTL, 245762306a36Sopenharmony_ci PTP_CMD_CTL_PTP_ENABLE_); 245862306a36Sopenharmony_ci else 245962306a36Sopenharmony_ci lanphy_write_page_reg(ptp_priv->phydev, 4, PTP_CMD_CTL, 246062306a36Sopenharmony_ci PTP_CMD_CTL_PTP_DISABLE_); 246162306a36Sopenharmony_ci mutex_unlock(&shared->shared_lock); 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci /* In case of multiple starts and stops, these needs to be cleared */ 246462306a36Sopenharmony_ci list_for_each_entry_safe(rx_ts, tmp, &ptp_priv->rx_ts_list, list) { 246562306a36Sopenharmony_ci list_del(&rx_ts->list); 246662306a36Sopenharmony_ci kfree(rx_ts); 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci skb_queue_purge(&ptp_priv->rx_queue); 246962306a36Sopenharmony_ci skb_queue_purge(&ptp_priv->tx_queue); 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci lan8814_flush_fifo(ptp_priv->phydev, false); 247262306a36Sopenharmony_ci lan8814_flush_fifo(ptp_priv->phydev, true); 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; 247562306a36Sopenharmony_ci} 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_cistatic void lan8814_txtstamp(struct mii_timestamper *mii_ts, 247862306a36Sopenharmony_ci struct sk_buff *skb, int type) 247962306a36Sopenharmony_ci{ 248062306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci switch (ptp_priv->hwts_tx_type) { 248362306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 248462306a36Sopenharmony_ci if (ptp_msg_is_sync(skb, type)) { 248562306a36Sopenharmony_ci kfree_skb(skb); 248662306a36Sopenharmony_ci return; 248762306a36Sopenharmony_ci } 248862306a36Sopenharmony_ci fallthrough; 248962306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 249062306a36Sopenharmony_ci skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; 249162306a36Sopenharmony_ci skb_queue_tail(&ptp_priv->tx_queue, skb); 249262306a36Sopenharmony_ci break; 249362306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 249462306a36Sopenharmony_ci default: 249562306a36Sopenharmony_ci kfree_skb(skb); 249662306a36Sopenharmony_ci break; 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci} 249962306a36Sopenharmony_ci 250062306a36Sopenharmony_cistatic void lan8814_get_sig_rx(struct sk_buff *skb, u16 *sig) 250162306a36Sopenharmony_ci{ 250262306a36Sopenharmony_ci struct ptp_header *ptp_header; 250362306a36Sopenharmony_ci u32 type; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci skb_push(skb, ETH_HLEN); 250662306a36Sopenharmony_ci type = ptp_classify_raw(skb); 250762306a36Sopenharmony_ci ptp_header = ptp_parse_header(skb, type); 250862306a36Sopenharmony_ci skb_pull_inline(skb, ETH_HLEN); 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci *sig = (__force u16)(ntohs(ptp_header->sequence_id)); 251162306a36Sopenharmony_ci} 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_cistatic bool lan8814_match_rx_skb(struct kszphy_ptp_priv *ptp_priv, 251462306a36Sopenharmony_ci struct sk_buff *skb) 251562306a36Sopenharmony_ci{ 251662306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 251762306a36Sopenharmony_ci struct lan8814_ptp_rx_ts *rx_ts, *tmp; 251862306a36Sopenharmony_ci unsigned long flags; 251962306a36Sopenharmony_ci bool ret = false; 252062306a36Sopenharmony_ci u16 skb_sig; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci lan8814_get_sig_rx(skb, &skb_sig); 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci /* Iterate over all RX timestamps and match it with the received skbs */ 252562306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->rx_ts_lock, flags); 252662306a36Sopenharmony_ci list_for_each_entry_safe(rx_ts, tmp, &ptp_priv->rx_ts_list, list) { 252762306a36Sopenharmony_ci /* Check if we found the signature we were looking for. */ 252862306a36Sopenharmony_ci if (memcmp(&skb_sig, &rx_ts->seq_id, sizeof(rx_ts->seq_id))) 252962306a36Sopenharmony_ci continue; 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 253262306a36Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 253362306a36Sopenharmony_ci shhwtstamps->hwtstamp = ktime_set(rx_ts->seconds, 253462306a36Sopenharmony_ci rx_ts->nsec); 253562306a36Sopenharmony_ci list_del(&rx_ts->list); 253662306a36Sopenharmony_ci kfree(rx_ts); 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci ret = true; 253962306a36Sopenharmony_ci break; 254062306a36Sopenharmony_ci } 254162306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->rx_ts_lock, flags); 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci if (ret) 254462306a36Sopenharmony_ci netif_rx(skb); 254562306a36Sopenharmony_ci return ret; 254662306a36Sopenharmony_ci} 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_cistatic bool lan8814_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type) 254962306a36Sopenharmony_ci{ 255062306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = 255162306a36Sopenharmony_ci container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci if (ptp_priv->rx_filter == HWTSTAMP_FILTER_NONE || 255462306a36Sopenharmony_ci type == PTP_CLASS_NONE) 255562306a36Sopenharmony_ci return false; 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci if ((type & ptp_priv->version) == 0 || (type & ptp_priv->layer) == 0) 255862306a36Sopenharmony_ci return false; 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci /* If we failed to match then add it to the queue for when the timestamp 256162306a36Sopenharmony_ci * will come 256262306a36Sopenharmony_ci */ 256362306a36Sopenharmony_ci if (!lan8814_match_rx_skb(ptp_priv, skb)) 256462306a36Sopenharmony_ci skb_queue_tail(&ptp_priv->rx_queue, skb); 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci return true; 256762306a36Sopenharmony_ci} 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_cistatic void lan8814_ptp_clock_set(struct phy_device *phydev, 257062306a36Sopenharmony_ci u32 seconds, u32 nano_seconds) 257162306a36Sopenharmony_ci{ 257262306a36Sopenharmony_ci u32 sec_low, sec_high, nsec_low, nsec_high; 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci sec_low = seconds & 0xffff; 257562306a36Sopenharmony_ci sec_high = (seconds >> 16) & 0xffff; 257662306a36Sopenharmony_ci nsec_low = nano_seconds & 0xffff; 257762306a36Sopenharmony_ci nsec_high = (nano_seconds >> 16) & 0x3fff; 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, sec_low); 258062306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, sec_high); 258162306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, nsec_low); 258262306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, nsec_high); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_LOAD_); 258562306a36Sopenharmony_ci} 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_cistatic void lan8814_ptp_clock_get(struct phy_device *phydev, 258862306a36Sopenharmony_ci u32 *seconds, u32 *nano_seconds) 258962306a36Sopenharmony_ci{ 259062306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_); 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci *seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID); 259362306a36Sopenharmony_ci *seconds = (*seconds << 16) | 259462306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO); 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci *nano_seconds = lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI); 259762306a36Sopenharmony_ci *nano_seconds = ((*nano_seconds & 0x3fff) << 16) | 259862306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO); 259962306a36Sopenharmony_ci} 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_cistatic int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci, 260262306a36Sopenharmony_ci struct timespec64 *ts) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, 260562306a36Sopenharmony_ci ptp_clock_info); 260662306a36Sopenharmony_ci struct phy_device *phydev = shared->phydev; 260762306a36Sopenharmony_ci u32 nano_seconds; 260862306a36Sopenharmony_ci u32 seconds; 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci mutex_lock(&shared->shared_lock); 261162306a36Sopenharmony_ci lan8814_ptp_clock_get(phydev, &seconds, &nano_seconds); 261262306a36Sopenharmony_ci mutex_unlock(&shared->shared_lock); 261362306a36Sopenharmony_ci ts->tv_sec = seconds; 261462306a36Sopenharmony_ci ts->tv_nsec = nano_seconds; 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci return 0; 261762306a36Sopenharmony_ci} 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_cistatic int lan8814_ptpci_settime64(struct ptp_clock_info *ptpci, 262062306a36Sopenharmony_ci const struct timespec64 *ts) 262162306a36Sopenharmony_ci{ 262262306a36Sopenharmony_ci struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, 262362306a36Sopenharmony_ci ptp_clock_info); 262462306a36Sopenharmony_ci struct phy_device *phydev = shared->phydev; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci mutex_lock(&shared->shared_lock); 262762306a36Sopenharmony_ci lan8814_ptp_clock_set(phydev, ts->tv_sec, ts->tv_nsec); 262862306a36Sopenharmony_ci mutex_unlock(&shared->shared_lock); 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci return 0; 263162306a36Sopenharmony_ci} 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_cistatic void lan8814_ptp_clock_step(struct phy_device *phydev, 263462306a36Sopenharmony_ci s64 time_step_ns) 263562306a36Sopenharmony_ci{ 263662306a36Sopenharmony_ci u32 nano_seconds_step; 263762306a36Sopenharmony_ci u64 abs_time_step_ns; 263862306a36Sopenharmony_ci u32 unsigned_seconds; 263962306a36Sopenharmony_ci u32 nano_seconds; 264062306a36Sopenharmony_ci u32 remainder; 264162306a36Sopenharmony_ci s32 seconds; 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci if (time_step_ns > 15000000000LL) { 264462306a36Sopenharmony_ci /* convert to clock set */ 264562306a36Sopenharmony_ci lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); 264662306a36Sopenharmony_ci unsigned_seconds += div_u64_rem(time_step_ns, 1000000000LL, 264762306a36Sopenharmony_ci &remainder); 264862306a36Sopenharmony_ci nano_seconds += remainder; 264962306a36Sopenharmony_ci if (nano_seconds >= 1000000000) { 265062306a36Sopenharmony_ci unsigned_seconds++; 265162306a36Sopenharmony_ci nano_seconds -= 1000000000; 265262306a36Sopenharmony_ci } 265362306a36Sopenharmony_ci lan8814_ptp_clock_set(phydev, unsigned_seconds, nano_seconds); 265462306a36Sopenharmony_ci return; 265562306a36Sopenharmony_ci } else if (time_step_ns < -15000000000LL) { 265662306a36Sopenharmony_ci /* convert to clock set */ 265762306a36Sopenharmony_ci time_step_ns = -time_step_ns; 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci lan8814_ptp_clock_get(phydev, &unsigned_seconds, &nano_seconds); 266062306a36Sopenharmony_ci unsigned_seconds -= div_u64_rem(time_step_ns, 1000000000LL, 266162306a36Sopenharmony_ci &remainder); 266262306a36Sopenharmony_ci nano_seconds_step = remainder; 266362306a36Sopenharmony_ci if (nano_seconds < nano_seconds_step) { 266462306a36Sopenharmony_ci unsigned_seconds--; 266562306a36Sopenharmony_ci nano_seconds += 1000000000; 266662306a36Sopenharmony_ci } 266762306a36Sopenharmony_ci nano_seconds -= nano_seconds_step; 266862306a36Sopenharmony_ci lan8814_ptp_clock_set(phydev, unsigned_seconds, 266962306a36Sopenharmony_ci nano_seconds); 267062306a36Sopenharmony_ci return; 267162306a36Sopenharmony_ci } 267262306a36Sopenharmony_ci 267362306a36Sopenharmony_ci /* do clock step */ 267462306a36Sopenharmony_ci if (time_step_ns >= 0) { 267562306a36Sopenharmony_ci abs_time_step_ns = (u64)time_step_ns; 267662306a36Sopenharmony_ci seconds = (s32)div_u64_rem(abs_time_step_ns, 1000000000, 267762306a36Sopenharmony_ci &remainder); 267862306a36Sopenharmony_ci nano_seconds = remainder; 267962306a36Sopenharmony_ci } else { 268062306a36Sopenharmony_ci abs_time_step_ns = (u64)(-time_step_ns); 268162306a36Sopenharmony_ci seconds = -((s32)div_u64_rem(abs_time_step_ns, 1000000000, 268262306a36Sopenharmony_ci &remainder)); 268362306a36Sopenharmony_ci nano_seconds = remainder; 268462306a36Sopenharmony_ci if (nano_seconds > 0) { 268562306a36Sopenharmony_ci /* subtracting nano seconds is not allowed 268662306a36Sopenharmony_ci * convert to subtracting from seconds, 268762306a36Sopenharmony_ci * and adding to nanoseconds 268862306a36Sopenharmony_ci */ 268962306a36Sopenharmony_ci seconds--; 269062306a36Sopenharmony_ci nano_seconds = (1000000000 - nano_seconds); 269162306a36Sopenharmony_ci } 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci if (nano_seconds > 0) { 269562306a36Sopenharmony_ci /* add 8 ns to cover the likely normal increment */ 269662306a36Sopenharmony_ci nano_seconds += 8; 269762306a36Sopenharmony_ci } 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci if (nano_seconds >= 1000000000) { 270062306a36Sopenharmony_ci /* carry into seconds */ 270162306a36Sopenharmony_ci seconds++; 270262306a36Sopenharmony_ci nano_seconds -= 1000000000; 270362306a36Sopenharmony_ci } 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci while (seconds) { 270662306a36Sopenharmony_ci if (seconds > 0) { 270762306a36Sopenharmony_ci u32 adjustment_value = (u32)seconds; 270862306a36Sopenharmony_ci u16 adjustment_value_lo, adjustment_value_hi; 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci if (adjustment_value > 0xF) 271162306a36Sopenharmony_ci adjustment_value = 0xF; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci adjustment_value_lo = adjustment_value & 0xffff; 271462306a36Sopenharmony_ci adjustment_value_hi = (adjustment_value >> 16) & 0x3fff; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO, 271762306a36Sopenharmony_ci adjustment_value_lo); 271862306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI, 271962306a36Sopenharmony_ci PTP_LTC_STEP_ADJ_DIR_ | 272062306a36Sopenharmony_ci adjustment_value_hi); 272162306a36Sopenharmony_ci seconds -= ((s32)adjustment_value); 272262306a36Sopenharmony_ci } else { 272362306a36Sopenharmony_ci u32 adjustment_value = (u32)(-seconds); 272462306a36Sopenharmony_ci u16 adjustment_value_lo, adjustment_value_hi; 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci if (adjustment_value > 0xF) 272762306a36Sopenharmony_ci adjustment_value = 0xF; 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci adjustment_value_lo = adjustment_value & 0xffff; 273062306a36Sopenharmony_ci adjustment_value_hi = (adjustment_value >> 16) & 0x3fff; 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO, 273362306a36Sopenharmony_ci adjustment_value_lo); 273462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI, 273562306a36Sopenharmony_ci adjustment_value_hi); 273662306a36Sopenharmony_ci seconds += ((s32)adjustment_value); 273762306a36Sopenharmony_ci } 273862306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, 273962306a36Sopenharmony_ci PTP_CMD_CTL_PTP_LTC_STEP_SEC_); 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci if (nano_seconds) { 274262306a36Sopenharmony_ci u16 nano_seconds_lo; 274362306a36Sopenharmony_ci u16 nano_seconds_hi; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci nano_seconds_lo = nano_seconds & 0xffff; 274662306a36Sopenharmony_ci nano_seconds_hi = (nano_seconds >> 16) & 0x3fff; 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO, 274962306a36Sopenharmony_ci nano_seconds_lo); 275062306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI, 275162306a36Sopenharmony_ci PTP_LTC_STEP_ADJ_DIR_ | 275262306a36Sopenharmony_ci nano_seconds_hi); 275362306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CMD_CTL, 275462306a36Sopenharmony_ci PTP_CMD_CTL_PTP_LTC_STEP_NSEC_); 275562306a36Sopenharmony_ci } 275662306a36Sopenharmony_ci} 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_cistatic int lan8814_ptpci_adjtime(struct ptp_clock_info *ptpci, s64 delta) 275962306a36Sopenharmony_ci{ 276062306a36Sopenharmony_ci struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, 276162306a36Sopenharmony_ci ptp_clock_info); 276262306a36Sopenharmony_ci struct phy_device *phydev = shared->phydev; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci mutex_lock(&shared->shared_lock); 276562306a36Sopenharmony_ci lan8814_ptp_clock_step(phydev, delta); 276662306a36Sopenharmony_ci mutex_unlock(&shared->shared_lock); 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci return 0; 276962306a36Sopenharmony_ci} 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_cistatic int lan8814_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm) 277262306a36Sopenharmony_ci{ 277362306a36Sopenharmony_ci struct lan8814_shared_priv *shared = container_of(ptpci, struct lan8814_shared_priv, 277462306a36Sopenharmony_ci ptp_clock_info); 277562306a36Sopenharmony_ci struct phy_device *phydev = shared->phydev; 277662306a36Sopenharmony_ci u16 kszphy_rate_adj_lo, kszphy_rate_adj_hi; 277762306a36Sopenharmony_ci bool positive = true; 277862306a36Sopenharmony_ci u32 kszphy_rate_adj; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci if (scaled_ppm < 0) { 278162306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 278262306a36Sopenharmony_ci positive = false; 278362306a36Sopenharmony_ci } 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci kszphy_rate_adj = LAN8814_1PPM_FORMAT * (scaled_ppm >> 16); 278662306a36Sopenharmony_ci kszphy_rate_adj += (LAN8814_1PPM_FORMAT * (0xffff & scaled_ppm)) >> 16; 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci kszphy_rate_adj_lo = kszphy_rate_adj & 0xffff; 278962306a36Sopenharmony_ci kszphy_rate_adj_hi = (kszphy_rate_adj >> 16) & 0x3fff; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci if (positive) 279262306a36Sopenharmony_ci kszphy_rate_adj_hi |= PTP_CLOCK_RATE_ADJ_DIR_; 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci mutex_lock(&shared->shared_lock); 279562306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_HI, kszphy_rate_adj_hi); 279662306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_LO, kszphy_rate_adj_lo); 279762306a36Sopenharmony_ci mutex_unlock(&shared->shared_lock); 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci return 0; 280062306a36Sopenharmony_ci} 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_cistatic void lan8814_get_sig_tx(struct sk_buff *skb, u16 *sig) 280362306a36Sopenharmony_ci{ 280462306a36Sopenharmony_ci struct ptp_header *ptp_header; 280562306a36Sopenharmony_ci u32 type; 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_ci type = ptp_classify_raw(skb); 280862306a36Sopenharmony_ci ptp_header = ptp_parse_header(skb, type); 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci *sig = (__force u16)(ntohs(ptp_header->sequence_id)); 281162306a36Sopenharmony_ci} 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_cistatic void lan8814_match_tx_skb(struct kszphy_ptp_priv *ptp_priv, 281462306a36Sopenharmony_ci u32 seconds, u32 nsec, u16 seq_id) 281562306a36Sopenharmony_ci{ 281662306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 281762306a36Sopenharmony_ci struct sk_buff *skb, *skb_tmp; 281862306a36Sopenharmony_ci unsigned long flags; 281962306a36Sopenharmony_ci bool ret = false; 282062306a36Sopenharmony_ci u16 skb_sig; 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->tx_queue.lock, flags); 282362306a36Sopenharmony_ci skb_queue_walk_safe(&ptp_priv->tx_queue, skb, skb_tmp) { 282462306a36Sopenharmony_ci lan8814_get_sig_tx(skb, &skb_sig); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci if (memcmp(&skb_sig, &seq_id, sizeof(seq_id))) 282762306a36Sopenharmony_ci continue; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci __skb_unlink(skb, &ptp_priv->tx_queue); 283062306a36Sopenharmony_ci ret = true; 283162306a36Sopenharmony_ci break; 283262306a36Sopenharmony_ci } 283362306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->tx_queue.lock, flags); 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci if (ret) { 283662306a36Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 283762306a36Sopenharmony_ci shhwtstamps.hwtstamp = ktime_set(seconds, nsec); 283862306a36Sopenharmony_ci skb_complete_tx_timestamp(skb, &shhwtstamps); 283962306a36Sopenharmony_ci } 284062306a36Sopenharmony_ci} 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_cistatic void lan8814_dequeue_tx_skb(struct kszphy_ptp_priv *ptp_priv) 284362306a36Sopenharmony_ci{ 284462306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 284562306a36Sopenharmony_ci u32 seconds, nsec; 284662306a36Sopenharmony_ci u16 seq_id; 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_ci lan8814_ptp_tx_ts_get(phydev, &seconds, &nsec, &seq_id); 284962306a36Sopenharmony_ci lan8814_match_tx_skb(ptp_priv, seconds, nsec, seq_id); 285062306a36Sopenharmony_ci} 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_cistatic void lan8814_get_tx_ts(struct kszphy_ptp_priv *ptp_priv) 285362306a36Sopenharmony_ci{ 285462306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 285562306a36Sopenharmony_ci u32 reg; 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci do { 285862306a36Sopenharmony_ci lan8814_dequeue_tx_skb(ptp_priv); 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci /* If other timestamps are available in the FIFO, 286162306a36Sopenharmony_ci * process them. 286262306a36Sopenharmony_ci */ 286362306a36Sopenharmony_ci reg = lanphy_read_page_reg(phydev, 5, PTP_CAP_INFO); 286462306a36Sopenharmony_ci } while (PTP_CAP_INFO_TX_TS_CNT_GET_(reg) > 0); 286562306a36Sopenharmony_ci} 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_cistatic bool lan8814_match_skb(struct kszphy_ptp_priv *ptp_priv, 286862306a36Sopenharmony_ci struct lan8814_ptp_rx_ts *rx_ts) 286962306a36Sopenharmony_ci{ 287062306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 287162306a36Sopenharmony_ci struct sk_buff *skb, *skb_tmp; 287262306a36Sopenharmony_ci unsigned long flags; 287362306a36Sopenharmony_ci bool ret = false; 287462306a36Sopenharmony_ci u16 skb_sig; 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->rx_queue.lock, flags); 287762306a36Sopenharmony_ci skb_queue_walk_safe(&ptp_priv->rx_queue, skb, skb_tmp) { 287862306a36Sopenharmony_ci lan8814_get_sig_rx(skb, &skb_sig); 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci if (memcmp(&skb_sig, &rx_ts->seq_id, sizeof(rx_ts->seq_id))) 288162306a36Sopenharmony_ci continue; 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci __skb_unlink(skb, &ptp_priv->rx_queue); 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci ret = true; 288662306a36Sopenharmony_ci break; 288762306a36Sopenharmony_ci } 288862306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->rx_queue.lock, flags); 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci if (ret) { 289162306a36Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 289262306a36Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 289362306a36Sopenharmony_ci shhwtstamps->hwtstamp = ktime_set(rx_ts->seconds, rx_ts->nsec); 289462306a36Sopenharmony_ci netif_rx(skb); 289562306a36Sopenharmony_ci } 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci return ret; 289862306a36Sopenharmony_ci} 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_cistatic void lan8814_match_rx_ts(struct kszphy_ptp_priv *ptp_priv, 290162306a36Sopenharmony_ci struct lan8814_ptp_rx_ts *rx_ts) 290262306a36Sopenharmony_ci{ 290362306a36Sopenharmony_ci unsigned long flags; 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci /* If we failed to match the skb add it to the queue for when 290662306a36Sopenharmony_ci * the frame will come 290762306a36Sopenharmony_ci */ 290862306a36Sopenharmony_ci if (!lan8814_match_skb(ptp_priv, rx_ts)) { 290962306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->rx_ts_lock, flags); 291062306a36Sopenharmony_ci list_add(&rx_ts->list, &ptp_priv->rx_ts_list); 291162306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->rx_ts_lock, flags); 291262306a36Sopenharmony_ci } else { 291362306a36Sopenharmony_ci kfree(rx_ts); 291462306a36Sopenharmony_ci } 291562306a36Sopenharmony_ci} 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_cistatic void lan8814_get_rx_ts(struct kszphy_ptp_priv *ptp_priv) 291862306a36Sopenharmony_ci{ 291962306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 292062306a36Sopenharmony_ci struct lan8814_ptp_rx_ts *rx_ts; 292162306a36Sopenharmony_ci u32 reg; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci do { 292462306a36Sopenharmony_ci rx_ts = kzalloc(sizeof(*rx_ts), GFP_KERNEL); 292562306a36Sopenharmony_ci if (!rx_ts) 292662306a36Sopenharmony_ci return; 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci lan8814_ptp_rx_ts_get(phydev, &rx_ts->seconds, &rx_ts->nsec, 292962306a36Sopenharmony_ci &rx_ts->seq_id); 293062306a36Sopenharmony_ci lan8814_match_rx_ts(ptp_priv, rx_ts); 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci /* If other timestamps are available in the FIFO, 293362306a36Sopenharmony_ci * process them. 293462306a36Sopenharmony_ci */ 293562306a36Sopenharmony_ci reg = lanphy_read_page_reg(phydev, 5, PTP_CAP_INFO); 293662306a36Sopenharmony_ci } while (PTP_CAP_INFO_RX_TS_CNT_GET_(reg) > 0); 293762306a36Sopenharmony_ci} 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_cistatic void lan8814_handle_ptp_interrupt(struct phy_device *phydev, u16 status) 294062306a36Sopenharmony_ci{ 294162306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 294262306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = &priv->ptp_priv; 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci if (status & PTP_TSU_INT_STS_PTP_TX_TS_EN_) 294562306a36Sopenharmony_ci lan8814_get_tx_ts(ptp_priv); 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci if (status & PTP_TSU_INT_STS_PTP_RX_TS_EN_) 294862306a36Sopenharmony_ci lan8814_get_rx_ts(ptp_priv); 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci if (status & PTP_TSU_INT_STS_PTP_TX_TS_OVRFL_INT_) { 295162306a36Sopenharmony_ci lan8814_flush_fifo(phydev, true); 295262306a36Sopenharmony_ci skb_queue_purge(&ptp_priv->tx_queue); 295362306a36Sopenharmony_ci } 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci if (status & PTP_TSU_INT_STS_PTP_RX_TS_OVRFL_INT_) { 295662306a36Sopenharmony_ci lan8814_flush_fifo(phydev, false); 295762306a36Sopenharmony_ci skb_queue_purge(&ptp_priv->rx_queue); 295862306a36Sopenharmony_ci } 295962306a36Sopenharmony_ci} 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_cistatic int lan8804_config_init(struct phy_device *phydev) 296262306a36Sopenharmony_ci{ 296362306a36Sopenharmony_ci int val; 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci /* MDI-X setting for swap A,B transmit */ 296662306a36Sopenharmony_ci val = lanphy_read_page_reg(phydev, 2, LAN8804_ALIGN_SWAP); 296762306a36Sopenharmony_ci val &= ~LAN8804_ALIGN_TX_A_B_SWAP_MASK; 296862306a36Sopenharmony_ci val |= LAN8804_ALIGN_TX_A_B_SWAP; 296962306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 2, LAN8804_ALIGN_SWAP, val); 297062306a36Sopenharmony_ci 297162306a36Sopenharmony_ci /* Make sure that the PHY will not stop generating the clock when the 297262306a36Sopenharmony_ci * link partner goes down 297362306a36Sopenharmony_ci */ 297462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 31, LAN8814_CLOCK_MANAGEMENT, 0x27e); 297562306a36Sopenharmony_ci lanphy_read_page_reg(phydev, 1, LAN8814_LINK_QUALITY); 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci return 0; 297862306a36Sopenharmony_ci} 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_cistatic irqreturn_t lan8804_handle_interrupt(struct phy_device *phydev) 298162306a36Sopenharmony_ci{ 298262306a36Sopenharmony_ci int status; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci status = phy_read(phydev, LAN8814_INTS); 298562306a36Sopenharmony_ci if (status < 0) { 298662306a36Sopenharmony_ci phy_error(phydev); 298762306a36Sopenharmony_ci return IRQ_NONE; 298862306a36Sopenharmony_ci } 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci if (status > 0) 299162306a36Sopenharmony_ci phy_trigger_machine(phydev); 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci return IRQ_HANDLED; 299462306a36Sopenharmony_ci} 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci#define LAN8804_OUTPUT_CONTROL 25 299762306a36Sopenharmony_ci#define LAN8804_OUTPUT_CONTROL_INTR_BUFFER BIT(14) 299862306a36Sopenharmony_ci#define LAN8804_CONTROL 31 299962306a36Sopenharmony_ci#define LAN8804_CONTROL_INTR_POLARITY BIT(14) 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_cistatic int lan8804_config_intr(struct phy_device *phydev) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci int err; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci /* This is an internal PHY of lan966x and is not possible to change the 300662306a36Sopenharmony_ci * polarity on the GIC found in lan966x, therefore change the polarity 300762306a36Sopenharmony_ci * of the interrupt in the PHY from being active low instead of active 300862306a36Sopenharmony_ci * high. 300962306a36Sopenharmony_ci */ 301062306a36Sopenharmony_ci phy_write(phydev, LAN8804_CONTROL, LAN8804_CONTROL_INTR_POLARITY); 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci /* By default interrupt buffer is open-drain in which case the interrupt 301362306a36Sopenharmony_ci * can be active only low. Therefore change the interrupt buffer to be 301462306a36Sopenharmony_ci * push-pull to be able to change interrupt polarity 301562306a36Sopenharmony_ci */ 301662306a36Sopenharmony_ci phy_write(phydev, LAN8804_OUTPUT_CONTROL, 301762306a36Sopenharmony_ci LAN8804_OUTPUT_CONTROL_INTR_BUFFER); 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 302062306a36Sopenharmony_ci err = phy_read(phydev, LAN8814_INTS); 302162306a36Sopenharmony_ci if (err < 0) 302262306a36Sopenharmony_ci return err; 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci err = phy_write(phydev, LAN8814_INTC, LAN8814_INT_LINK); 302562306a36Sopenharmony_ci if (err) 302662306a36Sopenharmony_ci return err; 302762306a36Sopenharmony_ci } else { 302862306a36Sopenharmony_ci err = phy_write(phydev, LAN8814_INTC, 0); 302962306a36Sopenharmony_ci if (err) 303062306a36Sopenharmony_ci return err; 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci err = phy_read(phydev, LAN8814_INTS); 303362306a36Sopenharmony_ci if (err < 0) 303462306a36Sopenharmony_ci return err; 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_ci return 0; 303862306a36Sopenharmony_ci} 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_cistatic irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev) 304162306a36Sopenharmony_ci{ 304262306a36Sopenharmony_ci int ret = IRQ_NONE; 304362306a36Sopenharmony_ci int irq_status; 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci irq_status = phy_read(phydev, LAN8814_INTS); 304662306a36Sopenharmony_ci if (irq_status < 0) { 304762306a36Sopenharmony_ci phy_error(phydev); 304862306a36Sopenharmony_ci return IRQ_NONE; 304962306a36Sopenharmony_ci } 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci if (irq_status & LAN8814_INT_LINK) { 305262306a36Sopenharmony_ci phy_trigger_machine(phydev); 305362306a36Sopenharmony_ci ret = IRQ_HANDLED; 305462306a36Sopenharmony_ci } 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci while (true) { 305762306a36Sopenharmony_ci irq_status = lanphy_read_page_reg(phydev, 5, PTP_TSU_INT_STS); 305862306a36Sopenharmony_ci if (!irq_status) 305962306a36Sopenharmony_ci break; 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci lan8814_handle_ptp_interrupt(phydev, irq_status); 306262306a36Sopenharmony_ci ret = IRQ_HANDLED; 306362306a36Sopenharmony_ci } 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci return ret; 306662306a36Sopenharmony_ci} 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_cistatic int lan8814_ack_interrupt(struct phy_device *phydev) 306962306a36Sopenharmony_ci{ 307062306a36Sopenharmony_ci /* bit[12..0] int status, which is a read and clear register. */ 307162306a36Sopenharmony_ci int rc; 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci rc = phy_read(phydev, LAN8814_INTS); 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci return (rc < 0) ? rc : 0; 307662306a36Sopenharmony_ci} 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_cistatic int lan8814_config_intr(struct phy_device *phydev) 307962306a36Sopenharmony_ci{ 308062306a36Sopenharmony_ci int err; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, LAN8814_INTR_CTRL_REG, 308362306a36Sopenharmony_ci LAN8814_INTR_CTRL_REG_POLARITY | 308462306a36Sopenharmony_ci LAN8814_INTR_CTRL_REG_INTR_ENABLE); 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci /* enable / disable interrupts */ 308762306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 308862306a36Sopenharmony_ci err = lan8814_ack_interrupt(phydev); 308962306a36Sopenharmony_ci if (err) 309062306a36Sopenharmony_ci return err; 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_ci err = phy_write(phydev, LAN8814_INTC, LAN8814_INT_LINK); 309362306a36Sopenharmony_ci } else { 309462306a36Sopenharmony_ci err = phy_write(phydev, LAN8814_INTC, 0); 309562306a36Sopenharmony_ci if (err) 309662306a36Sopenharmony_ci return err; 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci err = lan8814_ack_interrupt(phydev); 309962306a36Sopenharmony_ci } 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci return err; 310262306a36Sopenharmony_ci} 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_cistatic void lan8814_ptp_init(struct phy_device *phydev) 310562306a36Sopenharmony_ci{ 310662306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 310762306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = &priv->ptp_priv; 310862306a36Sopenharmony_ci u32 temp; 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PTP_1588_CLOCK) || 311162306a36Sopenharmony_ci !IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING)) 311262306a36Sopenharmony_ci return; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, TSU_HARD_RESET, TSU_HARD_RESET_); 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ci temp = lanphy_read_page_reg(phydev, 5, PTP_TX_MOD); 311762306a36Sopenharmony_ci temp |= PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_; 311862306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_TX_MOD, temp); 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci temp = lanphy_read_page_reg(phydev, 5, PTP_RX_MOD); 312162306a36Sopenharmony_ci temp |= PTP_RX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_; 312262306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_RX_MOD, temp); 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_CONFIG, 0); 312562306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_CONFIG, 0); 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci /* Removing default registers configs related to L2 and IP */ 312862306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_L2_ADDR_EN, 0); 312962306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_L2_ADDR_EN, 0); 313062306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_TX_PARSE_IP_ADDR_EN, 0); 313162306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_RX_PARSE_IP_ADDR_EN, 0); 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci /* Disable checking for minorVersionPTP field */ 313462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_RX_VERSION, 313562306a36Sopenharmony_ci PTP_MAX_VERSION(0xff) | PTP_MIN_VERSION(0x0)); 313662306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, PTP_TX_VERSION, 313762306a36Sopenharmony_ci PTP_MAX_VERSION(0xff) | PTP_MIN_VERSION(0x0)); 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci skb_queue_head_init(&ptp_priv->tx_queue); 314062306a36Sopenharmony_ci skb_queue_head_init(&ptp_priv->rx_queue); 314162306a36Sopenharmony_ci INIT_LIST_HEAD(&ptp_priv->rx_ts_list); 314262306a36Sopenharmony_ci spin_lock_init(&ptp_priv->rx_ts_lock); 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci ptp_priv->phydev = phydev; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci ptp_priv->mii_ts.rxtstamp = lan8814_rxtstamp; 314762306a36Sopenharmony_ci ptp_priv->mii_ts.txtstamp = lan8814_txtstamp; 314862306a36Sopenharmony_ci ptp_priv->mii_ts.hwtstamp = lan8814_hwtstamp; 314962306a36Sopenharmony_ci ptp_priv->mii_ts.ts_info = lan8814_ts_info; 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci phydev->mii_ts = &ptp_priv->mii_ts; 315262306a36Sopenharmony_ci} 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_cistatic int lan8814_ptp_probe_once(struct phy_device *phydev) 315562306a36Sopenharmony_ci{ 315662306a36Sopenharmony_ci struct lan8814_shared_priv *shared = phydev->shared->priv; 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_ci /* Initialise shared lock for clock*/ 315962306a36Sopenharmony_ci mutex_init(&shared->shared_lock); 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci shared->ptp_clock_info.owner = THIS_MODULE; 316262306a36Sopenharmony_ci snprintf(shared->ptp_clock_info.name, 30, "%s", phydev->drv->name); 316362306a36Sopenharmony_ci shared->ptp_clock_info.max_adj = 31249999; 316462306a36Sopenharmony_ci shared->ptp_clock_info.n_alarm = 0; 316562306a36Sopenharmony_ci shared->ptp_clock_info.n_ext_ts = 0; 316662306a36Sopenharmony_ci shared->ptp_clock_info.n_pins = 0; 316762306a36Sopenharmony_ci shared->ptp_clock_info.pps = 0; 316862306a36Sopenharmony_ci shared->ptp_clock_info.pin_config = NULL; 316962306a36Sopenharmony_ci shared->ptp_clock_info.adjfine = lan8814_ptpci_adjfine; 317062306a36Sopenharmony_ci shared->ptp_clock_info.adjtime = lan8814_ptpci_adjtime; 317162306a36Sopenharmony_ci shared->ptp_clock_info.gettime64 = lan8814_ptpci_gettime64; 317262306a36Sopenharmony_ci shared->ptp_clock_info.settime64 = lan8814_ptpci_settime64; 317362306a36Sopenharmony_ci shared->ptp_clock_info.getcrosststamp = NULL; 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_ci shared->ptp_clock = ptp_clock_register(&shared->ptp_clock_info, 317662306a36Sopenharmony_ci &phydev->mdio.dev); 317762306a36Sopenharmony_ci if (IS_ERR(shared->ptp_clock)) { 317862306a36Sopenharmony_ci phydev_err(phydev, "ptp_clock_register failed %lu\n", 317962306a36Sopenharmony_ci PTR_ERR(shared->ptp_clock)); 318062306a36Sopenharmony_ci return -EINVAL; 318162306a36Sopenharmony_ci } 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci /* Check if PHC support is missing at the configuration level */ 318462306a36Sopenharmony_ci if (!shared->ptp_clock) 318562306a36Sopenharmony_ci return 0; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci phydev_dbg(phydev, "successfully registered ptp clock\n"); 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci shared->phydev = phydev; 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci /* The EP.4 is shared between all the PHYs in the package and also it 319262306a36Sopenharmony_ci * can be accessed by any of the PHYs 319362306a36Sopenharmony_ci */ 319462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, LTC_HARD_RESET, LTC_HARD_RESET_); 319562306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, PTP_OPERATING_MODE, 319662306a36Sopenharmony_ci PTP_OPERATING_MODE_STANDALONE_); 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci return 0; 319962306a36Sopenharmony_ci} 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_cistatic void lan8814_setup_led(struct phy_device *phydev, int val) 320262306a36Sopenharmony_ci{ 320362306a36Sopenharmony_ci int temp; 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci temp = lanphy_read_page_reg(phydev, 5, LAN8814_LED_CTRL_1); 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci if (val) 320862306a36Sopenharmony_ci temp |= LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_; 320962306a36Sopenharmony_ci else 321062306a36Sopenharmony_ci temp &= ~LAN8814_LED_CTRL_1_KSZ9031_LED_MODE_; 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, LAN8814_LED_CTRL_1, temp); 321362306a36Sopenharmony_ci} 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_cistatic int lan8814_config_init(struct phy_device *phydev) 321662306a36Sopenharmony_ci{ 321762306a36Sopenharmony_ci struct kszphy_priv *lan8814 = phydev->priv; 321862306a36Sopenharmony_ci int val; 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci /* Reset the PHY */ 322162306a36Sopenharmony_ci val = lanphy_read_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET); 322262306a36Sopenharmony_ci val |= LAN8814_QSGMII_SOFT_RESET_BIT; 322362306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 4, LAN8814_QSGMII_SOFT_RESET, val); 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci /* Disable ANEG with QSGMII PCS Host side */ 322662306a36Sopenharmony_ci val = lanphy_read_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG); 322762306a36Sopenharmony_ci val &= ~LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA; 322862306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 5, LAN8814_QSGMII_PCS1G_ANEG_CONFIG, val); 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ci /* MDI-X setting for swap A,B transmit */ 323162306a36Sopenharmony_ci val = lanphy_read_page_reg(phydev, 2, LAN8814_ALIGN_SWAP); 323262306a36Sopenharmony_ci val &= ~LAN8814_ALIGN_TX_A_B_SWAP_MASK; 323362306a36Sopenharmony_ci val |= LAN8814_ALIGN_TX_A_B_SWAP; 323462306a36Sopenharmony_ci lanphy_write_page_reg(phydev, 2, LAN8814_ALIGN_SWAP, val); 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci if (lan8814->led_mode >= 0) 323762306a36Sopenharmony_ci lan8814_setup_led(phydev, lan8814->led_mode); 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci return 0; 324062306a36Sopenharmony_ci} 324162306a36Sopenharmony_ci 324262306a36Sopenharmony_ci/* It is expected that there will not be any 'lan8814_take_coma_mode' 324362306a36Sopenharmony_ci * function called in suspend. Because the GPIO line can be shared, so if one of 324462306a36Sopenharmony_ci * the phys goes back in coma mode, then all the other PHYs will go, which is 324562306a36Sopenharmony_ci * wrong. 324662306a36Sopenharmony_ci */ 324762306a36Sopenharmony_cistatic int lan8814_release_coma_mode(struct phy_device *phydev) 324862306a36Sopenharmony_ci{ 324962306a36Sopenharmony_ci struct gpio_desc *gpiod; 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci gpiod = devm_gpiod_get_optional(&phydev->mdio.dev, "coma-mode", 325262306a36Sopenharmony_ci GPIOD_OUT_HIGH_OPEN_DRAIN | 325362306a36Sopenharmony_ci GPIOD_FLAGS_BIT_NONEXCLUSIVE); 325462306a36Sopenharmony_ci if (IS_ERR(gpiod)) 325562306a36Sopenharmony_ci return PTR_ERR(gpiod); 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci gpiod_set_consumer_name(gpiod, "LAN8814 coma mode"); 325862306a36Sopenharmony_ci gpiod_set_value_cansleep(gpiod, 0); 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci return 0; 326162306a36Sopenharmony_ci} 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_cistatic int lan8814_probe(struct phy_device *phydev) 326462306a36Sopenharmony_ci{ 326562306a36Sopenharmony_ci const struct kszphy_type *type = phydev->drv->driver_data; 326662306a36Sopenharmony_ci struct kszphy_priv *priv; 326762306a36Sopenharmony_ci u16 addr; 326862306a36Sopenharmony_ci int err; 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); 327162306a36Sopenharmony_ci if (!priv) 327262306a36Sopenharmony_ci return -ENOMEM; 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci phydev->priv = priv; 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ci priv->type = type; 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci kszphy_parse_led_mode(phydev); 327962306a36Sopenharmony_ci 328062306a36Sopenharmony_ci /* Strap-in value for PHY address, below register read gives starting 328162306a36Sopenharmony_ci * phy address value 328262306a36Sopenharmony_ci */ 328362306a36Sopenharmony_ci addr = lanphy_read_page_reg(phydev, 4, 0) & 0x1F; 328462306a36Sopenharmony_ci devm_phy_package_join(&phydev->mdio.dev, phydev, 328562306a36Sopenharmony_ci addr, sizeof(struct lan8814_shared_priv)); 328662306a36Sopenharmony_ci 328762306a36Sopenharmony_ci if (phy_package_init_once(phydev)) { 328862306a36Sopenharmony_ci err = lan8814_release_coma_mode(phydev); 328962306a36Sopenharmony_ci if (err) 329062306a36Sopenharmony_ci return err; 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_ci err = lan8814_ptp_probe_once(phydev); 329362306a36Sopenharmony_ci if (err) 329462306a36Sopenharmony_ci return err; 329562306a36Sopenharmony_ci } 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci lan8814_ptp_init(phydev); 329862306a36Sopenharmony_ci 329962306a36Sopenharmony_ci return 0; 330062306a36Sopenharmony_ci} 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci#define LAN8841_MMD_TIMER_REG 0 330362306a36Sopenharmony_ci#define LAN8841_MMD0_REGISTER_17 17 330462306a36Sopenharmony_ci#define LAN8841_MMD0_REGISTER_17_DROP_OPT(x) ((x) & 0x3) 330562306a36Sopenharmony_ci#define LAN8841_MMD0_REGISTER_17_XMIT_TOG_TX_DIS BIT(3) 330662306a36Sopenharmony_ci#define LAN8841_OPERATION_MODE_STRAP_OVERRIDE_LOW_REG 2 330762306a36Sopenharmony_ci#define LAN8841_OPERATION_MODE_STRAP_OVERRIDE_LOW_REG_MAGJACK BIT(14) 330862306a36Sopenharmony_ci#define LAN8841_MMD_ANALOG_REG 28 330962306a36Sopenharmony_ci#define LAN8841_ANALOG_CONTROL_1 1 331062306a36Sopenharmony_ci#define LAN8841_ANALOG_CONTROL_1_PLL_TRIM(x) (((x) & 0x3) << 5) 331162306a36Sopenharmony_ci#define LAN8841_ANALOG_CONTROL_10 13 331262306a36Sopenharmony_ci#define LAN8841_ANALOG_CONTROL_10_PLL_DIV(x) ((x) & 0x3) 331362306a36Sopenharmony_ci#define LAN8841_ANALOG_CONTROL_11 14 331462306a36Sopenharmony_ci#define LAN8841_ANALOG_CONTROL_11_LDO_REF(x) (((x) & 0x7) << 12) 331562306a36Sopenharmony_ci#define LAN8841_TX_LOW_I_CH_C_D_POWER_MANAGMENT 69 331662306a36Sopenharmony_ci#define LAN8841_TX_LOW_I_CH_C_D_POWER_MANAGMENT_VAL 0xbffc 331762306a36Sopenharmony_ci#define LAN8841_BTRX_POWER_DOWN 70 331862306a36Sopenharmony_ci#define LAN8841_BTRX_POWER_DOWN_QBIAS_CH_A BIT(0) 331962306a36Sopenharmony_ci#define LAN8841_BTRX_POWER_DOWN_BTRX_CH_A BIT(1) 332062306a36Sopenharmony_ci#define LAN8841_BTRX_POWER_DOWN_QBIAS_CH_B BIT(2) 332162306a36Sopenharmony_ci#define LAN8841_BTRX_POWER_DOWN_BTRX_CH_B BIT(3) 332262306a36Sopenharmony_ci#define LAN8841_BTRX_POWER_DOWN_BTRX_CH_C BIT(5) 332362306a36Sopenharmony_ci#define LAN8841_BTRX_POWER_DOWN_BTRX_CH_D BIT(7) 332462306a36Sopenharmony_ci#define LAN8841_ADC_CHANNEL_MASK 198 332562306a36Sopenharmony_ci#define LAN8841_PTP_RX_PARSE_L2_ADDR_EN 370 332662306a36Sopenharmony_ci#define LAN8841_PTP_RX_PARSE_IP_ADDR_EN 371 332762306a36Sopenharmony_ci#define LAN8841_PTP_RX_VERSION 374 332862306a36Sopenharmony_ci#define LAN8841_PTP_TX_PARSE_L2_ADDR_EN 434 332962306a36Sopenharmony_ci#define LAN8841_PTP_TX_PARSE_IP_ADDR_EN 435 333062306a36Sopenharmony_ci#define LAN8841_PTP_TX_VERSION 438 333162306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL 256 333262306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL_PTP_ENABLE BIT(2) 333362306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL_PTP_DISABLE BIT(1) 333462306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL_PTP_RESET BIT(0) 333562306a36Sopenharmony_ci#define LAN8841_PTP_RX_PARSE_CONFIG 368 333662306a36Sopenharmony_ci#define LAN8841_PTP_TX_PARSE_CONFIG 432 333762306a36Sopenharmony_ci#define LAN8841_PTP_RX_MODE 381 333862306a36Sopenharmony_ci#define LAN8841_PTP_INSERT_TS_EN BIT(0) 333962306a36Sopenharmony_ci#define LAN8841_PTP_INSERT_TS_32BIT BIT(1) 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_cistatic int lan8841_config_init(struct phy_device *phydev) 334262306a36Sopenharmony_ci{ 334362306a36Sopenharmony_ci int ret; 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci ret = ksz9131_config_init(phydev); 334662306a36Sopenharmony_ci if (ret) 334762306a36Sopenharmony_ci return ret; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci /* Initialize the HW by resetting everything */ 335062306a36Sopenharmony_ci phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 335162306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL, 335262306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_RESET, 335362306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_RESET); 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 335662306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL, 335762306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_ENABLE, 335862306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_ENABLE); 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci /* Don't process any frames */ 336162306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 336262306a36Sopenharmony_ci LAN8841_PTP_RX_PARSE_CONFIG, 0); 336362306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 336462306a36Sopenharmony_ci LAN8841_PTP_TX_PARSE_CONFIG, 0); 336562306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 336662306a36Sopenharmony_ci LAN8841_PTP_TX_PARSE_L2_ADDR_EN, 0); 336762306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 336862306a36Sopenharmony_ci LAN8841_PTP_RX_PARSE_L2_ADDR_EN, 0); 336962306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 337062306a36Sopenharmony_ci LAN8841_PTP_TX_PARSE_IP_ADDR_EN, 0); 337162306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 337262306a36Sopenharmony_ci LAN8841_PTP_RX_PARSE_IP_ADDR_EN, 0); 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci /* Disable checking for minorVersionPTP field */ 337562306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 337662306a36Sopenharmony_ci LAN8841_PTP_RX_VERSION, 0xff00); 337762306a36Sopenharmony_ci phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 337862306a36Sopenharmony_ci LAN8841_PTP_TX_VERSION, 0xff00); 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci /* 100BT Clause 40 improvenent errata */ 338162306a36Sopenharmony_ci phy_write_mmd(phydev, LAN8841_MMD_ANALOG_REG, 338262306a36Sopenharmony_ci LAN8841_ANALOG_CONTROL_1, 338362306a36Sopenharmony_ci LAN8841_ANALOG_CONTROL_1_PLL_TRIM(0x2)); 338462306a36Sopenharmony_ci phy_write_mmd(phydev, LAN8841_MMD_ANALOG_REG, 338562306a36Sopenharmony_ci LAN8841_ANALOG_CONTROL_10, 338662306a36Sopenharmony_ci LAN8841_ANALOG_CONTROL_10_PLL_DIV(0x1)); 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ci /* 10M/100M Ethernet Signal Tuning Errata for Shorted-Center Tap 338962306a36Sopenharmony_ci * Magnetics 339062306a36Sopenharmony_ci */ 339162306a36Sopenharmony_ci ret = phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 339262306a36Sopenharmony_ci LAN8841_OPERATION_MODE_STRAP_OVERRIDE_LOW_REG); 339362306a36Sopenharmony_ci if (ret & LAN8841_OPERATION_MODE_STRAP_OVERRIDE_LOW_REG_MAGJACK) { 339462306a36Sopenharmony_ci phy_write_mmd(phydev, LAN8841_MMD_ANALOG_REG, 339562306a36Sopenharmony_ci LAN8841_TX_LOW_I_CH_C_D_POWER_MANAGMENT, 339662306a36Sopenharmony_ci LAN8841_TX_LOW_I_CH_C_D_POWER_MANAGMENT_VAL); 339762306a36Sopenharmony_ci phy_write_mmd(phydev, LAN8841_MMD_ANALOG_REG, 339862306a36Sopenharmony_ci LAN8841_BTRX_POWER_DOWN, 339962306a36Sopenharmony_ci LAN8841_BTRX_POWER_DOWN_QBIAS_CH_A | 340062306a36Sopenharmony_ci LAN8841_BTRX_POWER_DOWN_BTRX_CH_A | 340162306a36Sopenharmony_ci LAN8841_BTRX_POWER_DOWN_QBIAS_CH_B | 340262306a36Sopenharmony_ci LAN8841_BTRX_POWER_DOWN_BTRX_CH_B | 340362306a36Sopenharmony_ci LAN8841_BTRX_POWER_DOWN_BTRX_CH_C | 340462306a36Sopenharmony_ci LAN8841_BTRX_POWER_DOWN_BTRX_CH_D); 340562306a36Sopenharmony_ci } 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci /* LDO Adjustment errata */ 340862306a36Sopenharmony_ci phy_write_mmd(phydev, LAN8841_MMD_ANALOG_REG, 340962306a36Sopenharmony_ci LAN8841_ANALOG_CONTROL_11, 341062306a36Sopenharmony_ci LAN8841_ANALOG_CONTROL_11_LDO_REF(1)); 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_ci /* 100BT RGMII latency tuning errata */ 341362306a36Sopenharmony_ci phy_write_mmd(phydev, MDIO_MMD_PMAPMD, 341462306a36Sopenharmony_ci LAN8841_ADC_CHANNEL_MASK, 0x0); 341562306a36Sopenharmony_ci phy_write_mmd(phydev, LAN8841_MMD_TIMER_REG, 341662306a36Sopenharmony_ci LAN8841_MMD0_REGISTER_17, 341762306a36Sopenharmony_ci LAN8841_MMD0_REGISTER_17_DROP_OPT(2) | 341862306a36Sopenharmony_ci LAN8841_MMD0_REGISTER_17_XMIT_TOG_TX_DIS); 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci return 0; 342162306a36Sopenharmony_ci} 342262306a36Sopenharmony_ci 342362306a36Sopenharmony_ci#define LAN8841_OUTPUT_CTRL 25 342462306a36Sopenharmony_ci#define LAN8841_OUTPUT_CTRL_INT_BUFFER BIT(14) 342562306a36Sopenharmony_ci#define LAN8841_INT_PTP BIT(9) 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_cistatic int lan8841_config_intr(struct phy_device *phydev) 342862306a36Sopenharmony_ci{ 342962306a36Sopenharmony_ci int err; 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_ci phy_modify(phydev, LAN8841_OUTPUT_CTRL, 343262306a36Sopenharmony_ci LAN8841_OUTPUT_CTRL_INT_BUFFER, 0); 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 343562306a36Sopenharmony_ci err = phy_read(phydev, LAN8814_INTS); 343662306a36Sopenharmony_ci if (err) 343762306a36Sopenharmony_ci return err; 343862306a36Sopenharmony_ci 343962306a36Sopenharmony_ci /* Enable / disable interrupts. It is OK to enable PTP interrupt 344062306a36Sopenharmony_ci * even if it PTP is not enabled. Because the underneath blocks 344162306a36Sopenharmony_ci * will not enable the PTP so we will never get the PTP 344262306a36Sopenharmony_ci * interrupt. 344362306a36Sopenharmony_ci */ 344462306a36Sopenharmony_ci err = phy_write(phydev, LAN8814_INTC, 344562306a36Sopenharmony_ci LAN8814_INT_LINK | LAN8841_INT_PTP); 344662306a36Sopenharmony_ci } else { 344762306a36Sopenharmony_ci err = phy_write(phydev, LAN8814_INTC, 0); 344862306a36Sopenharmony_ci if (err) 344962306a36Sopenharmony_ci return err; 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_ci err = phy_read(phydev, LAN8814_INTS); 345262306a36Sopenharmony_ci } 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci return err; 345562306a36Sopenharmony_ci} 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_ci#define LAN8841_PTP_TX_EGRESS_SEC_LO 453 345862306a36Sopenharmony_ci#define LAN8841_PTP_TX_EGRESS_SEC_HI 452 345962306a36Sopenharmony_ci#define LAN8841_PTP_TX_EGRESS_NS_LO 451 346062306a36Sopenharmony_ci#define LAN8841_PTP_TX_EGRESS_NS_HI 450 346162306a36Sopenharmony_ci#define LAN8841_PTP_TX_EGRESS_NSEC_HI_VALID BIT(15) 346262306a36Sopenharmony_ci#define LAN8841_PTP_TX_MSG_HEADER2 455 346362306a36Sopenharmony_ci 346462306a36Sopenharmony_cistatic bool lan8841_ptp_get_tx_ts(struct kszphy_ptp_priv *ptp_priv, 346562306a36Sopenharmony_ci u32 *sec, u32 *nsec, u16 *seq) 346662306a36Sopenharmony_ci{ 346762306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci *nsec = phy_read_mmd(phydev, 2, LAN8841_PTP_TX_EGRESS_NS_HI); 347062306a36Sopenharmony_ci if (!(*nsec & LAN8841_PTP_TX_EGRESS_NSEC_HI_VALID)) 347162306a36Sopenharmony_ci return false; 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci *nsec = ((*nsec & 0x3fff) << 16); 347462306a36Sopenharmony_ci *nsec = *nsec | phy_read_mmd(phydev, 2, LAN8841_PTP_TX_EGRESS_NS_LO); 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci *sec = phy_read_mmd(phydev, 2, LAN8841_PTP_TX_EGRESS_SEC_HI); 347762306a36Sopenharmony_ci *sec = *sec << 16; 347862306a36Sopenharmony_ci *sec = *sec | phy_read_mmd(phydev, 2, LAN8841_PTP_TX_EGRESS_SEC_LO); 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_ci *seq = phy_read_mmd(phydev, 2, LAN8841_PTP_TX_MSG_HEADER2); 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci return true; 348362306a36Sopenharmony_ci} 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_cistatic void lan8841_ptp_process_tx_ts(struct kszphy_ptp_priv *ptp_priv) 348662306a36Sopenharmony_ci{ 348762306a36Sopenharmony_ci u32 sec, nsec; 348862306a36Sopenharmony_ci u16 seq; 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_ci while (lan8841_ptp_get_tx_ts(ptp_priv, &sec, &nsec, &seq)) 349162306a36Sopenharmony_ci lan8814_match_tx_skb(ptp_priv, sec, nsec, seq); 349262306a36Sopenharmony_ci} 349362306a36Sopenharmony_ci 349462306a36Sopenharmony_ci#define LAN8841_PTP_INT_STS 259 349562306a36Sopenharmony_ci#define LAN8841_PTP_INT_STS_PTP_TX_TS_OVRFL_INT BIT(13) 349662306a36Sopenharmony_ci#define LAN8841_PTP_INT_STS_PTP_TX_TS_INT BIT(12) 349762306a36Sopenharmony_ci#define LAN8841_PTP_INT_STS_PTP_GPIO_CAP_INT BIT(2) 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_cistatic void lan8841_ptp_flush_fifo(struct kszphy_ptp_priv *ptp_priv) 350062306a36Sopenharmony_ci{ 350162306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 350262306a36Sopenharmony_ci int i; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci for (i = 0; i < FIFO_SIZE; ++i) 350562306a36Sopenharmony_ci phy_read_mmd(phydev, 2, LAN8841_PTP_TX_MSG_HEADER2); 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci phy_read_mmd(phydev, 2, LAN8841_PTP_INT_STS); 350862306a36Sopenharmony_ci} 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_CAP_STS 506 351162306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_SEL 327 351262306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_SEL_GPIO_SEL(gpio) ((gpio) << 8) 351362306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_RE_LTC_SEC_HI_CAP 498 351462306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_RE_LTC_SEC_LO_CAP 499 351562306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_RE_LTC_NS_HI_CAP 500 351662306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_RE_LTC_NS_LO_CAP 501 351762306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_FE_LTC_SEC_HI_CAP 502 351862306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_FE_LTC_SEC_LO_CAP 503 351962306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_FE_LTC_NS_HI_CAP 504 352062306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_FE_LTC_NS_LO_CAP 505 352162306a36Sopenharmony_ci 352262306a36Sopenharmony_cistatic void lan8841_gpio_process_cap(struct kszphy_ptp_priv *ptp_priv) 352362306a36Sopenharmony_ci{ 352462306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 352562306a36Sopenharmony_ci struct ptp_clock_event ptp_event = {0}; 352662306a36Sopenharmony_ci int pin, ret, tmp; 352762306a36Sopenharmony_ci s32 sec, nsec; 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_ci pin = ptp_find_pin_unlocked(ptp_priv->ptp_clock, PTP_PF_EXTTS, 0); 353062306a36Sopenharmony_ci if (pin == -1) 353162306a36Sopenharmony_ci return; 353262306a36Sopenharmony_ci 353362306a36Sopenharmony_ci tmp = phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_CAP_STS); 353462306a36Sopenharmony_ci if (tmp < 0) 353562306a36Sopenharmony_ci return; 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_GPIO_SEL, 353862306a36Sopenharmony_ci LAN8841_PTP_GPIO_SEL_GPIO_SEL(pin)); 353962306a36Sopenharmony_ci if (ret) 354062306a36Sopenharmony_ci return; 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 354362306a36Sopenharmony_ci if (tmp & BIT(pin)) { 354462306a36Sopenharmony_ci sec = phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_RE_LTC_SEC_HI_CAP); 354562306a36Sopenharmony_ci sec <<= 16; 354662306a36Sopenharmony_ci sec |= phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_RE_LTC_SEC_LO_CAP); 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci nsec = phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_RE_LTC_NS_HI_CAP) & 0x3fff; 354962306a36Sopenharmony_ci nsec <<= 16; 355062306a36Sopenharmony_ci nsec |= phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_RE_LTC_NS_LO_CAP); 355162306a36Sopenharmony_ci } else { 355262306a36Sopenharmony_ci sec = phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_FE_LTC_SEC_HI_CAP); 355362306a36Sopenharmony_ci sec <<= 16; 355462306a36Sopenharmony_ci sec |= phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_FE_LTC_SEC_LO_CAP); 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci nsec = phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_FE_LTC_NS_HI_CAP) & 0x3fff; 355762306a36Sopenharmony_ci nsec <<= 16; 355862306a36Sopenharmony_ci nsec |= phy_read_mmd(phydev, 2, LAN8841_PTP_GPIO_FE_LTC_NS_LO_CAP); 355962306a36Sopenharmony_ci } 356062306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 356162306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_GPIO_SEL, 0); 356262306a36Sopenharmony_ci if (ret) 356362306a36Sopenharmony_ci return; 356462306a36Sopenharmony_ci 356562306a36Sopenharmony_ci ptp_event.index = 0; 356662306a36Sopenharmony_ci ptp_event.timestamp = ktime_set(sec, nsec); 356762306a36Sopenharmony_ci ptp_event.type = PTP_CLOCK_EXTTS; 356862306a36Sopenharmony_ci ptp_clock_event(ptp_priv->ptp_clock, &ptp_event); 356962306a36Sopenharmony_ci} 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_cistatic void lan8841_handle_ptp_interrupt(struct phy_device *phydev) 357262306a36Sopenharmony_ci{ 357362306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 357462306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = &priv->ptp_priv; 357562306a36Sopenharmony_ci u16 status; 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci do { 357862306a36Sopenharmony_ci status = phy_read_mmd(phydev, 2, LAN8841_PTP_INT_STS); 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci if (status & LAN8841_PTP_INT_STS_PTP_TX_TS_INT) 358162306a36Sopenharmony_ci lan8841_ptp_process_tx_ts(ptp_priv); 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_ci if (status & LAN8841_PTP_INT_STS_PTP_GPIO_CAP_INT) 358462306a36Sopenharmony_ci lan8841_gpio_process_cap(ptp_priv); 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci if (status & LAN8841_PTP_INT_STS_PTP_TX_TS_OVRFL_INT) { 358762306a36Sopenharmony_ci lan8841_ptp_flush_fifo(ptp_priv); 358862306a36Sopenharmony_ci skb_queue_purge(&ptp_priv->tx_queue); 358962306a36Sopenharmony_ci } 359062306a36Sopenharmony_ci 359162306a36Sopenharmony_ci } while (status & (LAN8841_PTP_INT_STS_PTP_TX_TS_INT | 359262306a36Sopenharmony_ci LAN8841_PTP_INT_STS_PTP_GPIO_CAP_INT | 359362306a36Sopenharmony_ci LAN8841_PTP_INT_STS_PTP_TX_TS_OVRFL_INT)); 359462306a36Sopenharmony_ci} 359562306a36Sopenharmony_ci 359662306a36Sopenharmony_ci#define LAN8841_INTS_PTP BIT(9) 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_cistatic irqreturn_t lan8841_handle_interrupt(struct phy_device *phydev) 359962306a36Sopenharmony_ci{ 360062306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 360162306a36Sopenharmony_ci int irq_status; 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci irq_status = phy_read(phydev, LAN8814_INTS); 360462306a36Sopenharmony_ci if (irq_status < 0) { 360562306a36Sopenharmony_ci phy_error(phydev); 360662306a36Sopenharmony_ci return IRQ_NONE; 360762306a36Sopenharmony_ci } 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_ci if (irq_status & LAN8814_INT_LINK) { 361062306a36Sopenharmony_ci phy_trigger_machine(phydev); 361162306a36Sopenharmony_ci ret = IRQ_HANDLED; 361262306a36Sopenharmony_ci } 361362306a36Sopenharmony_ci 361462306a36Sopenharmony_ci if (irq_status & LAN8841_INTS_PTP) { 361562306a36Sopenharmony_ci lan8841_handle_ptp_interrupt(phydev); 361662306a36Sopenharmony_ci ret = IRQ_HANDLED; 361762306a36Sopenharmony_ci } 361862306a36Sopenharmony_ci 361962306a36Sopenharmony_ci return ret; 362062306a36Sopenharmony_ci} 362162306a36Sopenharmony_ci 362262306a36Sopenharmony_cistatic int lan8841_ts_info(struct mii_timestamper *mii_ts, 362362306a36Sopenharmony_ci struct ethtool_ts_info *info) 362462306a36Sopenharmony_ci{ 362562306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_ci info->phc_index = ptp_priv->ptp_clock ? 363062306a36Sopenharmony_ci ptp_clock_index(ptp_priv->ptp_clock) : -1; 363162306a36Sopenharmony_ci if (info->phc_index == -1) 363262306a36Sopenharmony_ci return 0; 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ci info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | 363562306a36Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 363662306a36Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_ci info->tx_types = (1 << HWTSTAMP_TX_OFF) | 363962306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ON) | 364062306a36Sopenharmony_ci (1 << HWTSTAMP_TX_ONESTEP_SYNC); 364162306a36Sopenharmony_ci 364262306a36Sopenharmony_ci info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | 364362306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | 364462306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | 364562306a36Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); 364662306a36Sopenharmony_ci 364762306a36Sopenharmony_ci return 0; 364862306a36Sopenharmony_ci} 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci#define LAN8841_PTP_INT_EN 260 365162306a36Sopenharmony_ci#define LAN8841_PTP_INT_EN_PTP_TX_TS_OVRFL_EN BIT(13) 365262306a36Sopenharmony_ci#define LAN8841_PTP_INT_EN_PTP_TX_TS_EN BIT(12) 365362306a36Sopenharmony_ci 365462306a36Sopenharmony_cistatic void lan8841_ptp_enable_processing(struct kszphy_ptp_priv *ptp_priv, 365562306a36Sopenharmony_ci bool enable) 365662306a36Sopenharmony_ci{ 365762306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci if (enable) { 366062306a36Sopenharmony_ci /* Enable interrupts on the TX side */ 366162306a36Sopenharmony_ci phy_modify_mmd(phydev, 2, LAN8841_PTP_INT_EN, 366262306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_TX_TS_OVRFL_EN | 366362306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_TX_TS_EN, 366462306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_TX_TS_OVRFL_EN | 366562306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_TX_TS_EN); 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci /* Enable the modification of the frame on RX side, 366862306a36Sopenharmony_ci * this will add the ns and 2 bits of sec in the reserved field 366962306a36Sopenharmony_ci * of the PTP header 367062306a36Sopenharmony_ci */ 367162306a36Sopenharmony_ci phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 367262306a36Sopenharmony_ci LAN8841_PTP_RX_MODE, 367362306a36Sopenharmony_ci LAN8841_PTP_INSERT_TS_EN | 367462306a36Sopenharmony_ci LAN8841_PTP_INSERT_TS_32BIT, 367562306a36Sopenharmony_ci LAN8841_PTP_INSERT_TS_EN | 367662306a36Sopenharmony_ci LAN8841_PTP_INSERT_TS_32BIT); 367762306a36Sopenharmony_ci 367862306a36Sopenharmony_ci ptp_schedule_worker(ptp_priv->ptp_clock, 0); 367962306a36Sopenharmony_ci } else { 368062306a36Sopenharmony_ci /* Disable interrupts on the TX side */ 368162306a36Sopenharmony_ci phy_modify_mmd(phydev, 2, LAN8841_PTP_INT_EN, 368262306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_TX_TS_OVRFL_EN | 368362306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_TX_TS_EN, 0); 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci /* Disable modification of the RX frames */ 368662306a36Sopenharmony_ci phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 368762306a36Sopenharmony_ci LAN8841_PTP_RX_MODE, 368862306a36Sopenharmony_ci LAN8841_PTP_INSERT_TS_EN | 368962306a36Sopenharmony_ci LAN8841_PTP_INSERT_TS_32BIT, 0); 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_ci ptp_cancel_worker_sync(ptp_priv->ptp_clock); 369262306a36Sopenharmony_ci } 369362306a36Sopenharmony_ci} 369462306a36Sopenharmony_ci 369562306a36Sopenharmony_ci#define LAN8841_PTP_RX_TIMESTAMP_EN 379 369662306a36Sopenharmony_ci#define LAN8841_PTP_TX_TIMESTAMP_EN 443 369762306a36Sopenharmony_ci#define LAN8841_PTP_TX_MOD 445 369862306a36Sopenharmony_ci 369962306a36Sopenharmony_cistatic int lan8841_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr) 370062306a36Sopenharmony_ci{ 370162306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); 370262306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 370362306a36Sopenharmony_ci struct hwtstamp_config config; 370462306a36Sopenharmony_ci int txcfg = 0, rxcfg = 0; 370562306a36Sopenharmony_ci int pkt_ts_enable; 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) 370862306a36Sopenharmony_ci return -EFAULT; 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_ci ptp_priv->hwts_tx_type = config.tx_type; 371162306a36Sopenharmony_ci ptp_priv->rx_filter = config.rx_filter; 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci switch (config.rx_filter) { 371462306a36Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 371562306a36Sopenharmony_ci ptp_priv->layer = 0; 371662306a36Sopenharmony_ci ptp_priv->version = 0; 371762306a36Sopenharmony_ci break; 371862306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 371962306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 372062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 372162306a36Sopenharmony_ci ptp_priv->layer = PTP_CLASS_L4; 372262306a36Sopenharmony_ci ptp_priv->version = PTP_CLASS_V2; 372362306a36Sopenharmony_ci break; 372462306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 372562306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 372662306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 372762306a36Sopenharmony_ci ptp_priv->layer = PTP_CLASS_L2; 372862306a36Sopenharmony_ci ptp_priv->version = PTP_CLASS_V2; 372962306a36Sopenharmony_ci break; 373062306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 373162306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 373262306a36Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 373362306a36Sopenharmony_ci ptp_priv->layer = PTP_CLASS_L4 | PTP_CLASS_L2; 373462306a36Sopenharmony_ci ptp_priv->version = PTP_CLASS_V2; 373562306a36Sopenharmony_ci break; 373662306a36Sopenharmony_ci default: 373762306a36Sopenharmony_ci return -ERANGE; 373862306a36Sopenharmony_ci } 373962306a36Sopenharmony_ci 374062306a36Sopenharmony_ci /* Setup parsing of the frames and enable the timestamping for ptp 374162306a36Sopenharmony_ci * frames 374262306a36Sopenharmony_ci */ 374362306a36Sopenharmony_ci if (ptp_priv->layer & PTP_CLASS_L2) { 374462306a36Sopenharmony_ci rxcfg |= PTP_RX_PARSE_CONFIG_LAYER2_EN_; 374562306a36Sopenharmony_ci txcfg |= PTP_TX_PARSE_CONFIG_LAYER2_EN_; 374662306a36Sopenharmony_ci } else if (ptp_priv->layer & PTP_CLASS_L4) { 374762306a36Sopenharmony_ci rxcfg |= PTP_RX_PARSE_CONFIG_IPV4_EN_ | PTP_RX_PARSE_CONFIG_IPV6_EN_; 374862306a36Sopenharmony_ci txcfg |= PTP_TX_PARSE_CONFIG_IPV4_EN_ | PTP_TX_PARSE_CONFIG_IPV6_EN_; 374962306a36Sopenharmony_ci } 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_RX_PARSE_CONFIG, rxcfg); 375262306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_TX_PARSE_CONFIG, txcfg); 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci pkt_ts_enable = PTP_TIMESTAMP_EN_SYNC_ | PTP_TIMESTAMP_EN_DREQ_ | 375562306a36Sopenharmony_ci PTP_TIMESTAMP_EN_PDREQ_ | PTP_TIMESTAMP_EN_PDRES_; 375662306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_RX_TIMESTAMP_EN, pkt_ts_enable); 375762306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_TX_TIMESTAMP_EN, pkt_ts_enable); 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_ci /* Enable / disable of the TX timestamp in the SYNC frames */ 376062306a36Sopenharmony_ci phy_modify_mmd(phydev, 2, LAN8841_PTP_TX_MOD, 376162306a36Sopenharmony_ci PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_, 376262306a36Sopenharmony_ci ptp_priv->hwts_tx_type == HWTSTAMP_TX_ONESTEP_SYNC ? 376362306a36Sopenharmony_ci PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_ : 0); 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci /* Now enable/disable the timestamping */ 376662306a36Sopenharmony_ci lan8841_ptp_enable_processing(ptp_priv, 376762306a36Sopenharmony_ci config.rx_filter != HWTSTAMP_FILTER_NONE); 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_ci skb_queue_purge(&ptp_priv->tx_queue); 377062306a36Sopenharmony_ci 377162306a36Sopenharmony_ci lan8841_ptp_flush_fifo(ptp_priv); 377262306a36Sopenharmony_ci 377362306a36Sopenharmony_ci return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0; 377462306a36Sopenharmony_ci} 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_cistatic bool lan8841_rxtstamp(struct mii_timestamper *mii_ts, 377762306a36Sopenharmony_ci struct sk_buff *skb, int type) 377862306a36Sopenharmony_ci{ 377962306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = 378062306a36Sopenharmony_ci container_of(mii_ts, struct kszphy_ptp_priv, mii_ts); 378162306a36Sopenharmony_ci struct ptp_header *header = ptp_parse_header(skb, type); 378262306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 378362306a36Sopenharmony_ci struct timespec64 ts; 378462306a36Sopenharmony_ci unsigned long flags; 378562306a36Sopenharmony_ci u32 ts_header; 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci if (!header) 378862306a36Sopenharmony_ci return false; 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci if (ptp_priv->rx_filter == HWTSTAMP_FILTER_NONE || 379162306a36Sopenharmony_ci type == PTP_CLASS_NONE) 379262306a36Sopenharmony_ci return false; 379362306a36Sopenharmony_ci 379462306a36Sopenharmony_ci if ((type & ptp_priv->version) == 0 || (type & ptp_priv->layer) == 0) 379562306a36Sopenharmony_ci return false; 379662306a36Sopenharmony_ci 379762306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->seconds_lock, flags); 379862306a36Sopenharmony_ci ts.tv_sec = ptp_priv->seconds; 379962306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->seconds_lock, flags); 380062306a36Sopenharmony_ci ts_header = __be32_to_cpu(header->reserved2); 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 380362306a36Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci /* Check for any wrap arounds for the second part */ 380662306a36Sopenharmony_ci if ((ts.tv_sec & GENMASK(1, 0)) == 0 && (ts_header >> 30) == 3) 380762306a36Sopenharmony_ci ts.tv_sec -= GENMASK(1, 0) + 1; 380862306a36Sopenharmony_ci else if ((ts.tv_sec & GENMASK(1, 0)) == 3 && (ts_header >> 30) == 0) 380962306a36Sopenharmony_ci ts.tv_sec += 1; 381062306a36Sopenharmony_ci 381162306a36Sopenharmony_ci shhwtstamps->hwtstamp = 381262306a36Sopenharmony_ci ktime_set((ts.tv_sec & ~(GENMASK(1, 0))) | ts_header >> 30, 381362306a36Sopenharmony_ci ts_header & GENMASK(29, 0)); 381462306a36Sopenharmony_ci header->reserved2 = 0; 381562306a36Sopenharmony_ci 381662306a36Sopenharmony_ci netif_rx(skb); 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_ci return true; 381962306a36Sopenharmony_ci} 382062306a36Sopenharmony_ci 382162306a36Sopenharmony_ci#define LAN8841_EVENT_A 0 382262306a36Sopenharmony_ci#define LAN8841_EVENT_B 1 382362306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_SEC_HI(event) ((event) == LAN8841_EVENT_A ? 278 : 288) 382462306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_SEC_LO(event) ((event) == LAN8841_EVENT_A ? 279 : 289) 382562306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_NS_HI(event) ((event) == LAN8841_EVENT_A ? 280 : 290) 382662306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_NS_LO(event) ((event) == LAN8841_EVENT_A ? 281 : 291) 382762306a36Sopenharmony_ci 382862306a36Sopenharmony_cistatic int lan8841_ptp_set_target(struct kszphy_ptp_priv *ptp_priv, u8 event, 382962306a36Sopenharmony_ci s64 sec, u32 nsec) 383062306a36Sopenharmony_ci{ 383162306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 383262306a36Sopenharmony_ci int ret; 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_SEC_HI(event), 383562306a36Sopenharmony_ci upper_16_bits(sec)); 383662306a36Sopenharmony_ci if (ret) 383762306a36Sopenharmony_ci return ret; 383862306a36Sopenharmony_ci 383962306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_SEC_LO(event), 384062306a36Sopenharmony_ci lower_16_bits(sec)); 384162306a36Sopenharmony_ci if (ret) 384262306a36Sopenharmony_ci return ret; 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_NS_HI(event) & 0x3fff, 384562306a36Sopenharmony_ci upper_16_bits(nsec)); 384662306a36Sopenharmony_ci if (ret) 384762306a36Sopenharmony_ci return ret; 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ci return phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_NS_LO(event), 385062306a36Sopenharmony_ci lower_16_bits(nsec)); 385162306a36Sopenharmony_ci} 385262306a36Sopenharmony_ci 385362306a36Sopenharmony_ci#define LAN8841_BUFFER_TIME 2 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_cistatic int lan8841_ptp_update_target(struct kszphy_ptp_priv *ptp_priv, 385662306a36Sopenharmony_ci const struct timespec64 *ts) 385762306a36Sopenharmony_ci{ 385862306a36Sopenharmony_ci return lan8841_ptp_set_target(ptp_priv, LAN8841_EVENT_A, 385962306a36Sopenharmony_ci ts->tv_sec + LAN8841_BUFFER_TIME, 0); 386062306a36Sopenharmony_ci} 386162306a36Sopenharmony_ci 386262306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_RELOAD_SEC_HI(event) ((event) == LAN8841_EVENT_A ? 282 : 292) 386362306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_RELOAD_SEC_LO(event) ((event) == LAN8841_EVENT_A ? 283 : 293) 386462306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_RELOAD_NS_HI(event) ((event) == LAN8841_EVENT_A ? 284 : 294) 386562306a36Sopenharmony_ci#define LAN8841_PTP_LTC_TARGET_RELOAD_NS_LO(event) ((event) == LAN8841_EVENT_A ? 285 : 295) 386662306a36Sopenharmony_ci 386762306a36Sopenharmony_cistatic int lan8841_ptp_set_reload(struct kszphy_ptp_priv *ptp_priv, u8 event, 386862306a36Sopenharmony_ci s64 sec, u32 nsec) 386962306a36Sopenharmony_ci{ 387062306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 387162306a36Sopenharmony_ci int ret; 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_SEC_HI(event), 387462306a36Sopenharmony_ci upper_16_bits(sec)); 387562306a36Sopenharmony_ci if (ret) 387662306a36Sopenharmony_ci return ret; 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_SEC_LO(event), 387962306a36Sopenharmony_ci lower_16_bits(sec)); 388062306a36Sopenharmony_ci if (ret) 388162306a36Sopenharmony_ci return ret; 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_NS_HI(event) & 0x3fff, 388462306a36Sopenharmony_ci upper_16_bits(nsec)); 388562306a36Sopenharmony_ci if (ret) 388662306a36Sopenharmony_ci return ret; 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci return phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_TARGET_RELOAD_NS_LO(event), 388962306a36Sopenharmony_ci lower_16_bits(nsec)); 389062306a36Sopenharmony_ci} 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci#define LAN8841_PTP_LTC_SET_SEC_HI 262 389362306a36Sopenharmony_ci#define LAN8841_PTP_LTC_SET_SEC_MID 263 389462306a36Sopenharmony_ci#define LAN8841_PTP_LTC_SET_SEC_LO 264 389562306a36Sopenharmony_ci#define LAN8841_PTP_LTC_SET_NS_HI 265 389662306a36Sopenharmony_ci#define LAN8841_PTP_LTC_SET_NS_LO 266 389762306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL_PTP_LTC_LOAD BIT(4) 389862306a36Sopenharmony_ci 389962306a36Sopenharmony_cistatic int lan8841_ptp_settime64(struct ptp_clock_info *ptp, 390062306a36Sopenharmony_ci const struct timespec64 *ts) 390162306a36Sopenharmony_ci{ 390262306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 390362306a36Sopenharmony_ci ptp_clock_info); 390462306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 390562306a36Sopenharmony_ci unsigned long flags; 390662306a36Sopenharmony_ci int ret; 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_ci /* Set the value to be stored */ 390962306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 391062306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_SET_SEC_LO, lower_16_bits(ts->tv_sec)); 391162306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_SET_SEC_MID, upper_16_bits(ts->tv_sec)); 391262306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_SET_SEC_HI, upper_32_bits(ts->tv_sec) & 0xffff); 391362306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_SET_NS_LO, lower_16_bits(ts->tv_nsec)); 391462306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_SET_NS_HI, upper_16_bits(ts->tv_nsec) & 0x3fff); 391562306a36Sopenharmony_ci 391662306a36Sopenharmony_ci /* Set the command to load the LTC */ 391762306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_CMD_CTL, 391862306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_LTC_LOAD); 391962306a36Sopenharmony_ci ret = lan8841_ptp_update_target(ptp_priv, ts); 392062306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 392162306a36Sopenharmony_ci 392262306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->seconds_lock, flags); 392362306a36Sopenharmony_ci ptp_priv->seconds = ts->tv_sec; 392462306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->seconds_lock, flags); 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci return ret; 392762306a36Sopenharmony_ci} 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RD_SEC_HI 358 393062306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RD_SEC_MID 359 393162306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RD_SEC_LO 360 393262306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RD_NS_HI 361 393362306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RD_NS_LO 362 393462306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL_PTP_LTC_READ BIT(3) 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_cistatic int lan8841_ptp_gettime64(struct ptp_clock_info *ptp, 393762306a36Sopenharmony_ci struct timespec64 *ts) 393862306a36Sopenharmony_ci{ 393962306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 394062306a36Sopenharmony_ci ptp_clock_info); 394162306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 394262306a36Sopenharmony_ci time64_t s; 394362306a36Sopenharmony_ci s64 ns; 394462306a36Sopenharmony_ci 394562306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 394662306a36Sopenharmony_ci /* Issue the command to read the LTC */ 394762306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_CMD_CTL, 394862306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_LTC_READ); 394962306a36Sopenharmony_ci 395062306a36Sopenharmony_ci /* Read the LTC */ 395162306a36Sopenharmony_ci s = phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_SEC_HI); 395262306a36Sopenharmony_ci s <<= 16; 395362306a36Sopenharmony_ci s |= phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_SEC_MID); 395462306a36Sopenharmony_ci s <<= 16; 395562306a36Sopenharmony_ci s |= phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_SEC_LO); 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_ci ns = phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_NS_HI) & 0x3fff; 395862306a36Sopenharmony_ci ns <<= 16; 395962306a36Sopenharmony_ci ns |= phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_NS_LO); 396062306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 396162306a36Sopenharmony_ci 396262306a36Sopenharmony_ci set_normalized_timespec64(ts, s, ns); 396362306a36Sopenharmony_ci return 0; 396462306a36Sopenharmony_ci} 396562306a36Sopenharmony_ci 396662306a36Sopenharmony_cistatic void lan8841_ptp_getseconds(struct ptp_clock_info *ptp, 396762306a36Sopenharmony_ci struct timespec64 *ts) 396862306a36Sopenharmony_ci{ 396962306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 397062306a36Sopenharmony_ci ptp_clock_info); 397162306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 397262306a36Sopenharmony_ci time64_t s; 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 397562306a36Sopenharmony_ci /* Issue the command to read the LTC */ 397662306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_CMD_CTL, 397762306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_LTC_READ); 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_ci /* Read the LTC */ 398062306a36Sopenharmony_ci s = phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_SEC_HI); 398162306a36Sopenharmony_ci s <<= 16; 398262306a36Sopenharmony_ci s |= phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_SEC_MID); 398362306a36Sopenharmony_ci s <<= 16; 398462306a36Sopenharmony_ci s |= phy_read_mmd(phydev, 2, LAN8841_PTP_LTC_RD_SEC_LO); 398562306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 398662306a36Sopenharmony_ci 398762306a36Sopenharmony_ci set_normalized_timespec64(ts, s, 0); 398862306a36Sopenharmony_ci} 398962306a36Sopenharmony_ci 399062306a36Sopenharmony_ci#define LAN8841_PTP_LTC_STEP_ADJ_LO 276 399162306a36Sopenharmony_ci#define LAN8841_PTP_LTC_STEP_ADJ_HI 275 399262306a36Sopenharmony_ci#define LAN8841_PTP_LTC_STEP_ADJ_DIR BIT(15) 399362306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL_PTP_LTC_STEP_SECONDS BIT(5) 399462306a36Sopenharmony_ci#define LAN8841_PTP_CMD_CTL_PTP_LTC_STEP_NANOSECONDS BIT(6) 399562306a36Sopenharmony_ci 399662306a36Sopenharmony_cistatic int lan8841_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) 399762306a36Sopenharmony_ci{ 399862306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 399962306a36Sopenharmony_ci ptp_clock_info); 400062306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 400162306a36Sopenharmony_ci struct timespec64 ts; 400262306a36Sopenharmony_ci bool add = true; 400362306a36Sopenharmony_ci u32 nsec; 400462306a36Sopenharmony_ci s32 sec; 400562306a36Sopenharmony_ci int ret; 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_ci /* The HW allows up to 15 sec to adjust the time, but here we limit to 400862306a36Sopenharmony_ci * 10 sec the adjustment. The reason is, in case the adjustment is 14 400962306a36Sopenharmony_ci * sec and 999999999 nsec, then we add 8ns to compansate the actual 401062306a36Sopenharmony_ci * increment so the value can be bigger than 15 sec. Therefore limit the 401162306a36Sopenharmony_ci * possible adjustments so we will not have these corner cases 401262306a36Sopenharmony_ci */ 401362306a36Sopenharmony_ci if (delta > 10000000000LL || delta < -10000000000LL) { 401462306a36Sopenharmony_ci /* The timeadjustment is too big, so fall back using set time */ 401562306a36Sopenharmony_ci u64 now; 401662306a36Sopenharmony_ci 401762306a36Sopenharmony_ci ptp->gettime64(ptp, &ts); 401862306a36Sopenharmony_ci 401962306a36Sopenharmony_ci now = ktime_to_ns(timespec64_to_ktime(ts)); 402062306a36Sopenharmony_ci ts = ns_to_timespec64(now + delta); 402162306a36Sopenharmony_ci 402262306a36Sopenharmony_ci ptp->settime64(ptp, &ts); 402362306a36Sopenharmony_ci return 0; 402462306a36Sopenharmony_ci } 402562306a36Sopenharmony_ci 402662306a36Sopenharmony_ci sec = div_u64_rem(delta < 0 ? -delta : delta, NSEC_PER_SEC, &nsec); 402762306a36Sopenharmony_ci if (delta < 0 && nsec != 0) { 402862306a36Sopenharmony_ci /* It is not allowed to adjust low the nsec part, therefore 402962306a36Sopenharmony_ci * subtract more from second part and add to nanosecond such 403062306a36Sopenharmony_ci * that would roll over, so the second part will increase 403162306a36Sopenharmony_ci */ 403262306a36Sopenharmony_ci sec--; 403362306a36Sopenharmony_ci nsec = NSEC_PER_SEC - nsec; 403462306a36Sopenharmony_ci } 403562306a36Sopenharmony_ci 403662306a36Sopenharmony_ci /* Calculate the adjustments and the direction */ 403762306a36Sopenharmony_ci if (delta < 0) 403862306a36Sopenharmony_ci add = false; 403962306a36Sopenharmony_ci 404062306a36Sopenharmony_ci if (nsec > 0) 404162306a36Sopenharmony_ci /* add 8 ns to cover the likely normal increment */ 404262306a36Sopenharmony_ci nsec += 8; 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci if (nsec >= NSEC_PER_SEC) { 404562306a36Sopenharmony_ci /* carry into seconds */ 404662306a36Sopenharmony_ci sec++; 404762306a36Sopenharmony_ci nsec -= NSEC_PER_SEC; 404862306a36Sopenharmony_ci } 404962306a36Sopenharmony_ci 405062306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 405162306a36Sopenharmony_ci if (sec) { 405262306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_STEP_ADJ_LO, sec); 405362306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_STEP_ADJ_HI, 405462306a36Sopenharmony_ci add ? LAN8841_PTP_LTC_STEP_ADJ_DIR : 0); 405562306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_CMD_CTL, 405662306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_LTC_STEP_SECONDS); 405762306a36Sopenharmony_ci } 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci if (nsec) { 406062306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_STEP_ADJ_LO, 406162306a36Sopenharmony_ci nsec & 0xffff); 406262306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_STEP_ADJ_HI, 406362306a36Sopenharmony_ci (nsec >> 16) & 0x3fff); 406462306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_CMD_CTL, 406562306a36Sopenharmony_ci LAN8841_PTP_CMD_CTL_PTP_LTC_STEP_NANOSECONDS); 406662306a36Sopenharmony_ci } 406762306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 406862306a36Sopenharmony_ci 406962306a36Sopenharmony_ci /* Update the target clock */ 407062306a36Sopenharmony_ci ptp->gettime64(ptp, &ts); 407162306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 407262306a36Sopenharmony_ci ret = lan8841_ptp_update_target(ptp_priv, &ts); 407362306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 407462306a36Sopenharmony_ci 407562306a36Sopenharmony_ci return ret; 407662306a36Sopenharmony_ci} 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RATE_ADJ_HI 269 407962306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RATE_ADJ_HI_DIR BIT(15) 408062306a36Sopenharmony_ci#define LAN8841_PTP_LTC_RATE_ADJ_LO 270 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_cistatic int lan8841_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 408362306a36Sopenharmony_ci{ 408462306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 408562306a36Sopenharmony_ci ptp_clock_info); 408662306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 408762306a36Sopenharmony_ci bool faster = true; 408862306a36Sopenharmony_ci u32 rate; 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci if (!scaled_ppm) 409162306a36Sopenharmony_ci return 0; 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_ci if (scaled_ppm < 0) { 409462306a36Sopenharmony_ci scaled_ppm = -scaled_ppm; 409562306a36Sopenharmony_ci faster = false; 409662306a36Sopenharmony_ci } 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_ci rate = LAN8814_1PPM_FORMAT * (upper_16_bits(scaled_ppm)); 409962306a36Sopenharmony_ci rate += (LAN8814_1PPM_FORMAT * (lower_16_bits(scaled_ppm))) >> 16; 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 410262306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_RATE_ADJ_HI, 410362306a36Sopenharmony_ci faster ? LAN8841_PTP_LTC_RATE_ADJ_HI_DIR | (upper_16_bits(rate) & 0x3fff) 410462306a36Sopenharmony_ci : upper_16_bits(rate) & 0x3fff); 410562306a36Sopenharmony_ci phy_write_mmd(phydev, 2, LAN8841_PTP_LTC_RATE_ADJ_LO, lower_16_bits(rate)); 410662306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 410762306a36Sopenharmony_ci 410862306a36Sopenharmony_ci return 0; 410962306a36Sopenharmony_ci} 411062306a36Sopenharmony_ci 411162306a36Sopenharmony_cistatic int lan8841_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, 411262306a36Sopenharmony_ci enum ptp_pin_function func, unsigned int chan) 411362306a36Sopenharmony_ci{ 411462306a36Sopenharmony_ci switch (func) { 411562306a36Sopenharmony_ci case PTP_PF_NONE: 411662306a36Sopenharmony_ci case PTP_PF_PEROUT: 411762306a36Sopenharmony_ci case PTP_PF_EXTTS: 411862306a36Sopenharmony_ci break; 411962306a36Sopenharmony_ci default: 412062306a36Sopenharmony_ci return -1; 412162306a36Sopenharmony_ci } 412262306a36Sopenharmony_ci 412362306a36Sopenharmony_ci return 0; 412462306a36Sopenharmony_ci} 412562306a36Sopenharmony_ci 412662306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_NUM 10 412762306a36Sopenharmony_ci#define LAN8841_GPIO_EN 128 412862306a36Sopenharmony_ci#define LAN8841_GPIO_DIR 129 412962306a36Sopenharmony_ci#define LAN8841_GPIO_BUF 130 413062306a36Sopenharmony_ci 413162306a36Sopenharmony_cistatic int lan8841_ptp_perout_off(struct kszphy_ptp_priv *ptp_priv, int pin) 413262306a36Sopenharmony_ci{ 413362306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 413462306a36Sopenharmony_ci int ret; 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_EN, BIT(pin)); 413762306a36Sopenharmony_ci if (ret) 413862306a36Sopenharmony_ci return ret; 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_DIR, BIT(pin)); 414162306a36Sopenharmony_ci if (ret) 414262306a36Sopenharmony_ci return ret; 414362306a36Sopenharmony_ci 414462306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_BUF, BIT(pin)); 414562306a36Sopenharmony_ci} 414662306a36Sopenharmony_ci 414762306a36Sopenharmony_cistatic int lan8841_ptp_perout_on(struct kszphy_ptp_priv *ptp_priv, int pin) 414862306a36Sopenharmony_ci{ 414962306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 415062306a36Sopenharmony_ci int ret; 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_EN, BIT(pin)); 415362306a36Sopenharmony_ci if (ret) 415462306a36Sopenharmony_ci return ret; 415562306a36Sopenharmony_ci 415662306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_DIR, BIT(pin)); 415762306a36Sopenharmony_ci if (ret) 415862306a36Sopenharmony_ci return ret; 415962306a36Sopenharmony_ci 416062306a36Sopenharmony_ci return phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_BUF, BIT(pin)); 416162306a36Sopenharmony_ci} 416262306a36Sopenharmony_ci 416362306a36Sopenharmony_ci#define LAN8841_GPIO_DATA_SEL1 131 416462306a36Sopenharmony_ci#define LAN8841_GPIO_DATA_SEL2 132 416562306a36Sopenharmony_ci#define LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_MASK GENMASK(2, 0) 416662306a36Sopenharmony_ci#define LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_A 1 416762306a36Sopenharmony_ci#define LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_B 2 416862306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG 257 416962306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A BIT(1) 417062306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B BIT(3) 417162306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A_MASK GENMASK(7, 4) 417262306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B_MASK GENMASK(11, 8) 417362306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A 4 417462306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B 7 417562306a36Sopenharmony_ci 417662306a36Sopenharmony_cistatic int lan8841_ptp_remove_event(struct kszphy_ptp_priv *ptp_priv, int pin, 417762306a36Sopenharmony_ci u8 event) 417862306a36Sopenharmony_ci{ 417962306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 418062306a36Sopenharmony_ci u16 tmp; 418162306a36Sopenharmony_ci int ret; 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_ci /* Now remove pin from the event. GPIO_DATA_SEL1 contains the GPIO 418462306a36Sopenharmony_ci * pins 0-4 while GPIO_DATA_SEL2 contains GPIO pins 5-9, therefore 418562306a36Sopenharmony_ci * depending on the pin, it requires to read a different register 418662306a36Sopenharmony_ci */ 418762306a36Sopenharmony_ci if (pin < 5) { 418862306a36Sopenharmony_ci tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_MASK << (3 * pin); 418962306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL1, tmp); 419062306a36Sopenharmony_ci } else { 419162306a36Sopenharmony_ci tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_MASK << (3 * (pin - 5)); 419262306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL2, tmp); 419362306a36Sopenharmony_ci } 419462306a36Sopenharmony_ci if (ret) 419562306a36Sopenharmony_ci return ret; 419662306a36Sopenharmony_ci 419762306a36Sopenharmony_ci /* Disable the event */ 419862306a36Sopenharmony_ci if (event == LAN8841_EVENT_A) 419962306a36Sopenharmony_ci tmp = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A | 420062306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A_MASK; 420162306a36Sopenharmony_ci else 420262306a36Sopenharmony_ci tmp = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B | 420362306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B_MASK; 420462306a36Sopenharmony_ci return phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_EN, tmp); 420562306a36Sopenharmony_ci} 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_cistatic int lan8841_ptp_enable_event(struct kszphy_ptp_priv *ptp_priv, int pin, 420862306a36Sopenharmony_ci u8 event, int pulse_width) 420962306a36Sopenharmony_ci{ 421062306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 421162306a36Sopenharmony_ci u16 tmp; 421262306a36Sopenharmony_ci int ret; 421362306a36Sopenharmony_ci 421462306a36Sopenharmony_ci /* Enable the event */ 421562306a36Sopenharmony_ci if (event == LAN8841_EVENT_A) 421662306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, 2, LAN8841_PTP_GENERAL_CONFIG, 421762306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A | 421862306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A_MASK, 421962306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_A | 422062306a36Sopenharmony_ci pulse_width << LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_A); 422162306a36Sopenharmony_ci else 422262306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, 2, LAN8841_PTP_GENERAL_CONFIG, 422362306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B | 422462306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B_MASK, 422562306a36Sopenharmony_ci LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_POL_B | 422662306a36Sopenharmony_ci pulse_width << LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_B); 422762306a36Sopenharmony_ci if (ret) 422862306a36Sopenharmony_ci return ret; 422962306a36Sopenharmony_ci 423062306a36Sopenharmony_ci /* Now connect the pin to the event. GPIO_DATA_SEL1 contains the GPIO 423162306a36Sopenharmony_ci * pins 0-4 while GPIO_DATA_SEL2 contains GPIO pins 5-9, therefore 423262306a36Sopenharmony_ci * depending on the pin, it requires to read a different register 423362306a36Sopenharmony_ci */ 423462306a36Sopenharmony_ci if (event == LAN8841_EVENT_A) 423562306a36Sopenharmony_ci tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_A; 423662306a36Sopenharmony_ci else 423762306a36Sopenharmony_ci tmp = LAN8841_GPIO_DATA_SEL_GPIO_DATA_SEL_EVENT_B; 423862306a36Sopenharmony_ci 423962306a36Sopenharmony_ci if (pin < 5) 424062306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL1, 424162306a36Sopenharmony_ci tmp << (3 * pin)); 424262306a36Sopenharmony_ci else 424362306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_DATA_SEL2, 424462306a36Sopenharmony_ci tmp << (3 * (pin - 5))); 424562306a36Sopenharmony_ci 424662306a36Sopenharmony_ci return ret; 424762306a36Sopenharmony_ci} 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_200MS 13 425062306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100MS 12 425162306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50MS 11 425262306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10MS 10 425362306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5MS 9 425462306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1MS 8 425562306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500US 7 425662306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100US 6 425762306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50US 5 425862306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10US 4 425962306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5US 3 426062306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1US 2 426162306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500NS 1 426262306a36Sopenharmony_ci#define LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100NS 0 426362306a36Sopenharmony_ci 426462306a36Sopenharmony_cistatic int lan8841_ptp_perout(struct ptp_clock_info *ptp, 426562306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 426662306a36Sopenharmony_ci{ 426762306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 426862306a36Sopenharmony_ci ptp_clock_info); 426962306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 427062306a36Sopenharmony_ci struct timespec64 ts_on, ts_period; 427162306a36Sopenharmony_ci s64 on_nsec, period_nsec; 427262306a36Sopenharmony_ci int pulse_width; 427362306a36Sopenharmony_ci int pin; 427462306a36Sopenharmony_ci int ret; 427562306a36Sopenharmony_ci 427662306a36Sopenharmony_ci if (rq->perout.flags & ~PTP_PEROUT_DUTY_CYCLE) 427762306a36Sopenharmony_ci return -EOPNOTSUPP; 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci pin = ptp_find_pin(ptp_priv->ptp_clock, PTP_PF_PEROUT, rq->perout.index); 428062306a36Sopenharmony_ci if (pin == -1 || pin >= LAN8841_PTP_GPIO_NUM) 428162306a36Sopenharmony_ci return -EINVAL; 428262306a36Sopenharmony_ci 428362306a36Sopenharmony_ci if (!on) { 428462306a36Sopenharmony_ci ret = lan8841_ptp_perout_off(ptp_priv, pin); 428562306a36Sopenharmony_ci if (ret) 428662306a36Sopenharmony_ci return ret; 428762306a36Sopenharmony_ci 428862306a36Sopenharmony_ci return lan8841_ptp_remove_event(ptp_priv, LAN8841_EVENT_A, pin); 428962306a36Sopenharmony_ci } 429062306a36Sopenharmony_ci 429162306a36Sopenharmony_ci ts_on.tv_sec = rq->perout.on.sec; 429262306a36Sopenharmony_ci ts_on.tv_nsec = rq->perout.on.nsec; 429362306a36Sopenharmony_ci on_nsec = timespec64_to_ns(&ts_on); 429462306a36Sopenharmony_ci 429562306a36Sopenharmony_ci ts_period.tv_sec = rq->perout.period.sec; 429662306a36Sopenharmony_ci ts_period.tv_nsec = rq->perout.period.nsec; 429762306a36Sopenharmony_ci period_nsec = timespec64_to_ns(&ts_period); 429862306a36Sopenharmony_ci 429962306a36Sopenharmony_ci if (period_nsec < 200) { 430062306a36Sopenharmony_ci pr_warn_ratelimited("%s: perout period too small, minimum is 200 nsec\n", 430162306a36Sopenharmony_ci phydev_name(phydev)); 430262306a36Sopenharmony_ci return -EOPNOTSUPP; 430362306a36Sopenharmony_ci } 430462306a36Sopenharmony_ci 430562306a36Sopenharmony_ci if (on_nsec >= period_nsec) { 430662306a36Sopenharmony_ci pr_warn_ratelimited("%s: pulse width must be smaller than period\n", 430762306a36Sopenharmony_ci phydev_name(phydev)); 430862306a36Sopenharmony_ci return -EINVAL; 430962306a36Sopenharmony_ci } 431062306a36Sopenharmony_ci 431162306a36Sopenharmony_ci switch (on_nsec) { 431262306a36Sopenharmony_ci case 200000000: 431362306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_200MS; 431462306a36Sopenharmony_ci break; 431562306a36Sopenharmony_ci case 100000000: 431662306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100MS; 431762306a36Sopenharmony_ci break; 431862306a36Sopenharmony_ci case 50000000: 431962306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50MS; 432062306a36Sopenharmony_ci break; 432162306a36Sopenharmony_ci case 10000000: 432262306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10MS; 432362306a36Sopenharmony_ci break; 432462306a36Sopenharmony_ci case 5000000: 432562306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5MS; 432662306a36Sopenharmony_ci break; 432762306a36Sopenharmony_ci case 1000000: 432862306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1MS; 432962306a36Sopenharmony_ci break; 433062306a36Sopenharmony_ci case 500000: 433162306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500US; 433262306a36Sopenharmony_ci break; 433362306a36Sopenharmony_ci case 100000: 433462306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100US; 433562306a36Sopenharmony_ci break; 433662306a36Sopenharmony_ci case 50000: 433762306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_50US; 433862306a36Sopenharmony_ci break; 433962306a36Sopenharmony_ci case 10000: 434062306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_10US; 434162306a36Sopenharmony_ci break; 434262306a36Sopenharmony_ci case 5000: 434362306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_5US; 434462306a36Sopenharmony_ci break; 434562306a36Sopenharmony_ci case 1000: 434662306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_1US; 434762306a36Sopenharmony_ci break; 434862306a36Sopenharmony_ci case 500: 434962306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_500NS; 435062306a36Sopenharmony_ci break; 435162306a36Sopenharmony_ci case 100: 435262306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100NS; 435362306a36Sopenharmony_ci break; 435462306a36Sopenharmony_ci default: 435562306a36Sopenharmony_ci pr_warn_ratelimited("%s: Use default duty cycle of 100ns\n", 435662306a36Sopenharmony_ci phydev_name(phydev)); 435762306a36Sopenharmony_ci pulse_width = LAN8841_PTP_GENERAL_CONFIG_LTC_EVENT_100NS; 435862306a36Sopenharmony_ci break; 435962306a36Sopenharmony_ci } 436062306a36Sopenharmony_ci 436162306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 436262306a36Sopenharmony_ci ret = lan8841_ptp_set_target(ptp_priv, LAN8841_EVENT_A, rq->perout.start.sec, 436362306a36Sopenharmony_ci rq->perout.start.nsec); 436462306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 436562306a36Sopenharmony_ci if (ret) 436662306a36Sopenharmony_ci return ret; 436762306a36Sopenharmony_ci 436862306a36Sopenharmony_ci ret = lan8841_ptp_set_reload(ptp_priv, LAN8841_EVENT_A, rq->perout.period.sec, 436962306a36Sopenharmony_ci rq->perout.period.nsec); 437062306a36Sopenharmony_ci if (ret) 437162306a36Sopenharmony_ci return ret; 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci ret = lan8841_ptp_enable_event(ptp_priv, pin, LAN8841_EVENT_A, 437462306a36Sopenharmony_ci pulse_width); 437562306a36Sopenharmony_ci if (ret) 437662306a36Sopenharmony_ci return ret; 437762306a36Sopenharmony_ci 437862306a36Sopenharmony_ci ret = lan8841_ptp_perout_on(ptp_priv, pin); 437962306a36Sopenharmony_ci if (ret) 438062306a36Sopenharmony_ci lan8841_ptp_remove_event(ptp_priv, pin, LAN8841_EVENT_A); 438162306a36Sopenharmony_ci 438262306a36Sopenharmony_ci return ret; 438362306a36Sopenharmony_ci} 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_CAP_EN 496 438662306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(gpio) (BIT(gpio)) 438762306a36Sopenharmony_ci#define LAN8841_PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(gpio) (BIT(gpio) << 8) 438862306a36Sopenharmony_ci#define LAN8841_PTP_INT_EN_PTP_GPIO_CAP_EN BIT(2) 438962306a36Sopenharmony_ci 439062306a36Sopenharmony_cistatic int lan8841_ptp_extts_on(struct kszphy_ptp_priv *ptp_priv, int pin, 439162306a36Sopenharmony_ci u32 flags) 439262306a36Sopenharmony_ci{ 439362306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 439462306a36Sopenharmony_ci u16 tmp = 0; 439562306a36Sopenharmony_ci int ret; 439662306a36Sopenharmony_ci 439762306a36Sopenharmony_ci /* Set GPIO to be intput */ 439862306a36Sopenharmony_ci ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_EN, BIT(pin)); 439962306a36Sopenharmony_ci if (ret) 440062306a36Sopenharmony_ci return ret; 440162306a36Sopenharmony_ci 440262306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_BUF, BIT(pin)); 440362306a36Sopenharmony_ci if (ret) 440462306a36Sopenharmony_ci return ret; 440562306a36Sopenharmony_ci 440662306a36Sopenharmony_ci /* Enable capture on the edges of the pin */ 440762306a36Sopenharmony_ci if (flags & PTP_RISING_EDGE) 440862306a36Sopenharmony_ci tmp |= LAN8841_PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(pin); 440962306a36Sopenharmony_ci if (flags & PTP_FALLING_EDGE) 441062306a36Sopenharmony_ci tmp |= LAN8841_PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(pin); 441162306a36Sopenharmony_ci ret = phy_write_mmd(phydev, 2, LAN8841_PTP_GPIO_CAP_EN, tmp); 441262306a36Sopenharmony_ci if (ret) 441362306a36Sopenharmony_ci return ret; 441462306a36Sopenharmony_ci 441562306a36Sopenharmony_ci /* Enable interrupt */ 441662306a36Sopenharmony_ci return phy_modify_mmd(phydev, 2, LAN8841_PTP_INT_EN, 441762306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_GPIO_CAP_EN, 441862306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_GPIO_CAP_EN); 441962306a36Sopenharmony_ci} 442062306a36Sopenharmony_ci 442162306a36Sopenharmony_cistatic int lan8841_ptp_extts_off(struct kszphy_ptp_priv *ptp_priv, int pin) 442262306a36Sopenharmony_ci{ 442362306a36Sopenharmony_ci struct phy_device *phydev = ptp_priv->phydev; 442462306a36Sopenharmony_ci int ret; 442562306a36Sopenharmony_ci 442662306a36Sopenharmony_ci /* Set GPIO to be output */ 442762306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_EN, BIT(pin)); 442862306a36Sopenharmony_ci if (ret) 442962306a36Sopenharmony_ci return ret; 443062306a36Sopenharmony_ci 443162306a36Sopenharmony_ci ret = phy_clear_bits_mmd(phydev, 2, LAN8841_GPIO_BUF, BIT(pin)); 443262306a36Sopenharmony_ci if (ret) 443362306a36Sopenharmony_ci return ret; 443462306a36Sopenharmony_ci 443562306a36Sopenharmony_ci /* Disable capture on both of the edges */ 443662306a36Sopenharmony_ci ret = phy_modify_mmd(phydev, 2, LAN8841_PTP_GPIO_CAP_EN, 443762306a36Sopenharmony_ci LAN8841_PTP_GPIO_CAP_EN_GPIO_RE_CAPTURE_ENABLE(pin) | 443862306a36Sopenharmony_ci LAN8841_PTP_GPIO_CAP_EN_GPIO_FE_CAPTURE_ENABLE(pin), 443962306a36Sopenharmony_ci 0); 444062306a36Sopenharmony_ci if (ret) 444162306a36Sopenharmony_ci return ret; 444262306a36Sopenharmony_ci 444362306a36Sopenharmony_ci /* Disable interrupt */ 444462306a36Sopenharmony_ci return phy_modify_mmd(phydev, 2, LAN8841_PTP_INT_EN, 444562306a36Sopenharmony_ci LAN8841_PTP_INT_EN_PTP_GPIO_CAP_EN, 444662306a36Sopenharmony_ci 0); 444762306a36Sopenharmony_ci} 444862306a36Sopenharmony_ci 444962306a36Sopenharmony_cistatic int lan8841_ptp_extts(struct ptp_clock_info *ptp, 445062306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 445162306a36Sopenharmony_ci{ 445262306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 445362306a36Sopenharmony_ci ptp_clock_info); 445462306a36Sopenharmony_ci int pin; 445562306a36Sopenharmony_ci int ret; 445662306a36Sopenharmony_ci 445762306a36Sopenharmony_ci /* Reject requests with unsupported flags */ 445862306a36Sopenharmony_ci if (rq->extts.flags & ~(PTP_ENABLE_FEATURE | 445962306a36Sopenharmony_ci PTP_EXTTS_EDGES | 446062306a36Sopenharmony_ci PTP_STRICT_FLAGS)) 446162306a36Sopenharmony_ci return -EOPNOTSUPP; 446262306a36Sopenharmony_ci 446362306a36Sopenharmony_ci pin = ptp_find_pin(ptp_priv->ptp_clock, PTP_PF_EXTTS, rq->extts.index); 446462306a36Sopenharmony_ci if (pin == -1 || pin >= LAN8841_PTP_GPIO_NUM) 446562306a36Sopenharmony_ci return -EINVAL; 446662306a36Sopenharmony_ci 446762306a36Sopenharmony_ci mutex_lock(&ptp_priv->ptp_lock); 446862306a36Sopenharmony_ci if (on) 446962306a36Sopenharmony_ci ret = lan8841_ptp_extts_on(ptp_priv, pin, rq->extts.flags); 447062306a36Sopenharmony_ci else 447162306a36Sopenharmony_ci ret = lan8841_ptp_extts_off(ptp_priv, pin); 447262306a36Sopenharmony_ci mutex_unlock(&ptp_priv->ptp_lock); 447362306a36Sopenharmony_ci 447462306a36Sopenharmony_ci return ret; 447562306a36Sopenharmony_ci} 447662306a36Sopenharmony_ci 447762306a36Sopenharmony_cistatic int lan8841_ptp_enable(struct ptp_clock_info *ptp, 447862306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 447962306a36Sopenharmony_ci{ 448062306a36Sopenharmony_ci switch (rq->type) { 448162306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 448262306a36Sopenharmony_ci return lan8841_ptp_extts(ptp, rq, on); 448362306a36Sopenharmony_ci case PTP_CLK_REQ_PEROUT: 448462306a36Sopenharmony_ci return lan8841_ptp_perout(ptp, rq, on); 448562306a36Sopenharmony_ci default: 448662306a36Sopenharmony_ci return -EOPNOTSUPP; 448762306a36Sopenharmony_ci } 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci return 0; 449062306a36Sopenharmony_ci} 449162306a36Sopenharmony_ci 449262306a36Sopenharmony_cistatic long lan8841_ptp_do_aux_work(struct ptp_clock_info *ptp) 449362306a36Sopenharmony_ci{ 449462306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = container_of(ptp, struct kszphy_ptp_priv, 449562306a36Sopenharmony_ci ptp_clock_info); 449662306a36Sopenharmony_ci struct timespec64 ts; 449762306a36Sopenharmony_ci unsigned long flags; 449862306a36Sopenharmony_ci 449962306a36Sopenharmony_ci lan8841_ptp_getseconds(&ptp_priv->ptp_clock_info, &ts); 450062306a36Sopenharmony_ci 450162306a36Sopenharmony_ci spin_lock_irqsave(&ptp_priv->seconds_lock, flags); 450262306a36Sopenharmony_ci ptp_priv->seconds = ts.tv_sec; 450362306a36Sopenharmony_ci spin_unlock_irqrestore(&ptp_priv->seconds_lock, flags); 450462306a36Sopenharmony_ci 450562306a36Sopenharmony_ci return nsecs_to_jiffies(LAN8841_GET_SEC_LTC_DELAY); 450662306a36Sopenharmony_ci} 450762306a36Sopenharmony_ci 450862306a36Sopenharmony_cistatic struct ptp_clock_info lan8841_ptp_clock_info = { 450962306a36Sopenharmony_ci .owner = THIS_MODULE, 451062306a36Sopenharmony_ci .name = "lan8841 ptp", 451162306a36Sopenharmony_ci .max_adj = 31249999, 451262306a36Sopenharmony_ci .gettime64 = lan8841_ptp_gettime64, 451362306a36Sopenharmony_ci .settime64 = lan8841_ptp_settime64, 451462306a36Sopenharmony_ci .adjtime = lan8841_ptp_adjtime, 451562306a36Sopenharmony_ci .adjfine = lan8841_ptp_adjfine, 451662306a36Sopenharmony_ci .verify = lan8841_ptp_verify, 451762306a36Sopenharmony_ci .enable = lan8841_ptp_enable, 451862306a36Sopenharmony_ci .do_aux_work = lan8841_ptp_do_aux_work, 451962306a36Sopenharmony_ci .n_per_out = LAN8841_PTP_GPIO_NUM, 452062306a36Sopenharmony_ci .n_ext_ts = LAN8841_PTP_GPIO_NUM, 452162306a36Sopenharmony_ci .n_pins = LAN8841_PTP_GPIO_NUM, 452262306a36Sopenharmony_ci}; 452362306a36Sopenharmony_ci 452462306a36Sopenharmony_ci#define LAN8841_OPERATION_MODE_STRAP_LOW_REGISTER 3 452562306a36Sopenharmony_ci#define LAN8841_OPERATION_MODE_STRAP_LOW_REGISTER_STRAP_RGMII_EN BIT(0) 452662306a36Sopenharmony_ci 452762306a36Sopenharmony_cistatic int lan8841_probe(struct phy_device *phydev) 452862306a36Sopenharmony_ci{ 452962306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv; 453062306a36Sopenharmony_ci struct kszphy_priv *priv; 453162306a36Sopenharmony_ci int err; 453262306a36Sopenharmony_ci 453362306a36Sopenharmony_ci err = kszphy_probe(phydev); 453462306a36Sopenharmony_ci if (err) 453562306a36Sopenharmony_ci return err; 453662306a36Sopenharmony_ci 453762306a36Sopenharmony_ci if (phy_read_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, 453862306a36Sopenharmony_ci LAN8841_OPERATION_MODE_STRAP_LOW_REGISTER) & 453962306a36Sopenharmony_ci LAN8841_OPERATION_MODE_STRAP_LOW_REGISTER_STRAP_RGMII_EN) 454062306a36Sopenharmony_ci phydev->interface = PHY_INTERFACE_MODE_RGMII_RXID; 454162306a36Sopenharmony_ci 454262306a36Sopenharmony_ci /* Register the clock */ 454362306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING)) 454462306a36Sopenharmony_ci return 0; 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_ci priv = phydev->priv; 454762306a36Sopenharmony_ci ptp_priv = &priv->ptp_priv; 454862306a36Sopenharmony_ci 454962306a36Sopenharmony_ci ptp_priv->pin_config = devm_kcalloc(&phydev->mdio.dev, 455062306a36Sopenharmony_ci LAN8841_PTP_GPIO_NUM, 455162306a36Sopenharmony_ci sizeof(*ptp_priv->pin_config), 455262306a36Sopenharmony_ci GFP_KERNEL); 455362306a36Sopenharmony_ci if (!ptp_priv->pin_config) 455462306a36Sopenharmony_ci return -ENOMEM; 455562306a36Sopenharmony_ci 455662306a36Sopenharmony_ci for (int i = 0; i < LAN8841_PTP_GPIO_NUM; ++i) { 455762306a36Sopenharmony_ci struct ptp_pin_desc *p = &ptp_priv->pin_config[i]; 455862306a36Sopenharmony_ci 455962306a36Sopenharmony_ci snprintf(p->name, sizeof(p->name), "pin%d", i); 456062306a36Sopenharmony_ci p->index = i; 456162306a36Sopenharmony_ci p->func = PTP_PF_NONE; 456262306a36Sopenharmony_ci } 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci ptp_priv->ptp_clock_info = lan8841_ptp_clock_info; 456562306a36Sopenharmony_ci ptp_priv->ptp_clock_info.pin_config = ptp_priv->pin_config; 456662306a36Sopenharmony_ci ptp_priv->ptp_clock = ptp_clock_register(&ptp_priv->ptp_clock_info, 456762306a36Sopenharmony_ci &phydev->mdio.dev); 456862306a36Sopenharmony_ci if (IS_ERR(ptp_priv->ptp_clock)) { 456962306a36Sopenharmony_ci phydev_err(phydev, "ptp_clock_register failed: %lu\n", 457062306a36Sopenharmony_ci PTR_ERR(ptp_priv->ptp_clock)); 457162306a36Sopenharmony_ci return -EINVAL; 457262306a36Sopenharmony_ci } 457362306a36Sopenharmony_ci 457462306a36Sopenharmony_ci if (!ptp_priv->ptp_clock) 457562306a36Sopenharmony_ci return 0; 457662306a36Sopenharmony_ci 457762306a36Sopenharmony_ci /* Initialize the SW */ 457862306a36Sopenharmony_ci skb_queue_head_init(&ptp_priv->tx_queue); 457962306a36Sopenharmony_ci ptp_priv->phydev = phydev; 458062306a36Sopenharmony_ci mutex_init(&ptp_priv->ptp_lock); 458162306a36Sopenharmony_ci spin_lock_init(&ptp_priv->seconds_lock); 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_ci ptp_priv->mii_ts.rxtstamp = lan8841_rxtstamp; 458462306a36Sopenharmony_ci ptp_priv->mii_ts.txtstamp = lan8814_txtstamp; 458562306a36Sopenharmony_ci ptp_priv->mii_ts.hwtstamp = lan8841_hwtstamp; 458662306a36Sopenharmony_ci ptp_priv->mii_ts.ts_info = lan8841_ts_info; 458762306a36Sopenharmony_ci 458862306a36Sopenharmony_ci phydev->mii_ts = &ptp_priv->mii_ts; 458962306a36Sopenharmony_ci 459062306a36Sopenharmony_ci return 0; 459162306a36Sopenharmony_ci} 459262306a36Sopenharmony_ci 459362306a36Sopenharmony_cistatic int lan8841_suspend(struct phy_device *phydev) 459462306a36Sopenharmony_ci{ 459562306a36Sopenharmony_ci struct kszphy_priv *priv = phydev->priv; 459662306a36Sopenharmony_ci struct kszphy_ptp_priv *ptp_priv = &priv->ptp_priv; 459762306a36Sopenharmony_ci 459862306a36Sopenharmony_ci ptp_cancel_worker_sync(ptp_priv->ptp_clock); 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci return genphy_suspend(phydev); 460162306a36Sopenharmony_ci} 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_cistatic struct phy_driver ksphy_driver[] = { 460462306a36Sopenharmony_ci{ 460562306a36Sopenharmony_ci .phy_id = PHY_ID_KS8737, 460662306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 460762306a36Sopenharmony_ci .name = "Micrel KS8737", 460862306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 460962306a36Sopenharmony_ci .driver_data = &ks8737_type, 461062306a36Sopenharmony_ci .probe = kszphy_probe, 461162306a36Sopenharmony_ci .config_init = kszphy_config_init, 461262306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 461362306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 461462306a36Sopenharmony_ci .suspend = kszphy_suspend, 461562306a36Sopenharmony_ci .resume = kszphy_resume, 461662306a36Sopenharmony_ci}, { 461762306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8021, 461862306a36Sopenharmony_ci .phy_id_mask = 0x00ffffff, 461962306a36Sopenharmony_ci .name = "Micrel KSZ8021 or KSZ8031", 462062306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 462162306a36Sopenharmony_ci .driver_data = &ksz8021_type, 462262306a36Sopenharmony_ci .probe = kszphy_probe, 462362306a36Sopenharmony_ci .config_init = kszphy_config_init, 462462306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 462562306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 462662306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 462762306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 462862306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 462962306a36Sopenharmony_ci .suspend = kszphy_suspend, 463062306a36Sopenharmony_ci .resume = kszphy_resume, 463162306a36Sopenharmony_ci}, { 463262306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8031, 463362306a36Sopenharmony_ci .phy_id_mask = 0x00ffffff, 463462306a36Sopenharmony_ci .name = "Micrel KSZ8031", 463562306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 463662306a36Sopenharmony_ci .driver_data = &ksz8021_type, 463762306a36Sopenharmony_ci .probe = kszphy_probe, 463862306a36Sopenharmony_ci .config_init = kszphy_config_init, 463962306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 464062306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 464162306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 464262306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 464362306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 464462306a36Sopenharmony_ci .suspend = kszphy_suspend, 464562306a36Sopenharmony_ci .resume = kszphy_resume, 464662306a36Sopenharmony_ci}, { 464762306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8041, 464862306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 464962306a36Sopenharmony_ci .name = "Micrel KSZ8041", 465062306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 465162306a36Sopenharmony_ci .driver_data = &ksz8041_type, 465262306a36Sopenharmony_ci .probe = kszphy_probe, 465362306a36Sopenharmony_ci .config_init = ksz8041_config_init, 465462306a36Sopenharmony_ci .config_aneg = ksz8041_config_aneg, 465562306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 465662306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 465762306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 465862306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 465962306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 466062306a36Sopenharmony_ci /* No suspend/resume callbacks because of errata DS80000700A, 466162306a36Sopenharmony_ci * receiver error following software power down. 466262306a36Sopenharmony_ci */ 466362306a36Sopenharmony_ci}, { 466462306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8041RNLI, 466562306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 466662306a36Sopenharmony_ci .name = "Micrel KSZ8041RNLI", 466762306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 466862306a36Sopenharmony_ci .driver_data = &ksz8041_type, 466962306a36Sopenharmony_ci .probe = kszphy_probe, 467062306a36Sopenharmony_ci .config_init = kszphy_config_init, 467162306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 467262306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 467362306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 467462306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 467562306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 467662306a36Sopenharmony_ci .suspend = kszphy_suspend, 467762306a36Sopenharmony_ci .resume = kszphy_resume, 467862306a36Sopenharmony_ci}, { 467962306a36Sopenharmony_ci .name = "Micrel KSZ8051", 468062306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 468162306a36Sopenharmony_ci .driver_data = &ksz8051_type, 468262306a36Sopenharmony_ci .probe = kszphy_probe, 468362306a36Sopenharmony_ci .config_init = kszphy_config_init, 468462306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 468562306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 468662306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 468762306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 468862306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 468962306a36Sopenharmony_ci .match_phy_device = ksz8051_match_phy_device, 469062306a36Sopenharmony_ci .suspend = kszphy_suspend, 469162306a36Sopenharmony_ci .resume = kszphy_resume, 469262306a36Sopenharmony_ci}, { 469362306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8001, 469462306a36Sopenharmony_ci .name = "Micrel KSZ8001 or KS8721", 469562306a36Sopenharmony_ci .phy_id_mask = 0x00fffffc, 469662306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 469762306a36Sopenharmony_ci .driver_data = &ksz8041_type, 469862306a36Sopenharmony_ci .probe = kszphy_probe, 469962306a36Sopenharmony_ci .config_init = kszphy_config_init, 470062306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 470162306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 470262306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 470362306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 470462306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 470562306a36Sopenharmony_ci .suspend = kszphy_suspend, 470662306a36Sopenharmony_ci .resume = kszphy_resume, 470762306a36Sopenharmony_ci}, { 470862306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8081, 470962306a36Sopenharmony_ci .name = "Micrel KSZ8081 or KSZ8091", 471062306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 471162306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 471262306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 471362306a36Sopenharmony_ci .driver_data = &ksz8081_type, 471462306a36Sopenharmony_ci .probe = kszphy_probe, 471562306a36Sopenharmony_ci .config_init = ksz8081_config_init, 471662306a36Sopenharmony_ci .soft_reset = genphy_soft_reset, 471762306a36Sopenharmony_ci .config_aneg = ksz8081_config_aneg, 471862306a36Sopenharmony_ci .read_status = ksz8081_read_status, 471962306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 472062306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 472162306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 472262306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 472362306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 472462306a36Sopenharmony_ci .suspend = kszphy_suspend, 472562306a36Sopenharmony_ci .resume = kszphy_resume, 472662306a36Sopenharmony_ci .cable_test_start = ksz886x_cable_test_start, 472762306a36Sopenharmony_ci .cable_test_get_status = ksz886x_cable_test_get_status, 472862306a36Sopenharmony_ci}, { 472962306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8061, 473062306a36Sopenharmony_ci .name = "Micrel KSZ8061", 473162306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 473262306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 473362306a36Sopenharmony_ci .probe = kszphy_probe, 473462306a36Sopenharmony_ci .config_init = ksz8061_config_init, 473562306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 473662306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 473762306a36Sopenharmony_ci .suspend = kszphy_suspend, 473862306a36Sopenharmony_ci .resume = kszphy_resume, 473962306a36Sopenharmony_ci}, { 474062306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ9021, 474162306a36Sopenharmony_ci .phy_id_mask = 0x000ffffe, 474262306a36Sopenharmony_ci .name = "Micrel KSZ9021 Gigabit PHY", 474362306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 474462306a36Sopenharmony_ci .driver_data = &ksz9021_type, 474562306a36Sopenharmony_ci .probe = kszphy_probe, 474662306a36Sopenharmony_ci .get_features = ksz9031_get_features, 474762306a36Sopenharmony_ci .config_init = ksz9021_config_init, 474862306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 474962306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 475062306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 475162306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 475262306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 475362306a36Sopenharmony_ci .suspend = kszphy_suspend, 475462306a36Sopenharmony_ci .resume = kszphy_resume, 475562306a36Sopenharmony_ci .read_mmd = genphy_read_mmd_unsupported, 475662306a36Sopenharmony_ci .write_mmd = genphy_write_mmd_unsupported, 475762306a36Sopenharmony_ci}, { 475862306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ9031, 475962306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 476062306a36Sopenharmony_ci .name = "Micrel KSZ9031 Gigabit PHY", 476162306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 476262306a36Sopenharmony_ci .driver_data = &ksz9021_type, 476362306a36Sopenharmony_ci .probe = kszphy_probe, 476462306a36Sopenharmony_ci .get_features = ksz9031_get_features, 476562306a36Sopenharmony_ci .config_init = ksz9031_config_init, 476662306a36Sopenharmony_ci .soft_reset = genphy_soft_reset, 476762306a36Sopenharmony_ci .read_status = ksz9031_read_status, 476862306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 476962306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 477062306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 477162306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 477262306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 477362306a36Sopenharmony_ci .suspend = kszphy_suspend, 477462306a36Sopenharmony_ci .resume = kszphy_resume, 477562306a36Sopenharmony_ci .cable_test_start = ksz9x31_cable_test_start, 477662306a36Sopenharmony_ci .cable_test_get_status = ksz9x31_cable_test_get_status, 477762306a36Sopenharmony_ci}, { 477862306a36Sopenharmony_ci .phy_id = PHY_ID_LAN8814, 477962306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 478062306a36Sopenharmony_ci .name = "Microchip INDY Gigabit Quad PHY", 478162306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 478262306a36Sopenharmony_ci .config_init = lan8814_config_init, 478362306a36Sopenharmony_ci .driver_data = &lan8814_type, 478462306a36Sopenharmony_ci .probe = lan8814_probe, 478562306a36Sopenharmony_ci .soft_reset = genphy_soft_reset, 478662306a36Sopenharmony_ci .read_status = ksz9031_read_status, 478762306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 478862306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 478962306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 479062306a36Sopenharmony_ci .suspend = genphy_suspend, 479162306a36Sopenharmony_ci .resume = kszphy_resume, 479262306a36Sopenharmony_ci .config_intr = lan8814_config_intr, 479362306a36Sopenharmony_ci .handle_interrupt = lan8814_handle_interrupt, 479462306a36Sopenharmony_ci .cable_test_start = lan8814_cable_test_start, 479562306a36Sopenharmony_ci .cable_test_get_status = ksz886x_cable_test_get_status, 479662306a36Sopenharmony_ci}, { 479762306a36Sopenharmony_ci .phy_id = PHY_ID_LAN8804, 479862306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 479962306a36Sopenharmony_ci .name = "Microchip LAN966X Gigabit PHY", 480062306a36Sopenharmony_ci .config_init = lan8804_config_init, 480162306a36Sopenharmony_ci .driver_data = &ksz9021_type, 480262306a36Sopenharmony_ci .probe = kszphy_probe, 480362306a36Sopenharmony_ci .soft_reset = genphy_soft_reset, 480462306a36Sopenharmony_ci .read_status = ksz9031_read_status, 480562306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 480662306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 480762306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 480862306a36Sopenharmony_ci .suspend = genphy_suspend, 480962306a36Sopenharmony_ci .resume = kszphy_resume, 481062306a36Sopenharmony_ci .config_intr = lan8804_config_intr, 481162306a36Sopenharmony_ci .handle_interrupt = lan8804_handle_interrupt, 481262306a36Sopenharmony_ci}, { 481362306a36Sopenharmony_ci .phy_id = PHY_ID_LAN8841, 481462306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 481562306a36Sopenharmony_ci .name = "Microchip LAN8841 Gigabit PHY", 481662306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 481762306a36Sopenharmony_ci .driver_data = &lan8841_type, 481862306a36Sopenharmony_ci .config_init = lan8841_config_init, 481962306a36Sopenharmony_ci .probe = lan8841_probe, 482062306a36Sopenharmony_ci .soft_reset = genphy_soft_reset, 482162306a36Sopenharmony_ci .config_intr = lan8841_config_intr, 482262306a36Sopenharmony_ci .handle_interrupt = lan8841_handle_interrupt, 482362306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 482462306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 482562306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 482662306a36Sopenharmony_ci .suspend = lan8841_suspend, 482762306a36Sopenharmony_ci .resume = genphy_resume, 482862306a36Sopenharmony_ci .cable_test_start = lan8814_cable_test_start, 482962306a36Sopenharmony_ci .cable_test_get_status = ksz886x_cable_test_get_status, 483062306a36Sopenharmony_ci}, { 483162306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ9131, 483262306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 483362306a36Sopenharmony_ci .name = "Microchip KSZ9131 Gigabit PHY", 483462306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 483562306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 483662306a36Sopenharmony_ci .driver_data = &ksz9131_type, 483762306a36Sopenharmony_ci .probe = kszphy_probe, 483862306a36Sopenharmony_ci .soft_reset = genphy_soft_reset, 483962306a36Sopenharmony_ci .config_init = ksz9131_config_init, 484062306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 484162306a36Sopenharmony_ci .config_aneg = ksz9131_config_aneg, 484262306a36Sopenharmony_ci .read_status = ksz9131_read_status, 484362306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 484462306a36Sopenharmony_ci .get_sset_count = kszphy_get_sset_count, 484562306a36Sopenharmony_ci .get_strings = kszphy_get_strings, 484662306a36Sopenharmony_ci .get_stats = kszphy_get_stats, 484762306a36Sopenharmony_ci .suspend = kszphy_suspend, 484862306a36Sopenharmony_ci .resume = kszphy_resume, 484962306a36Sopenharmony_ci .cable_test_start = ksz9x31_cable_test_start, 485062306a36Sopenharmony_ci .cable_test_get_status = ksz9x31_cable_test_get_status, 485162306a36Sopenharmony_ci .get_features = ksz9477_get_features, 485262306a36Sopenharmony_ci}, { 485362306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ8873MLL, 485462306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 485562306a36Sopenharmony_ci .name = "Micrel KSZ8873MLL Switch", 485662306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 485762306a36Sopenharmony_ci .config_init = kszphy_config_init, 485862306a36Sopenharmony_ci .config_aneg = ksz8873mll_config_aneg, 485962306a36Sopenharmony_ci .read_status = ksz8873mll_read_status, 486062306a36Sopenharmony_ci .suspend = genphy_suspend, 486162306a36Sopenharmony_ci .resume = genphy_resume, 486262306a36Sopenharmony_ci}, { 486362306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ886X, 486462306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 486562306a36Sopenharmony_ci .name = "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch", 486662306a36Sopenharmony_ci .driver_data = &ksz886x_type, 486762306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 486862306a36Sopenharmony_ci .flags = PHY_POLL_CABLE_TEST, 486962306a36Sopenharmony_ci .config_init = kszphy_config_init, 487062306a36Sopenharmony_ci .config_aneg = ksz886x_config_aneg, 487162306a36Sopenharmony_ci .read_status = ksz886x_read_status, 487262306a36Sopenharmony_ci .suspend = genphy_suspend, 487362306a36Sopenharmony_ci .resume = genphy_resume, 487462306a36Sopenharmony_ci .cable_test_start = ksz886x_cable_test_start, 487562306a36Sopenharmony_ci .cable_test_get_status = ksz886x_cable_test_get_status, 487662306a36Sopenharmony_ci}, { 487762306a36Sopenharmony_ci .name = "Micrel KSZ87XX Switch", 487862306a36Sopenharmony_ci /* PHY_BASIC_FEATURES */ 487962306a36Sopenharmony_ci .config_init = kszphy_config_init, 488062306a36Sopenharmony_ci .match_phy_device = ksz8795_match_phy_device, 488162306a36Sopenharmony_ci .suspend = genphy_suspend, 488262306a36Sopenharmony_ci .resume = genphy_resume, 488362306a36Sopenharmony_ci}, { 488462306a36Sopenharmony_ci .phy_id = PHY_ID_KSZ9477, 488562306a36Sopenharmony_ci .phy_id_mask = MICREL_PHY_ID_MASK, 488662306a36Sopenharmony_ci .name = "Microchip KSZ9477", 488762306a36Sopenharmony_ci /* PHY_GBIT_FEATURES */ 488862306a36Sopenharmony_ci .config_init = ksz9477_config_init, 488962306a36Sopenharmony_ci .config_intr = kszphy_config_intr, 489062306a36Sopenharmony_ci .handle_interrupt = kszphy_handle_interrupt, 489162306a36Sopenharmony_ci .suspend = genphy_suspend, 489262306a36Sopenharmony_ci .resume = genphy_resume, 489362306a36Sopenharmony_ci .get_features = ksz9477_get_features, 489462306a36Sopenharmony_ci} }; 489562306a36Sopenharmony_ci 489662306a36Sopenharmony_cimodule_phy_driver(ksphy_driver); 489762306a36Sopenharmony_ci 489862306a36Sopenharmony_ciMODULE_DESCRIPTION("Micrel PHY driver"); 489962306a36Sopenharmony_ciMODULE_AUTHOR("David J. Choi"); 490062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 490162306a36Sopenharmony_ci 490262306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused micrel_tbl[] = { 490362306a36Sopenharmony_ci { PHY_ID_KSZ9021, 0x000ffffe }, 490462306a36Sopenharmony_ci { PHY_ID_KSZ9031, MICREL_PHY_ID_MASK }, 490562306a36Sopenharmony_ci { PHY_ID_KSZ9131, MICREL_PHY_ID_MASK }, 490662306a36Sopenharmony_ci { PHY_ID_KSZ8001, 0x00fffffc }, 490762306a36Sopenharmony_ci { PHY_ID_KS8737, MICREL_PHY_ID_MASK }, 490862306a36Sopenharmony_ci { PHY_ID_KSZ8021, 0x00ffffff }, 490962306a36Sopenharmony_ci { PHY_ID_KSZ8031, 0x00ffffff }, 491062306a36Sopenharmony_ci { PHY_ID_KSZ8041, MICREL_PHY_ID_MASK }, 491162306a36Sopenharmony_ci { PHY_ID_KSZ8051, MICREL_PHY_ID_MASK }, 491262306a36Sopenharmony_ci { PHY_ID_KSZ8061, MICREL_PHY_ID_MASK }, 491362306a36Sopenharmony_ci { PHY_ID_KSZ8081, MICREL_PHY_ID_MASK }, 491462306a36Sopenharmony_ci { PHY_ID_KSZ8873MLL, MICREL_PHY_ID_MASK }, 491562306a36Sopenharmony_ci { PHY_ID_KSZ886X, MICREL_PHY_ID_MASK }, 491662306a36Sopenharmony_ci { PHY_ID_LAN8814, MICREL_PHY_ID_MASK }, 491762306a36Sopenharmony_ci { PHY_ID_LAN8804, MICREL_PHY_ID_MASK }, 491862306a36Sopenharmony_ci { PHY_ID_LAN8841, MICREL_PHY_ID_MASK }, 491962306a36Sopenharmony_ci { } 492062306a36Sopenharmony_ci}; 492162306a36Sopenharmony_ci 492262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, micrel_tbl); 4923