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

Quelle  nxp-c45-tja11xx.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* NXP C45 PHY driver
 * Copyright 2021-2025 NXP
 * Author: Radu Pirea <radu-nicolae.pirea@oss.nxp.com>
 */


#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/processor.h>
#include <linux/property.h>
#include <linux/ptp_classify.h>
#include <linux/net_tstamp.h>

#include "nxp-c45-tja11xx.h"

/* Same id: TJA1103, TJA1104 */
#define PHY_ID_TJA_1103   0x001BB010
/* Same id: TJA1120, TJA1121 */
#define PHY_ID_TJA_1120   0x001BB031

#define VEND1_DEVICE_ID3  0x0004
#define TJA1120_DEV_ID3_SILICON_VERSION GENMASK(15, 12)
#define TJA1120_DEV_ID3_SAMPLE_TYPE GENMASK(11, 8)
#define DEVICE_ID3_SAMPLE_TYPE_R 0x9

#define VEND1_DEVICE_CONTROL  0x0040
#define DEVICE_CONTROL_RESET  BIT(15)
#define DEVICE_CONTROL_CONFIG_GLOBAL_EN BIT(14)
#define DEVICE_CONTROL_CONFIG_ALL_EN BIT(13)

#define VEND1_DEVICE_CONFIG  0x0048

#define TJA1120_VEND1_EXT_TS_MODE 0x1012

#define TJA1120_GLOBAL_INFRA_IRQ_ACK 0x2C08
#define TJA1120_GLOBAL_INFRA_IRQ_EN 0x2C0A
#define TJA1120_GLOBAL_INFRA_IRQ_STATUS 0x2C0C
#define TJA1120_DEV_BOOT_DONE  BIT(1)

#define TJA1120_VEND1_PTP_TRIG_DATA_S 0x1070

#define TJA1120_EGRESS_TS_DATA_S 0x9060
#define TJA1120_EGRESS_TS_END  0x9067
#define TJA1120_TS_VALID  BIT(0)
#define TJA1120_MORE_TS   BIT(15)

#define VEND1_PHY_IRQ_ACK  0x80A0
#define VEND1_PHY_IRQ_EN  0x80A1
#define VEND1_PHY_IRQ_STATUS  0x80A2
#define PHY_IRQ_LINK_EVENT  BIT(1)

#define VEND1_ALWAYS_ACCESSIBLE  0x801F
#define FUSA_PASS   BIT(4)

#define VEND1_PHY_CONTROL  0x8100
#define PHY_CONFIG_EN   BIT(14)
#define PHY_START_OP   BIT(0)

#define VEND1_PHY_CONFIG  0x8108
#define PHY_CONFIG_AUTO   BIT(0)

#define TJA1120_EPHY_RESETS  0x810A
#define EPHY_PCS_RESET   BIT(3)

#define VEND1_SIGNAL_QUALITY  0x8320
#define SQI_VALID   BIT(14)
#define SQI_MASK   GENMASK(2, 0)
#define MAX_SQI    SQI_MASK

#define CABLE_TEST_ENABLE  BIT(15)
#define CABLE_TEST_START  BIT(14)
#define CABLE_TEST_OK   0x00
#define CABLE_TEST_SHORTED  0x01
#define CABLE_TEST_OPEN   0x02
#define CABLE_TEST_UNKNOWN  0x07

#define VEND1_PORT_CONTROL  0x8040
#define PORT_CONTROL_EN   BIT(14)

#define VEND1_PORT_ABILITIES  0x8046
#define MACSEC_ABILITY   BIT(5)
#define PTP_ABILITY   BIT(3)

#define VEND1_PORT_FUNC_IRQ_EN  0x807A
#define MACSEC_IRQS   BIT(5)
#define PTP_IRQS   BIT(3)

#define VEND1_PTP_IRQ_ACK  0x9008
#define EGR_TS_IRQ   BIT(1)

#define VEND1_PORT_INFRA_CONTROL 0xAC00
#define PORT_INFRA_CONTROL_EN  BIT(14)

#define VEND1_RXID   0xAFCC
#define VEND1_TXID   0xAFCD
#define ID_ENABLE   BIT(15)

#define VEND1_ABILITIES   0xAFC4
#define RGMII_ID_ABILITY  BIT(15)
#define RGMII_ABILITY   BIT(14)
#define RMII_ABILITY   BIT(10)
#define REVMII_ABILITY   BIT(9)
#define MII_ABILITY   BIT(8)
#define SGMII_ABILITY   BIT(0)

#define VEND1_MII_BASIC_CONFIG  0xAFC6
#define MII_BASIC_CONFIG_REV  BIT(4)
#define MII_BASIC_CONFIG_SGMII  0x9
#define MII_BASIC_CONFIG_RGMII  0x7
#define MII_BASIC_CONFIG_RMII  0x5
#define MII_BASIC_CONFIG_MII  0x4

#define VEND1_SGMII_BASIC_CONTROL 0xB000
#define SGMII_LPM   BIT(11)

#define VEND1_SYMBOL_ERROR_CNT_XTD 0x8351
#define EXTENDED_CNT_EN   BIT(15)
#define VEND1_MONITOR_STATUS  0xAC80
#define MONITOR_RESET   BIT(15)
#define VEND1_MONITOR_CONFIG  0xAC86
#define LOST_FRAMES_CNT_EN  BIT(9)
#define ALL_FRAMES_CNT_EN  BIT(8)

#define VEND1_SYMBOL_ERROR_COUNTER 0x8350
#define VEND1_LINK_DROP_COUNTER  0x8352
#define VEND1_LINK_LOSSES_AND_FAILURES 0x8353
#define VEND1_RX_PREAMBLE_COUNT  0xAFCE
#define VEND1_TX_PREAMBLE_COUNT  0xAFCF
#define VEND1_RX_IPG_LENGTH  0xAFD0
#define VEND1_TX_IPG_LENGTH  0xAFD1
#define COUNTER_EN   BIT(15)

#define VEND1_PTP_CONFIG  0x1102
#define EXT_TRG_EDGE   BIT(1)

#define TJA1120_SYNC_TRIG_FILTER 0x1010
#define PTP_TRIG_RISE_TS  BIT(3)
#define PTP_TRIG_FALLING_TS  BIT(2)

#define CLK_RATE_ADJ_LD   BIT(15)
#define CLK_RATE_ADJ_DIR  BIT(14)

#define VEND1_RX_TS_INSRT_CTRL  0x114D
#define TJA1103_RX_TS_INSRT_MODE2 0x02

#define TJA1120_RX_TS_INSRT_CTRL 0x9012
#define TJA1120_RX_TS_INSRT_EN  BIT(15)
#define TJA1120_TS_INSRT_MODE  BIT(4)

#define VEND1_EGR_RING_DATA_0  0x114E
#define VEND1_EGR_RING_CTRL  0x1154

#define RING_DATA_0_TS_VALID  BIT(15)

#define RING_DONE   BIT(0)

#define TS_SEC_MASK   GENMASK(1, 0)

#define PTP_ENABLE   BIT(3)
#define PHY_TEST_ENABLE   BIT(0)

#define VEND1_PORT_PTP_CONTROL  0x9000
#define PORT_PTP_CONTROL_BYPASS  BIT(11)

#define PTP_CLK_PERIOD_100BT1  15ULL
#define PTP_CLK_PERIOD_1000BT1  8ULL

#define EVENT_MSG_FILT_ALL  0x0F
#define EVENT_MSG_FILT_NONE  0x00

#define VEND1_GPIO_FUNC_CONFIG_BASE 0x2C40
#define GPIO_FUNC_EN   BIT(15)
#define GPIO_FUNC_PTP   BIT(6)
#define GPIO_SIGNAL_PTP_TRIGGER  0x01
#define GPIO_SIGNAL_PPS_OUT  0x12
#define GPIO_DISABLE   0
#define GPIO_PPS_OUT_CFG  (GPIO_FUNC_EN | GPIO_FUNC_PTP | \
 GPIO_SIGNAL_PPS_OUT)
#define GPIO_EXTTS_OUT_CFG  (GPIO_FUNC_EN | GPIO_FUNC_PTP | \
 GPIO_SIGNAL_PTP_TRIGGER)

#define RGMII_PERIOD_PS   8000U
#define PS_PER_DEGREE   div_u64(RGMII_PERIOD_PS, 360)
#define MIN_ID_PS   1644U
#define MAX_ID_PS   2260U
#define DEFAULT_ID_PS   2000U

#define PPM_TO_SUBNS_INC(ppb, ptp_clk_period) div_u64(GENMASK_ULL(31, 0) * \
 (ppb) * (ptp_clk_period), NSEC_PER_SEC)

#define NXP_C45_SKB_CB(skb) ((struct nxp_c45_skb_cb *)(skb)->cb)

#define TJA11XX_REVERSE_MODE  BIT(0)

struct nxp_c45_phy;

struct nxp_c45_skb_cb {
 struct ptp_header *header;
 unsigned int type;
};

#define NXP_C45_REG_FIELD(_reg, _devad, _offset, _size) \
 ((struct nxp_c45_reg_field) {   \
  .reg = _reg,    \
  .devad =  _devad,   \
  .offset = _offset,   \
  .size = _size,    \
 })

struct nxp_c45_reg_field {
 u16 reg;
 u8 devad;
 u8 offset;
 u8 size;
};

struct nxp_c45_hwts {
 u32 nsec;
 u32 sec;
 u8 domain_number;
 u16 sequence_id;
 u8 msg_type;
};

struct nxp_c45_regmap {
 /* PTP config regs. */
 u16 vend1_ptp_clk_period;
 u16 vend1_event_msg_filt;

 /* LTC bits and regs. */
 struct nxp_c45_reg_field ltc_read;
 struct nxp_c45_reg_field ltc_write;
 struct nxp_c45_reg_field ltc_lock_ctrl;
 u16 vend1_ltc_wr_nsec_0;
 u16 vend1_ltc_wr_nsec_1;
 u16 vend1_ltc_wr_sec_0;
 u16 vend1_ltc_wr_sec_1;
 u16 vend1_ltc_rd_nsec_0;
 u16 vend1_ltc_rd_nsec_1;
 u16 vend1_ltc_rd_sec_0;
 u16 vend1_ltc_rd_sec_1;
 u16 vend1_rate_adj_subns_0;
 u16 vend1_rate_adj_subns_1;

 /* External trigger reg fields. */
 struct nxp_c45_reg_field irq_egr_ts_en;
 struct nxp_c45_reg_field irq_egr_ts_status;
 struct nxp_c45_reg_field domain_number;
 struct nxp_c45_reg_field msg_type;
 struct nxp_c45_reg_field sequence_id;
 struct nxp_c45_reg_field sec_1_0;
 struct nxp_c45_reg_field sec_4_2;
 struct nxp_c45_reg_field nsec_15_0;
 struct nxp_c45_reg_field nsec_29_16;

 /* PPS and EXT Trigger bits and regs. */
 struct nxp_c45_reg_field pps_enable;
 struct nxp_c45_reg_field pps_polarity;
 u16 vend1_ext_trg_data_0;
 u16 vend1_ext_trg_data_1;
 u16 vend1_ext_trg_data_2;
 u16 vend1_ext_trg_data_3;
 u16 vend1_ext_trg_ctrl;

 /* Cable test reg fields. */
 u16 cable_test;
 struct nxp_c45_reg_field cable_test_valid;
 struct nxp_c45_reg_field cable_test_result;
};

struct nxp_c45_phy_stats {
 const char *name;
 const struct nxp_c45_reg_field counter;
};

struct nxp_c45_phy_data {
 const struct nxp_c45_regmap *regmap;
 const struct nxp_c45_phy_stats *stats;
 int n_stats;
 u8 ptp_clk_period;
 bool ext_ts_both_edges;
 bool ack_ptp_irq;
 void (*counters_enable)(struct phy_device *phydev);
 bool (*get_egressts)(struct nxp_c45_phy *priv,
        struct nxp_c45_hwts *hwts);
 bool (*get_extts)(struct nxp_c45_phy *priv, struct timespec64 *extts);
 void (*ptp_init)(struct phy_device *phydev);
 void (*ptp_enable)(struct phy_device *phydev, bool enable);
 void (*nmi_handler)(struct phy_device *phydev,
       irqreturn_t *irq_status);
};

static const
struct nxp_c45_phy_data *nxp_c45_get_data(struct phy_device *phydev)
{
 return phydev->drv->driver_data;
}

static const
struct nxp_c45_regmap *nxp_c45_get_regmap(struct phy_device *phydev)
{
 const struct nxp_c45_phy_data *phy_data = nxp_c45_get_data(phydev);

 return phy_data->regmap;
}

static int nxp_c45_read_reg_field(struct phy_device *phydev,
      const struct nxp_c45_reg_field *reg_field)
{
 u16 mask;
 int ret;

 if (reg_field->size == 0) {
  phydev_err(phydev, "Trying to read a reg field of size 0.\n");
  return -EINVAL;
 }

 ret = phy_read_mmd(phydev, reg_field->devad, reg_field->reg);
 if (ret < 0)
  return ret;

 mask = reg_field->size == 1 ? BIT(reg_field->offset) :
  GENMASK(reg_field->offset + reg_field->size - 1,
   reg_field->offset);
 ret &= mask;
 ret >>= reg_field->offset;

 return ret;
}

static int nxp_c45_write_reg_field(struct phy_device *phydev,
       const struct nxp_c45_reg_field *reg_field,
       u16 val)
{
 u16 mask;
 u16 set;

 if (reg_field->size == 0) {
  phydev_err(phydev, "Trying to write a reg field of size 0.\n");
  return -EINVAL;
 }

 mask = reg_field->size == 1 ? BIT(reg_field->offset) :
  GENMASK(reg_field->offset + reg_field->size - 1,
   reg_field->offset);
 set = val << reg_field->offset;

 return phy_modify_mmd_changed(phydev, reg_field->devad,
          reg_field->reg, mask, set);
}

static int nxp_c45_set_reg_field(struct phy_device *phydev,
     const struct nxp_c45_reg_field *reg_field)
{
 if (reg_field->size != 1) {
  phydev_err(phydev, "Trying to set a reg field of size different than 1.\n");
  return -EINVAL;
 }

 return nxp_c45_write_reg_field(phydev, reg_field, 1);
}

static int nxp_c45_clear_reg_field(struct phy_device *phydev,
       const struct nxp_c45_reg_field *reg_field)
{
 if (reg_field->size != 1) {
  phydev_err(phydev, "Trying to set a reg field of size different than 1.\n");
  return -EINVAL;
 }

 return nxp_c45_write_reg_field(phydev, reg_field, 0);
}

static bool nxp_c45_poll_txts(struct phy_device *phydev)
{
 return phydev->irq <= 0;
}

static int _nxp_c45_ptp_gettimex64(struct ptp_clock_info *ptp,
       struct timespec64 *ts,
       struct ptp_system_timestamp *sts)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(priv->phydev);

 nxp_c45_set_reg_field(priv->phydev, ®map->ltc_read);
 ts->tv_nsec = phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
       regmap->vend1_ltc_rd_nsec_0);
 ts->tv_nsec |= phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
        regmap->vend1_ltc_rd_nsec_1) << 16;
 ts->tv_sec = phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
      regmap->vend1_ltc_rd_sec_0);
 ts->tv_sec |= phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
       regmap->vend1_ltc_rd_sec_1) << 16;

 return 0;
}

static int nxp_c45_ptp_gettimex64(struct ptp_clock_info *ptp,
      struct timespec64 *ts,
      struct ptp_system_timestamp *sts)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);

 mutex_lock(&priv->ptp_lock);
 _nxp_c45_ptp_gettimex64(ptp, ts, sts);
 mutex_unlock(&priv->ptp_lock);

 return 0;
}

static int _nxp_c45_ptp_settime64(struct ptp_clock_info *ptp,
      const struct timespec64 *ts)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(priv->phydev);

 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1, regmap->vend1_ltc_wr_nsec_0,
        ts->tv_nsec);
 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1, regmap->vend1_ltc_wr_nsec_1,
        ts->tv_nsec >> 16);
 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1, regmap->vend1_ltc_wr_sec_0,
        ts->tv_sec);
 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1, regmap->vend1_ltc_wr_sec_1,
        ts->tv_sec >> 16);
 nxp_c45_set_reg_field(priv->phydev, ®map->ltc_write);

 return 0;
}

static int nxp_c45_ptp_settime64(struct ptp_clock_info *ptp,
     const struct timespec64 *ts)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);

 mutex_lock(&priv->ptp_lock);
 _nxp_c45_ptp_settime64(ptp, ts);
 mutex_unlock(&priv->ptp_lock);

 return 0;
}

static int nxp_c45_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);
 const struct nxp_c45_phy_data *data = nxp_c45_get_data(priv->phydev);
 const struct nxp_c45_regmap *regmap = data->regmap;
 s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
 u64 subns_inc_val;
 bool inc;

 mutex_lock(&priv->ptp_lock);
 inc = ppb >= 0;
 ppb = abs(ppb);

 subns_inc_val = PPM_TO_SUBNS_INC(ppb, data->ptp_clk_period);

 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1,
        regmap->vend1_rate_adj_subns_0,
        subns_inc_val);
 subns_inc_val >>= 16;
 subns_inc_val |= CLK_RATE_ADJ_LD;
 if (inc)
  subns_inc_val |= CLK_RATE_ADJ_DIR;

 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1,
        regmap->vend1_rate_adj_subns_1,
        subns_inc_val);
 mutex_unlock(&priv->ptp_lock);

 return 0;
}

static int nxp_c45_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);
 struct timespec64 now, then;

 mutex_lock(&priv->ptp_lock);
 then = ns_to_timespec64(delta);
 _nxp_c45_ptp_gettimex64(ptp, &now, NULL);
 now = timespec64_add(now, then);
 _nxp_c45_ptp_settime64(ptp, &now);
 mutex_unlock(&priv->ptp_lock);

 return 0;
}

static void nxp_c45_reconstruct_ts(struct timespec64 *ts,
       struct nxp_c45_hwts *hwts)
{
 ts->tv_nsec = hwts->nsec;
 if ((ts->tv_sec & TS_SEC_MASK) < (hwts->sec & TS_SEC_MASK))
  ts->tv_sec -= TS_SEC_MASK + 1;
 ts->tv_sec &= ~TS_SEC_MASK;
 ts->tv_sec |= hwts->sec & TS_SEC_MASK;
}

static bool nxp_c45_match_ts(struct ptp_header *header,
        struct nxp_c45_hwts *hwts,
        unsigned int type)
{
 return ntohs(header->sequence_id) == hwts->sequence_id &&
        ptp_get_msgtype(header, type) == hwts->msg_type &&
        header->domain_number  == hwts->domain_number;
}

static bool nxp_c45_get_extts(struct nxp_c45_phy *priv,
         struct timespec64 *extts)
{
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(priv->phydev);

 extts->tv_nsec = phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
          regmap->vend1_ext_trg_data_0);
 extts->tv_nsec |= phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
           regmap->vend1_ext_trg_data_1) << 16;
 extts->tv_sec = phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
         regmap->vend1_ext_trg_data_2);
 extts->tv_sec |= phy_read_mmd(priv->phydev, MDIO_MMD_VEND1,
          regmap->vend1_ext_trg_data_3) << 16;
 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1,
        regmap->vend1_ext_trg_ctrl, RING_DONE);

 return true;
}

static bool tja1120_extts_is_valid(struct phy_device *phydev)
{
 bool valid;
 int reg;

 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1,
      TJA1120_VEND1_PTP_TRIG_DATA_S);
 valid = !!(reg & TJA1120_TS_VALID);

 return valid;
}

static bool tja1120_get_extts(struct nxp_c45_phy *priv,
         struct timespec64 *extts)
{
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(priv->phydev);
 struct phy_device *phydev = priv->phydev;
 bool more_ts;
 bool valid;
 u16 reg;

 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1,
      regmap->vend1_ext_trg_ctrl);
 more_ts = !!(reg & TJA1120_MORE_TS);

 valid = tja1120_extts_is_valid(phydev);
 if (!valid) {
  if (!more_ts)
   goto tja1120_get_extts_out;

  /* Bug workaround for TJA1120 engineering samples: move the new
 * timestamp from the FIFO to the buffer.
 */

  phy_write_mmd(phydev, MDIO_MMD_VEND1,
         regmap->vend1_ext_trg_ctrl, RING_DONE);
  valid = tja1120_extts_is_valid(phydev);
  if (!valid)
   goto tja1120_get_extts_out;
 }

 nxp_c45_get_extts(priv, extts);
tja1120_get_extts_out:
 return valid;
}

static void nxp_c45_read_egress_ts(struct nxp_c45_phy *priv,
       struct nxp_c45_hwts *hwts)
{
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(priv->phydev);
 struct phy_device *phydev = priv->phydev;

 hwts->domain_number =
  nxp_c45_read_reg_field(phydev, ®map->domain_number);
 hwts->msg_type =
  nxp_c45_read_reg_field(phydev, ®map->msg_type);
 hwts->sequence_id =
  nxp_c45_read_reg_field(phydev, ®map->sequence_id);
 hwts->nsec =
  nxp_c45_read_reg_field(phydev, ®map->nsec_15_0);
 hwts->nsec |=
  nxp_c45_read_reg_field(phydev, ®map->nsec_29_16) << 16;
 hwts->sec = nxp_c45_read_reg_field(phydev, ®map->sec_1_0);
 hwts->sec |= nxp_c45_read_reg_field(phydev, ®map->sec_4_2) << 2;
}

static bool nxp_c45_get_hwtxts(struct nxp_c45_phy *priv,
          struct nxp_c45_hwts *hwts)
{
 bool valid;
 u16 reg;

 mutex_lock(&priv->ptp_lock);
 phy_write_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_EGR_RING_CTRL,
        RING_DONE);
 reg = phy_read_mmd(priv->phydev, MDIO_MMD_VEND1, VEND1_EGR_RING_DATA_0);
 valid = !!(reg & RING_DATA_0_TS_VALID);
 if (!valid)
  goto nxp_c45_get_hwtxts_out;

 nxp_c45_read_egress_ts(priv, hwts);
nxp_c45_get_hwtxts_out:
 mutex_unlock(&priv->ptp_lock);
 return valid;
}

static bool tja1120_egress_ts_is_valid(struct phy_device *phydev)
{
 bool valid;
 u16 reg;

 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, TJA1120_EGRESS_TS_DATA_S);
 valid = !!(reg & TJA1120_TS_VALID);

 return valid;
}

static bool tja1120_get_hwtxts(struct nxp_c45_phy *priv,
          struct nxp_c45_hwts *hwts)
{
 struct phy_device *phydev = priv->phydev;
 bool more_ts;
 bool valid;
 u16 reg;

 mutex_lock(&priv->ptp_lock);
 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, TJA1120_EGRESS_TS_END);
 more_ts = !!(reg & TJA1120_MORE_TS);
 valid = tja1120_egress_ts_is_valid(phydev);
 if (!valid) {
  if (!more_ts)
   goto tja1120_get_hwtxts_out;

  /* Bug workaround for TJA1120 engineering samples: move the
 * new timestamp from the FIFO to the buffer.
 */

  phy_write_mmd(phydev, MDIO_MMD_VEND1,
         TJA1120_EGRESS_TS_END, TJA1120_TS_VALID);
  valid = tja1120_egress_ts_is_valid(phydev);
  if (!valid)
   goto tja1120_get_hwtxts_out;
 }
 nxp_c45_read_egress_ts(priv, hwts);
 phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, TJA1120_EGRESS_TS_DATA_S,
      TJA1120_TS_VALID);
tja1120_get_hwtxts_out:
 mutex_unlock(&priv->ptp_lock);
 return valid;
}

static void nxp_c45_process_txts(struct nxp_c45_phy *priv,
     struct nxp_c45_hwts *txts)
{
 struct sk_buff *skb, *tmp, *skb_match = NULL;
 struct skb_shared_hwtstamps shhwtstamps;
 struct timespec64 ts;
 unsigned long flags;
 bool ts_match;
 s64 ts_ns;

 spin_lock_irqsave(&priv->tx_queue.lock, flags);
 skb_queue_walk_safe(&priv->tx_queue, skb, tmp) {
  ts_match = nxp_c45_match_ts(NXP_C45_SKB_CB(skb)->header, txts,
         NXP_C45_SKB_CB(skb)->type);
  if (!ts_match)
   continue;
  skb_match = skb;
  __skb_unlink(skb, &priv->tx_queue);
  break;
 }
 spin_unlock_irqrestore(&priv->tx_queue.lock, flags);

 if (skb_match) {
  nxp_c45_ptp_gettimex64(&priv->caps, &ts, NULL);
  nxp_c45_reconstruct_ts(&ts, txts);
  memset(&shhwtstamps, 0, sizeof(shhwtstamps));
  ts_ns = timespec64_to_ns(&ts);
  shhwtstamps.hwtstamp = ns_to_ktime(ts_ns);
  skb_complete_tx_timestamp(skb_match, &shhwtstamps);
 } else {
  phydev_warn(priv->phydev,
       "the tx timestamp doesn't match with any skb\n");
 }
}

static long nxp_c45_do_aux_work(struct ptp_clock_info *ptp)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);
 const struct nxp_c45_phy_data *data = nxp_c45_get_data(priv->phydev);
 bool poll_txts = nxp_c45_poll_txts(priv->phydev);
 struct skb_shared_hwtstamps *shhwtstamps_rx;
 struct ptp_clock_event event;
 struct nxp_c45_hwts hwts;
 bool reschedule = false;
 struct timespec64 ts;
 struct sk_buff *skb;
 bool ts_valid;
 u32 ts_raw;

 while (!skb_queue_empty_lockless(&priv->tx_queue) && poll_txts) {
  ts_valid = data->get_egressts(priv, &hwts);
  if (unlikely(!ts_valid)) {
   /* Still more skbs in the queue */
   reschedule = true;
   break;
  }

  nxp_c45_process_txts(priv, &hwts);
 }

 while ((skb = skb_dequeue(&priv->rx_queue)) != NULL) {
  nxp_c45_ptp_gettimex64(&priv->caps, &ts, NULL);
  ts_raw = __be32_to_cpu(NXP_C45_SKB_CB(skb)->header->reserved2);
  hwts.sec = ts_raw >> 30;
  hwts.nsec = ts_raw & GENMASK(29, 0);
  nxp_c45_reconstruct_ts(&ts, &hwts);
  shhwtstamps_rx = skb_hwtstamps(skb);
  shhwtstamps_rx->hwtstamp = ns_to_ktime(timespec64_to_ns(&ts));
  NXP_C45_SKB_CB(skb)->header->reserved2 = 0;
  netif_rx(skb);
 }

 if (priv->extts) {
  ts_valid = data->get_extts(priv, &ts);
  if (ts_valid && timespec64_compare(&ts, &priv->extts_ts) != 0) {
   priv->extts_ts = ts;
   event.index = priv->extts_index;
   event.type = PTP_CLOCK_EXTTS;
   event.timestamp = ns_to_ktime(timespec64_to_ns(&ts));
   ptp_clock_event(priv->ptp_clock, &event);
  }
  reschedule = true;
 }

 return reschedule ? 1 : -1;
}

static void nxp_c45_gpio_config(struct nxp_c45_phy *priv,
    int pin, u16 pin_cfg)
{
 struct phy_device *phydev = priv->phydev;

 phy_write_mmd(phydev, MDIO_MMD_VEND1,
        VEND1_GPIO_FUNC_CONFIG_BASE + pin, pin_cfg);
}

static int nxp_c45_perout_enable(struct nxp_c45_phy *priv,
     struct ptp_perout_request *perout, int on)
{
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(priv->phydev);
 struct phy_device *phydev = priv->phydev;
 int pin;

 pin = ptp_find_pin(priv->ptp_clock, PTP_PF_PEROUT, perout->index);
 if (pin < 0)
  return pin;

 if (!on) {
  nxp_c45_clear_reg_field(priv->phydev,
     ®map->pps_enable);
  nxp_c45_clear_reg_field(priv->phydev,
     ®map->pps_polarity);

  nxp_c45_gpio_config(priv, pin, GPIO_DISABLE);

  return 0;
 }

 /* The PPS signal is fixed to 1 second and is always generated when the
 * seconds counter is incremented. The start time is not configurable.
 * If the clock is adjusted, the PPS signal is automatically readjusted.
 */

 if (perout->period.sec != 1 || perout->period.nsec != 0) {
  phydev_warn(phydev, "The period can be set only to 1 second.");
  return -EINVAL;
 }

 if (!(perout->flags & PTP_PEROUT_PHASE)) {
  if (perout->start.sec != 0 || perout->start.nsec != 0) {
   phydev_warn(phydev, "The start time is not configurable. Should be set to 0 seconds and 0 nanoseconds.");
   return -EINVAL;
  }
 } else {
  if (perout->phase.nsec != 0 &&
      perout->phase.nsec != (NSEC_PER_SEC >> 1)) {
   phydev_warn(phydev, "The phase can be set only to 0 or 500000000 nanoseconds.");
   return -EINVAL;
  }

  if (perout->phase.nsec == 0)
   nxp_c45_clear_reg_field(priv->phydev,
      ®map->pps_polarity);
  else
   nxp_c45_set_reg_field(priv->phydev,
           ®map->pps_polarity);
 }

 nxp_c45_gpio_config(priv, pin, GPIO_PPS_OUT_CFG);

 nxp_c45_set_reg_field(priv->phydev, ®map->pps_enable);

 return 0;
}

static void nxp_c45_set_rising_or_falling(struct phy_device *phydev,
       struct ptp_extts_request *extts)
{
 if (extts->flags & PTP_RISING_EDGE)
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
       VEND1_PTP_CONFIG, EXT_TRG_EDGE);

 if (extts->flags & PTP_FALLING_EDGE)
  phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
     VEND1_PTP_CONFIG, EXT_TRG_EDGE);
}

static void nxp_c45_set_rising_and_falling(struct phy_device *phydev,
        struct ptp_extts_request *extts)
{
 /* PTP_EXTTS_REQUEST may have only the PTP_ENABLE_FEATURE flag set. In
 * this case external ts will be enabled on rising edge.
 */

 if (extts->flags & PTP_RISING_EDGE ||
     extts->flags == PTP_ENABLE_FEATURE)
  phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
     TJA1120_SYNC_TRIG_FILTER,
     PTP_TRIG_RISE_TS);
 else
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
       TJA1120_SYNC_TRIG_FILTER,
       PTP_TRIG_RISE_TS);

 if (extts->flags & PTP_FALLING_EDGE)
  phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
     TJA1120_SYNC_TRIG_FILTER,
     PTP_TRIG_FALLING_TS);
 else
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
       TJA1120_SYNC_TRIG_FILTER,
       PTP_TRIG_FALLING_TS);
}

static int nxp_c45_extts_enable(struct nxp_c45_phy *priv,
    struct ptp_extts_request *extts, int on)
{
 const struct nxp_c45_phy_data *data = nxp_c45_get_data(priv->phydev);
 int pin;

 /* Sampling on both edges is not supported */
 if ((extts->flags & PTP_RISING_EDGE) &&
     (extts->flags & PTP_FALLING_EDGE) &&
     !data->ext_ts_both_edges)
  return -EOPNOTSUPP;

 pin = ptp_find_pin(priv->ptp_clock, PTP_PF_EXTTS, extts->index);
 if (pin < 0)
  return pin;

 if (!on) {
  nxp_c45_gpio_config(priv, pin, GPIO_DISABLE);
  priv->extts = false;

  return 0;
 }

 if (data->ext_ts_both_edges)
  nxp_c45_set_rising_and_falling(priv->phydev, extts);
 else
  nxp_c45_set_rising_or_falling(priv->phydev, extts);

 nxp_c45_gpio_config(priv, pin, GPIO_EXTTS_OUT_CFG);
 priv->extts = true;
 priv->extts_index = extts->index;
 ptp_schedule_worker(priv->ptp_clock, 0);

 return 0;
}

static int nxp_c45_ptp_enable(struct ptp_clock_info *ptp,
         struct ptp_clock_request *req, int on)
{
 struct nxp_c45_phy *priv = container_of(ptp, struct nxp_c45_phy, caps);

 switch (req->type) {
 case PTP_CLK_REQ_EXTTS:
  return nxp_c45_extts_enable(priv, &req->extts, on);
 case PTP_CLK_REQ_PEROUT:
  return nxp_c45_perout_enable(priv, &req->perout, on);
 default:
  return -EOPNOTSUPP;
 }
}

static struct ptp_pin_desc nxp_c45_ptp_pins[] = {
 { "nxp_c45_gpio0", 0, PTP_PF_NONE},
 { "nxp_c45_gpio1", 1, PTP_PF_NONE},
 { "nxp_c45_gpio2", 2, PTP_PF_NONE},
 { "nxp_c45_gpio3", 3, PTP_PF_NONE},
 { "nxp_c45_gpio4", 4, PTP_PF_NONE},
 { "nxp_c45_gpio5", 5, PTP_PF_NONE},
 { "nxp_c45_gpio6", 6, PTP_PF_NONE},
 { "nxp_c45_gpio7", 7, PTP_PF_NONE},
 { "nxp_c45_gpio8", 8, PTP_PF_NONE},
 { "nxp_c45_gpio9", 9, PTP_PF_NONE},
 { "nxp_c45_gpio10", 10, PTP_PF_NONE},
 { "nxp_c45_gpio11", 11, PTP_PF_NONE},
};

static int nxp_c45_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
      enum ptp_pin_function func, unsigned int chan)
{
 if (pin >= ARRAY_SIZE(nxp_c45_ptp_pins))
  return -EINVAL;

 switch (func) {
 case PTP_PF_NONE:
 case PTP_PF_PEROUT:
 case PTP_PF_EXTTS:
  break;
 default:
  return -EOPNOTSUPP;
 }

 return 0;
}

static int nxp_c45_init_ptp_clock(struct nxp_c45_phy *priv)
{
 priv->caps = (struct ptp_clock_info) {
  .owner  = THIS_MODULE,
  .name  = "NXP C45 PHC",
  .max_adj = 16666666,
  .adjfine = nxp_c45_ptp_adjfine,
  .adjtime = nxp_c45_ptp_adjtime,
  .gettimex64 = nxp_c45_ptp_gettimex64,
  .settime64 = nxp_c45_ptp_settime64,
  .enable  = nxp_c45_ptp_enable,
  .verify  = nxp_c45_ptp_verify_pin,
  .do_aux_work = nxp_c45_do_aux_work,
  .pin_config = nxp_c45_ptp_pins,
  .n_pins  = ARRAY_SIZE(nxp_c45_ptp_pins),
  .n_ext_ts = 1,
  .n_per_out = 1,
  .supported_extts_flags = PTP_RISING_EDGE |
      PTP_FALLING_EDGE |
      PTP_STRICT_FLAGS,
  .supported_perout_flags = PTP_PEROUT_PHASE,
 };

 priv->ptp_clock = ptp_clock_register(&priv->caps,
          &priv->phydev->mdio.dev);

 if (IS_ERR(priv->ptp_clock))
  return PTR_ERR(priv->ptp_clock);

 if (!priv->ptp_clock)
  return -ENOMEM;

 return 0;
}

static void nxp_c45_txtstamp(struct mii_timestamper *mii_ts,
        struct sk_buff *skb, int type)
{
 struct nxp_c45_phy *priv = container_of(mii_ts, struct nxp_c45_phy,
      mii_ts);

 switch (priv->hwts_tx) {
 case HWTSTAMP_TX_ON:
  NXP_C45_SKB_CB(skb)->type = type;
  NXP_C45_SKB_CB(skb)->header = ptp_parse_header(skb, type);
  skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
  skb_queue_tail(&priv->tx_queue, skb);
  if (nxp_c45_poll_txts(priv->phydev))
   ptp_schedule_worker(priv->ptp_clock, 0);
  break;
 case HWTSTAMP_TX_OFF:
 default:
  kfree_skb(skb);
  break;
 }
}

static bool nxp_c45_rxtstamp(struct mii_timestamper *mii_ts,
        struct sk_buff *skb, int type)
{
 struct nxp_c45_phy *priv = container_of(mii_ts, struct nxp_c45_phy,
      mii_ts);
 struct ptp_header *header = ptp_parse_header(skb, type);

 if (!header)
  return false;

 if (!priv->hwts_rx)
  return false;

 NXP_C45_SKB_CB(skb)->header = header;
 skb_queue_tail(&priv->rx_queue, skb);
 ptp_schedule_worker(priv->ptp_clock, 0);

 return true;
}

static int nxp_c45_hwtstamp(struct mii_timestamper *mii_ts,
       struct kernel_hwtstamp_config *cfg,
       struct netlink_ext_ack *extack)
{
 struct nxp_c45_phy *priv = container_of(mii_ts, struct nxp_c45_phy,
      mii_ts);
 struct phy_device *phydev = priv->phydev;
 const struct nxp_c45_phy_data *data;

 if (cfg->tx_type < 0 || cfg->tx_type > HWTSTAMP_TX_ON)
  return -ERANGE;

 data = nxp_c45_get_data(phydev);
 priv->hwts_tx = cfg->tx_type;

 switch (cfg->rx_filter) {
 case HWTSTAMP_FILTER_NONE:
  priv->hwts_rx = 0;
  break;
 case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
 case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
 case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
  priv->hwts_rx = 1;
  cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
  break;
 default:
  return -ERANGE;
 }

 if (priv->hwts_rx || priv->hwts_tx) {
  phy_write_mmd(phydev, MDIO_MMD_VEND1,
         data->regmap->vend1_event_msg_filt,
         EVENT_MSG_FILT_ALL);
  data->ptp_enable(phydev, true);
 } else {
  phy_write_mmd(phydev, MDIO_MMD_VEND1,
         data->regmap->vend1_event_msg_filt,
         EVENT_MSG_FILT_NONE);
  data->ptp_enable(phydev, false);
 }

 if (nxp_c45_poll_txts(priv->phydev))
  goto nxp_c45_no_ptp_irq;

 if (priv->hwts_tx)
  nxp_c45_set_reg_field(phydev, &data->regmap->irq_egr_ts_en);
 else
  nxp_c45_clear_reg_field(phydev, &data->regmap->irq_egr_ts_en);

nxp_c45_no_ptp_irq:
 return 0;
}

static int nxp_c45_ts_info(struct mii_timestamper *mii_ts,
      struct kernel_ethtool_ts_info *ts_info)
{
 struct nxp_c45_phy *priv = container_of(mii_ts, struct nxp_c45_phy,
      mii_ts);

 ts_info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
   SOF_TIMESTAMPING_RX_HARDWARE |
   SOF_TIMESTAMPING_RAW_HARDWARE;
 ts_info->phc_index = ptp_clock_index(priv->ptp_clock);
 ts_info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON);
 ts_info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
   (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) |
   (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) |
   (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT);

 return 0;
}

static const struct nxp_c45_phy_stats common_hw_stats[] = {
 { "phy_link_status_drop_cnt",
  NXP_C45_REG_FIELD(0x8352, MDIO_MMD_VEND1, 8, 6), },
 { "phy_link_availability_drop_cnt",
  NXP_C45_REG_FIELD(0x8352, MDIO_MMD_VEND1, 0, 6), },
 { "phy_link_loss_cnt",
  NXP_C45_REG_FIELD(0x8353, MDIO_MMD_VEND1, 10, 6), },
 { "phy_link_failure_cnt",
  NXP_C45_REG_FIELD(0x8353, MDIO_MMD_VEND1, 0, 10), },
 { "phy_symbol_error_cnt",
  NXP_C45_REG_FIELD(0x8350, MDIO_MMD_VEND1, 0, 16) },
};

static const struct nxp_c45_phy_stats tja1103_hw_stats[] = {
 { "rx_preamble_count",
  NXP_C45_REG_FIELD(0xAFCE, MDIO_MMD_VEND1, 0, 6), },
 { "tx_preamble_count",
  NXP_C45_REG_FIELD(0xAFCF, MDIO_MMD_VEND1, 0, 6), },
 { "rx_ipg_length",
  NXP_C45_REG_FIELD(0xAFD0, MDIO_MMD_VEND1, 0, 9), },
 { "tx_ipg_length",
  NXP_C45_REG_FIELD(0xAFD1, MDIO_MMD_VEND1, 0, 9), },
};

static const struct nxp_c45_phy_stats tja1120_hw_stats[] = {
 { "phy_symbol_error_cnt_ext",
  NXP_C45_REG_FIELD(0x8351, MDIO_MMD_VEND1, 0, 14) },
 { "tx_frames_xtd",
  NXP_C45_REG_FIELD(0xACA1, MDIO_MMD_VEND1, 0, 8), },
 { "tx_frames",
  NXP_C45_REG_FIELD(0xACA0, MDIO_MMD_VEND1, 0, 16), },
 { "rx_frames_xtd",
  NXP_C45_REG_FIELD(0xACA3, MDIO_MMD_VEND1, 0, 8), },
 { "rx_frames",
  NXP_C45_REG_FIELD(0xACA2, MDIO_MMD_VEND1, 0, 16), },
 { "tx_lost_frames_xtd",
  NXP_C45_REG_FIELD(0xACA5, MDIO_MMD_VEND1, 0, 8), },
 { "tx_lost_frames",
  NXP_C45_REG_FIELD(0xACA4, MDIO_MMD_VEND1, 0, 16), },
 { "rx_lost_frames_xtd",
  NXP_C45_REG_FIELD(0xACA7, MDIO_MMD_VEND1, 0, 8), },
 { "rx_lost_frames",
  NXP_C45_REG_FIELD(0xACA6, MDIO_MMD_VEND1, 0, 16), },
};

static int nxp_c45_get_sset_count(struct phy_device *phydev)
{
 const struct nxp_c45_phy_data *phy_data = nxp_c45_get_data(phydev);

 return ARRAY_SIZE(common_hw_stats) + (phy_data ? phy_data->n_stats : 0);
}

static void nxp_c45_get_strings(struct phy_device *phydev, u8 *data)
{
 const struct nxp_c45_phy_data *phy_data = nxp_c45_get_data(phydev);
 size_t count = nxp_c45_get_sset_count(phydev);
 size_t idx;
 size_t i;

 for (i = 0; i < count; i++) {
  if (i < ARRAY_SIZE(common_hw_stats)) {
   ethtool_puts(&data, common_hw_stats[i].name);
   continue;
  }
  idx = i - ARRAY_SIZE(common_hw_stats);
  ethtool_puts(&data, phy_data->stats[idx].name);
 }
}

static void nxp_c45_get_stats(struct phy_device *phydev,
         struct ethtool_stats *stats, u64 *data)
{
 const struct nxp_c45_phy_data *phy_data = nxp_c45_get_data(phydev);
 size_t count = nxp_c45_get_sset_count(phydev);
 const struct nxp_c45_reg_field *reg_field;
 size_t idx;
 size_t i;
 int ret;

 for (i = 0; i < count; i++) {
  if (i < ARRAY_SIZE(common_hw_stats)) {
   reg_field = &common_hw_stats[i].counter;
  } else {
   idx = i - ARRAY_SIZE(common_hw_stats);
   reg_field = &phy_data->stats[idx].counter;
  }

  ret = nxp_c45_read_reg_field(phydev, reg_field);
  if (ret < 0)
   data[i] = U64_MAX;
  else
   data[i] = ret;
 }
}

static int nxp_c45_config_enable(struct phy_device *phydev)
{
 phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_CONTROL,
        DEVICE_CONTROL_CONFIG_GLOBAL_EN |
        DEVICE_CONTROL_CONFIG_ALL_EN);
 usleep_range(400, 450);

 phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_CONTROL,
        PORT_CONTROL_EN);
 phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONTROL,
        PHY_CONFIG_EN);
 phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_INFRA_CONTROL,
        PORT_INFRA_CONTROL_EN);

 return 0;
}

static int nxp_c45_start_op(struct phy_device *phydev)
{
 return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONTROL,
    PHY_START_OP);
}

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

 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
  ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
           VEND1_PORT_FUNC_IRQ_EN, MACSEC_IRQS);
  if (ret)
   return ret;

  return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
     VEND1_PHY_IRQ_EN, PHY_IRQ_LINK_EVENT);
 }

 ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
     VEND1_PORT_FUNC_IRQ_EN, MACSEC_IRQS);
 if (ret)
  return ret;

 return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
      VEND1_PHY_IRQ_EN, PHY_IRQ_LINK_EVENT);
}

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

 /* We can't disable the FUSA IRQ for TJA1103, but we can clean it up. */
 ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_ALWAYS_ACCESSIBLE,
       FUSA_PASS);
 if (ret)
  return ret;

 return nxp_c45_config_intr(phydev);
}

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

 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
  ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
           TJA1120_GLOBAL_INFRA_IRQ_EN,
           TJA1120_DEV_BOOT_DONE);
 else
  ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
      TJA1120_GLOBAL_INFRA_IRQ_EN,
      TJA1120_DEV_BOOT_DONE);
 if (ret)
  return ret;

 return nxp_c45_config_intr(phydev);
}

static irqreturn_t nxp_c45_handle_interrupt(struct phy_device *phydev)
{
 const struct nxp_c45_phy_data *data = nxp_c45_get_data(phydev);
 struct nxp_c45_phy *priv = phydev->priv;
 irqreturn_t ret = IRQ_NONE;
 struct nxp_c45_hwts hwts;
 int irq;

 irq = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_IRQ_STATUS);
 if (irq & PHY_IRQ_LINK_EVENT) {
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_IRQ_ACK,
         PHY_IRQ_LINK_EVENT);
  phy_trigger_machine(phydev);
  ret = IRQ_HANDLED;
 }

 irq = nxp_c45_read_reg_field(phydev, &data->regmap->irq_egr_ts_status);
 if (irq) {
  /* If ack_ptp_irq is false, the IRQ bit is self-clear and will
 * be cleared when the EGR TS FIFO is empty. Otherwise, the
 * IRQ bit should be cleared before reading the timestamp,
 */

  if (data->ack_ptp_irq)
   phy_write_mmd(phydev, MDIO_MMD_VEND1,
          VEND1_PTP_IRQ_ACK, EGR_TS_IRQ);
  while (data->get_egressts(priv, &hwts))
   nxp_c45_process_txts(priv, &hwts);

  ret = IRQ_HANDLED;
 }

 data->nmi_handler(phydev, &ret);
 nxp_c45_handle_macsec_interrupt(phydev, &ret);

 return ret;
}

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

 ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_CONTROL,
       DEVICE_CONTROL_RESET);
 if (ret)
  return ret;

 usleep_range(2000, 2050);

 return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
      VEND1_DEVICE_CONTROL, ret,
      !(ret & DEVICE_CONTROL_RESET), 20000,
      240000, false);
}

static int nxp_c45_cable_test_start(struct phy_device *phydev)
{
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(phydev);

 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
    VEND1_PORT_FUNC_ENABLES, PHY_TEST_ENABLE);
 return phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, regmap->cable_test,
    CABLE_TEST_ENABLE | CABLE_TEST_START);
}

static int nxp_c45_cable_test_get_status(struct phy_device *phydev,
      bool *finished)
{
 const struct nxp_c45_regmap *regmap = nxp_c45_get_regmap(phydev);
 int ret;
 u8 cable_test_result;

 ret = nxp_c45_read_reg_field(phydev, ®map->cable_test_valid);
 if (!ret) {
  *finished = false;
  return 0;
 }

 *finished = true;
 cable_test_result = nxp_c45_read_reg_field(phydev,
         ®map->cable_test_result);

 switch (cable_test_result) {
 case CABLE_TEST_OK:
  ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
     ETHTOOL_A_CABLE_RESULT_CODE_OK);
  break;
 case CABLE_TEST_SHORTED:
  ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
     ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT);
  break;
 case CABLE_TEST_OPEN:
  ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
     ETHTOOL_A_CABLE_RESULT_CODE_OPEN);
  break;
 default:
  ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
     ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC);
 }

 phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, regmap->cable_test,
      CABLE_TEST_ENABLE);
 phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
      VEND1_PORT_FUNC_ENABLES, PHY_TEST_ENABLE);

 return nxp_c45_start_op(phydev);
}

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

 reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_SIGNAL_QUALITY);
 if (!(reg & SQI_VALID))
  return -EINVAL;

 reg &= SQI_MASK;

 return reg;
}

static void tja1120_link_change_notify(struct phy_device *phydev)
{
 /* Bug workaround for TJA1120 enegineering samples: fix egress
 * timestamps lost after link recovery.
 */

 if (phydev->state == PHY_NOLINK) {
  phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
     TJA1120_EPHY_RESETS, EPHY_PCS_RESET);
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
       TJA1120_EPHY_RESETS, EPHY_PCS_RESET);
 }
}

static int nxp_c45_get_sqi_max(struct phy_device *phydev)
{
 return MAX_SQI;
}

static int nxp_c45_check_delay(struct phy_device *phydev, u32 delay)
{
 if (delay < MIN_ID_PS) {
  phydev_err(phydev, "delay value smaller than %u\n", MIN_ID_PS);
  return -EINVAL;
 }

 if (delay > MAX_ID_PS) {
  phydev_err(phydev, "delay value higher than %u\n", MAX_ID_PS);
  return -EINVAL;
 }

 return 0;
}

static void nxp_c45_counters_enable(struct phy_device *phydev)
{
 const struct nxp_c45_phy_data *data = nxp_c45_get_data(phydev);

 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_LINK_DROP_COUNTER,
    COUNTER_EN);

 data->counters_enable(phydev);
}

static void nxp_c45_ptp_init(struct phy_device *phydev)
{
 const struct nxp_c45_phy_data *data = nxp_c45_get_data(phydev);

 phy_write_mmd(phydev, MDIO_MMD_VEND1,
        data->regmap->vend1_ptp_clk_period,
        data->ptp_clk_period);
 nxp_c45_clear_reg_field(phydev, &data->regmap->ltc_lock_ctrl);

 data->ptp_init(phydev);
}

static u64 nxp_c45_get_phase_shift(u64 phase_offset_raw)
{
 /* The delay in degree phase is 73.8 + phase_offset_raw * 0.9.
 * To avoid floating point operations we'll multiply by 10
 * and get 1 decimal point precision.
 */

 phase_offset_raw *= 10;
 phase_offset_raw -= 738;
 return div_u64(phase_offset_raw, 9);
}

static void nxp_c45_disable_delays(struct phy_device *phydev)
{
 phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID, ID_ENABLE);
 phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID, ID_ENABLE);
}

static void nxp_c45_set_delays(struct phy_device *phydev)
{
 struct nxp_c45_phy *priv = phydev->priv;
 u64 tx_delay = priv->tx_delay;
 u64 rx_delay = priv->rx_delay;
 u64 degree;

 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
     phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
  degree = div_u64(tx_delay, PS_PER_DEGREE);
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID,
         ID_ENABLE | nxp_c45_get_phase_shift(degree));
 } else {
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID,
       ID_ENABLE);
 }

 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
     phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
  degree = div_u64(rx_delay, PS_PER_DEGREE);
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID,
         ID_ENABLE | nxp_c45_get_phase_shift(degree));
 } else {
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID,
       ID_ENABLE);
 }
}

static int nxp_c45_get_delays(struct phy_device *phydev)
{
 struct nxp_c45_phy *priv = phydev->priv;
 int ret;

 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
     phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
  ret = device_property_read_u32(&phydev->mdio.dev,
            "tx-internal-delay-ps",
            &priv->tx_delay);
  if (ret)
   priv->tx_delay = DEFAULT_ID_PS;

  ret = nxp_c45_check_delay(phydev, priv->tx_delay);
  if (ret) {
   phydev_err(phydev,
       "tx-internal-delay-ps invalid value\n");
   return ret;
  }
 }

 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
     phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
  ret = device_property_read_u32(&phydev->mdio.dev,
            "rx-internal-delay-ps",
            &priv->rx_delay);
  if (ret)
   priv->rx_delay = DEFAULT_ID_PS;

  ret = nxp_c45_check_delay(phydev, priv->rx_delay);
  if (ret) {
   phydev_err(phydev,
       "rx-internal-delay-ps invalid value\n");
   return ret;
  }
 }

 return 0;
}

static int nxp_c45_set_phy_mode(struct phy_device *phydev)
{
 struct nxp_c45_phy *priv = phydev->priv;
 u16 basic_config;
 int ret;

 ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_ABILITIES);
 phydev_dbg(phydev, "Clause 45 managed PHY abilities 0x%x\n", ret);

 switch (phydev->interface) {
 case PHY_INTERFACE_MODE_RGMII:
  if (!(ret & RGMII_ABILITY)) {
   phydev_err(phydev, "rgmii mode not supported\n");
   return -EINVAL;
  }
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG,
         MII_BASIC_CONFIG_RGMII);
  nxp_c45_disable_delays(phydev);
  break;
 case PHY_INTERFACE_MODE_RGMII_ID:
 case PHY_INTERFACE_MODE_RGMII_TXID:
 case PHY_INTERFACE_MODE_RGMII_RXID:
  if (!(ret & RGMII_ID_ABILITY)) {
   phydev_err(phydev, "rgmii-id, rgmii-txid, rgmii-rxid modes are not supported\n");
   return -EINVAL;
  }
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG,
         MII_BASIC_CONFIG_RGMII);
  ret = nxp_c45_get_delays(phydev);
  if (ret)
   return ret;

  nxp_c45_set_delays(phydev);
  break;
 case PHY_INTERFACE_MODE_MII:
  if (!(ret & MII_ABILITY)) {
   phydev_err(phydev, "mii mode not supported\n");
   return -EINVAL;
  }
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG,
         MII_BASIC_CONFIG_MII);
  break;
 case PHY_INTERFACE_MODE_REVMII:
  if (!(ret & REVMII_ABILITY)) {
   phydev_err(phydev, "rev-mii mode not supported\n");
   return -EINVAL;
  }
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG,
         MII_BASIC_CONFIG_MII | MII_BASIC_CONFIG_REV);
  break;
 case PHY_INTERFACE_MODE_RMII:
  if (!(ret & RMII_ABILITY)) {
   phydev_err(phydev, "rmii mode not supported\n");
   return -EINVAL;
  }

  basic_config = MII_BASIC_CONFIG_RMII;

  /* This is not PHY_INTERFACE_MODE_REVRMII */
  if (priv->flags & TJA11XX_REVERSE_MODE)
   basic_config |= MII_BASIC_CONFIG_REV;

  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG,
         basic_config);
  break;
 case PHY_INTERFACE_MODE_SGMII:
  if (!(ret & SGMII_ABILITY)) {
   phydev_err(phydev, "sgmii mode not supported\n");
   return -EINVAL;
  }
  phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG,
         MII_BASIC_CONFIG_SGMII);
  break;
 case PHY_INTERFACE_MODE_INTERNAL:
  break;
 default:
  return -EINVAL;
 }

 return 0;
}

/* Errata: ES_TJA1120 and ES_TJA1121 Rev. 1.0 — 28 November 2024 Section 3.1 & 3.2 */
static void nxp_c45_tja1120_errata(struct phy_device *phydev)
{
 bool macsec_ability, sgmii_ability;
 int silicon_version, sample_type;
 int phy_abilities;
 int ret = 0;

 ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_ID3);
 if (ret < 0)
  return;

 sample_type = FIELD_GET(TJA1120_DEV_ID3_SAMPLE_TYPE, ret);
 if (sample_type != DEVICE_ID3_SAMPLE_TYPE_R)
  return;

 silicon_version = FIELD_GET(TJA1120_DEV_ID3_SILICON_VERSION, ret);

 phy_abilities = phy_read_mmd(phydev, MDIO_MMD_VEND1,
         VEND1_PORT_ABILITIES);
 macsec_ability = !!(phy_abilities & MACSEC_ABILITY);
 sgmii_ability = !!(phy_abilities & SGMII_ABILITY);
 if ((!macsec_ability && silicon_version == 2) ||
     (macsec_ability && silicon_version == 1)) {
  /* TJA1120/TJA1121 PHY configuration errata workaround.
 * Apply PHY writes sequence before link up.
 */

  if (!macsec_ability) {
   phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F8, 0x4b95);
   phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F9, 0xf3cd);
  } else {
   phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F8, 0x89c7);
   phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F9, 0x0893);
  }

  phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x0476, 0x58a0);

  phy_write_mmd(phydev, MDIO_MMD_PMAPMD, 0x8921, 0xa3a);
  phy_write_mmd(phydev, MDIO_MMD_PMAPMD, 0x89F1, 0x16c1);

  phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F8, 0x0);
  phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F9, 0x0);

  if (sgmii_ability) {
   /* TJA1120B/TJA1121B SGMII PCS restart errata workaround.
 * Put SGMII PCS into power down mode and back up.
 */

   phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
      VEND1_SGMII_BASIC_CONTROL,
      SGMII_LPM);
   phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
        VEND1_SGMII_BASIC_CONTROL,
        SGMII_LPM);
  }
 }
}

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

 ret = nxp_c45_config_enable(phydev);
 if (ret) {
  phydev_err(phydev, "Failed to enable config\n");
  return ret;
 }

 /* Bug workaround for SJA1110 rev B: enable write access
 * to MDIO_MMD_PMAPMD
 */

 phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F8, 1);
 phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x01F9, 2);

 if (phy_id_compare(phydev->phy_id, PHY_ID_TJA_1120, GENMASK(31, 4)))
  nxp_c45_tja1120_errata(phydev);

 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONFIG,
    PHY_CONFIG_AUTO);

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

 phydev->autoneg = AUTONEG_DISABLE;

 nxp_c45_counters_enable(phydev);
 nxp_c45_ptp_init(phydev);
 ret = nxp_c45_macsec_config_init(phydev);
 if (ret)
  return ret;

 return nxp_c45_start_op(phydev);
}

static int nxp_c45_get_features(struct phy_device *phydev)
{
 linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT, phydev->supported);
 linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, phydev->supported);

 return genphy_c45_pma_read_abilities(phydev);
}

static int nxp_c45_parse_dt(struct phy_device *phydev)
{
 struct device_node *node = phydev->mdio.dev.of_node;
 struct nxp_c45_phy *priv = phydev->priv;

 if (!IS_ENABLED(CONFIG_OF_MDIO))
  return 0;

 if (of_property_read_bool(node, "nxp,rmii-refclk-out"))
  priv->flags |= TJA11XX_REVERSE_MODE;

 return 0;
}

static int nxp_c45_probe(struct phy_device *phydev)
{
 struct nxp_c45_phy *priv;
 bool macsec_ability;
 int phy_abilities;
 bool ptp_ability;
 int ret = 0;

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

 skb_queue_head_init(&priv->tx_queue);
 skb_queue_head_init(&priv->rx_queue);

 priv->phydev = phydev;

 phydev->priv = priv;

 nxp_c45_parse_dt(phydev);

 mutex_init(&priv->ptp_lock);

 phy_abilities = phy_read_mmd(phydev, MDIO_MMD_VEND1,
         VEND1_PORT_ABILITIES);
 ptp_ability = !!(phy_abilities & PTP_ABILITY);
 if (!ptp_ability) {
  phydev_dbg(phydev, "the phy does not support PTP");
  goto no_ptp_support;
 }

 if (IS_ENABLED(CONFIG_PTP_1588_CLOCK) &&
     IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING)) {
  priv->mii_ts.rxtstamp = nxp_c45_rxtstamp;
  priv->mii_ts.txtstamp = nxp_c45_txtstamp;
  priv->mii_ts.hwtstamp = nxp_c45_hwtstamp;
  priv->mii_ts.ts_info = nxp_c45_ts_info;
  phydev->mii_ts = &priv->mii_ts;
  ret = nxp_c45_init_ptp_clock(priv);

  /* Timestamp selected by default to keep legacy API */
  phydev->default_timestamp = true;
 } else {
  phydev_dbg(phydev, "PTP support not enabled even if the phy supports it");
 }

no_ptp_support:
 macsec_ability = !!(phy_abilities & MACSEC_ABILITY);
 if (!macsec_ability) {
  phydev_info(phydev, "the phy does not support MACsec\n");
  goto no_macsec_support;
 }

 if (IS_ENABLED(CONFIG_MACSEC)) {
  ret = nxp_c45_macsec_probe(phydev);
  phydev_dbg(phydev, "MACsec support enabled.");
 } else {
  phydev_dbg(phydev, "MACsec support not enabled even if the phy supports it");
 }

no_macsec_support:

 return ret;
}

static void nxp_c45_remove(struct phy_device *phydev)
{
 struct nxp_c45_phy *priv = phydev->priv;

 if (priv->ptp_clock)
  ptp_clock_unregister(priv->ptp_clock);

 skb_queue_purge(&priv->tx_queue);
 skb_queue_purge(&priv->rx_queue);
 nxp_c45_macsec_remove(phydev);
}

static void tja1103_counters_enable(struct phy_device *phydev)
{
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RX_PREAMBLE_COUNT,
    COUNTER_EN);
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TX_PREAMBLE_COUNT,
    COUNTER_EN);
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_RX_IPG_LENGTH,
    COUNTER_EN);
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_TX_IPG_LENGTH,
    COUNTER_EN);
}

static void tja1103_ptp_init(struct phy_device *phydev)
{
 phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_RX_TS_INSRT_CTRL,
        TJA1103_RX_TS_INSRT_MODE2);
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_FUNC_ENABLES,
    PTP_ENABLE);
}

static void tja1103_ptp_enable(struct phy_device *phydev, bool enable)
{
 if (enable)
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
       VEND1_PORT_PTP_CONTROL,
       PORT_PTP_CONTROL_BYPASS);
 else
  phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
     VEND1_PORT_PTP_CONTROL,
     PORT_PTP_CONTROL_BYPASS);
}

static void tja1103_nmi_handler(struct phy_device *phydev,
    irqreturn_t *irq_status)
{
 int ret;

 ret = phy_read_mmd(phydev, MDIO_MMD_VEND1,
      VEND1_ALWAYS_ACCESSIBLE);
 if (ret & FUSA_PASS) {
  phy_write_mmd(phydev, MDIO_MMD_VEND1,
         VEND1_ALWAYS_ACCESSIBLE,
         FUSA_PASS);
  *irq_status = IRQ_HANDLED;
 }
}

static const struct nxp_c45_regmap tja1103_regmap = {
 .vend1_ptp_clk_period = 0x1104,
 .vend1_event_msg_filt = 0x1148,
 .pps_enable  =
  NXP_C45_REG_FIELD(0x1102, MDIO_MMD_VEND1, 3, 1),
 .pps_polarity  =
  NXP_C45_REG_FIELD(0x1102, MDIO_MMD_VEND1, 2, 1),
 .ltc_lock_ctrl  =
  NXP_C45_REG_FIELD(0x1115, MDIO_MMD_VEND1, 0, 1),
 .ltc_read  =
  NXP_C45_REG_FIELD(0x1105, MDIO_MMD_VEND1, 2, 1),
 .ltc_write  =
  NXP_C45_REG_FIELD(0x1105, MDIO_MMD_VEND1, 0, 1),
 .vend1_ltc_wr_nsec_0 = 0x1106,
 .vend1_ltc_wr_nsec_1 = 0x1107,
 .vend1_ltc_wr_sec_0 = 0x1108,
 .vend1_ltc_wr_sec_1 = 0x1109,
 .vend1_ltc_rd_nsec_0 = 0x110A,
 .vend1_ltc_rd_nsec_1 = 0x110B,
 .vend1_ltc_rd_sec_0 = 0x110C,
 .vend1_ltc_rd_sec_1 = 0x110D,
 .vend1_rate_adj_subns_0 = 0x110F,
 .vend1_rate_adj_subns_1 = 0x1110,
 .irq_egr_ts_en  =
  NXP_C45_REG_FIELD(0x1131, MDIO_MMD_VEND1, 0, 1),
 .irq_egr_ts_status =
  NXP_C45_REG_FIELD(0x1132, MDIO_MMD_VEND1, 0, 1),
 .domain_number  =
  NXP_C45_REG_FIELD(0x114E, MDIO_MMD_VEND1, 0, 8),
 .msg_type  =
  NXP_C45_REG_FIELD(0x114E, MDIO_MMD_VEND1, 8, 4),
 .sequence_id  =
  NXP_C45_REG_FIELD(0x114F, MDIO_MMD_VEND1, 0, 16),
 .sec_1_0  =
  NXP_C45_REG_FIELD(0x1151, MDIO_MMD_VEND1, 14, 2),
 .sec_4_2  =
  NXP_C45_REG_FIELD(0x114E, MDIO_MMD_VEND1, 12, 3),
 .nsec_15_0  =
  NXP_C45_REG_FIELD(0x1150, MDIO_MMD_VEND1, 0, 16),
 .nsec_29_16  =
  NXP_C45_REG_FIELD(0x1151, MDIO_MMD_VEND1, 0, 14),
 .vend1_ext_trg_data_0 = 0x1121,
 .vend1_ext_trg_data_1 = 0x1122,
 .vend1_ext_trg_data_2 = 0x1123,
 .vend1_ext_trg_data_3 = 0x1124,
 .vend1_ext_trg_ctrl = 0x1126,
 .cable_test  = 0x8330,
 .cable_test_valid =
  NXP_C45_REG_FIELD(0x8330, MDIO_MMD_VEND1, 13, 1),
 .cable_test_result =
  NXP_C45_REG_FIELD(0x8330, MDIO_MMD_VEND1, 0, 3),
};

static const struct nxp_c45_phy_data tja1103_phy_data = {
 .regmap = &tja1103_regmap,
 .stats = tja1103_hw_stats,
 .n_stats = ARRAY_SIZE(tja1103_hw_stats),
 .ptp_clk_period = PTP_CLK_PERIOD_100BT1,
 .ext_ts_both_edges = false,
 .ack_ptp_irq = false,
 .counters_enable = tja1103_counters_enable,
 .get_egressts = nxp_c45_get_hwtxts,
 .get_extts = nxp_c45_get_extts,
 .ptp_init = tja1103_ptp_init,
 .ptp_enable = tja1103_ptp_enable,
 .nmi_handler = tja1103_nmi_handler,
};

static void tja1120_counters_enable(struct phy_device *phydev)
{
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_SYMBOL_ERROR_CNT_XTD,
    EXTENDED_CNT_EN);
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_MONITOR_STATUS,
    MONITOR_RESET);
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_MONITOR_CONFIG,
    ALL_FRAMES_CNT_EN | LOST_FRAMES_CNT_EN);
}

static void tja1120_ptp_init(struct phy_device *phydev)
{
 phy_write_mmd(phydev, MDIO_MMD_VEND1, TJA1120_RX_TS_INSRT_CTRL,
        TJA1120_RX_TS_INSRT_EN | TJA1120_TS_INSRT_MODE);
 phy_write_mmd(phydev, MDIO_MMD_VEND1, TJA1120_VEND1_EXT_TS_MODE,
        TJA1120_TS_INSRT_MODE);
 phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_CONFIG,
    PTP_ENABLE);
}

static void tja1120_ptp_enable(struct phy_device *phydev, bool enable)
{
 if (enable)
  phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
     VEND1_PORT_FUNC_ENABLES,
     PTP_ENABLE);
 else
  phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
       VEND1_PORT_FUNC_ENABLES,
       PTP_ENABLE);
}

static void tja1120_nmi_handler(struct phy_device *phydev,
    irqreturn_t *irq_status)
{
 int ret;

 ret = phy_read_mmd(phydev, MDIO_MMD_VEND1,
      TJA1120_GLOBAL_INFRA_IRQ_STATUS);
 if (ret & TJA1120_DEV_BOOT_DONE) {
  phy_write_mmd(phydev, MDIO_MMD_VEND1,
         TJA1120_GLOBAL_INFRA_IRQ_ACK,
         TJA1120_DEV_BOOT_DONE);
  *irq_status = IRQ_HANDLED;
 }
}

static int nxp_c45_macsec_ability(struct phy_device *phydev)
{
 bool macsec_ability;
 int phy_abilities;

 phy_abilities = phy_read_mmd(phydev, MDIO_MMD_VEND1,
         VEND1_PORT_ABILITIES);
 macsec_ability = !!(phy_abilities & MACSEC_ABILITY);

 return macsec_ability;
}

static bool tja11xx_phy_id_compare(struct phy_device *phydev,
       const struct phy_driver *phydrv)
{
 u32 id = phydev->is_c45 ? phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] :
      phydev->phy_id;

 return phy_id_compare(id, phydrv->phy_id, phydrv->phy_id_mask);
}

static int tja11xx_no_macsec_match_phy_device(struct phy_device *phydev,
           const struct phy_driver *phydrv)
{
 return tja11xx_phy_id_compare(phydev, phydrv) &&
        !nxp_c45_macsec_ability(phydev);
}

static int tja11xx_macsec_match_phy_device(struct phy_device *phydev,
        const struct phy_driver *phydrv)
{
 return tja11xx_phy_id_compare(phydev, phydrv) &&
        nxp_c45_macsec_ability(phydev);
}

static const struct nxp_c45_regmap tja1120_regmap = {
 .vend1_ptp_clk_period = 0x1020,
 .vend1_event_msg_filt = 0x9010,
 .pps_enable  =
  NXP_C45_REG_FIELD(0x1006, MDIO_MMD_VEND1, 4, 1),
 .pps_polarity  =
  NXP_C45_REG_FIELD(0x1006, MDIO_MMD_VEND1, 5, 1),
 .ltc_lock_ctrl  =
  NXP_C45_REG_FIELD(0x1006, MDIO_MMD_VEND1, 2, 1),
 .ltc_read  =
  NXP_C45_REG_FIELD(0x1000, MDIO_MMD_VEND1, 1, 1),
 .ltc_write  =
  NXP_C45_REG_FIELD(0x1000, MDIO_MMD_VEND1, 2, 1),
 .vend1_ltc_wr_nsec_0 = 0x1040,
 .vend1_ltc_wr_nsec_1 = 0x1041,
 .vend1_ltc_wr_sec_0 = 0x1042,
 .vend1_ltc_wr_sec_1 = 0x1043,
 .vend1_ltc_rd_nsec_0 = 0x1048,
 .vend1_ltc_rd_nsec_1 = 0x1049,
 .vend1_ltc_rd_sec_0 = 0x104A,
 .vend1_ltc_rd_sec_1 = 0x104B,
 .vend1_rate_adj_subns_0 = 0x1030,
 .vend1_rate_adj_subns_1 = 0x1031,
 .irq_egr_ts_en  =
  NXP_C45_REG_FIELD(0x900A, MDIO_MMD_VEND1, 1, 1),
 .irq_egr_ts_status =
  NXP_C45_REG_FIELD(0x900C, MDIO_MMD_VEND1, 1, 1),
 .domain_number  =
  NXP_C45_REG_FIELD(0x9061, MDIO_MMD_VEND1, 8, 8),
 .msg_type  =
  NXP_C45_REG_FIELD(0x9061, MDIO_MMD_VEND1, 4, 4),
 .sequence_id  =
  NXP_C45_REG_FIELD(0x9062, MDIO_MMD_VEND1, 0, 16),
 .sec_1_0  =
  NXP_C45_REG_FIELD(0x9065, MDIO_MMD_VEND1, 0, 2),
 .sec_4_2  =
  NXP_C45_REG_FIELD(0x9065, MDIO_MMD_VEND1, 2, 3),
 .nsec_15_0  =
  NXP_C45_REG_FIELD(0x9063, MDIO_MMD_VEND1, 0, 16),
 .nsec_29_16  =
  NXP_C45_REG_FIELD(0x9064, MDIO_MMD_VEND1, 0, 14),
 .vend1_ext_trg_data_0 = 0x1071,
 .vend1_ext_trg_data_1 = 0x1072,
 .vend1_ext_trg_data_2 = 0x1073,
 .vend1_ext_trg_data_3 = 0x1074,
 .vend1_ext_trg_ctrl = 0x1075,
 .cable_test  = 0x8360,
 .cable_test_valid =
  NXP_C45_REG_FIELD(0x8361, MDIO_MMD_VEND1, 15, 1),
 .cable_test_result =
  NXP_C45_REG_FIELD(0x8361, MDIO_MMD_VEND1, 0, 3),
};

static const struct nxp_c45_phy_data tja1120_phy_data = {
 .regmap = &tja1120_regmap,
 .stats = tja1120_hw_stats,
 .n_stats = ARRAY_SIZE(tja1120_hw_stats),
 .ptp_clk_period = PTP_CLK_PERIOD_1000BT1,
 .ext_ts_both_edges = true,
 .ack_ptp_irq = true,
 .counters_enable = tja1120_counters_enable,
 .get_egressts = tja1120_get_hwtxts,
 .get_extts = tja1120_get_extts,
 .ptp_init = tja1120_ptp_init,
 .ptp_enable = tja1120_ptp_enable,
 .nmi_handler = tja1120_nmi_handler,
};

static struct phy_driver nxp_c45_driver[] = {
 {
  PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103),
  .name   = "NXP C45 TJA1103",
  .get_features  = nxp_c45_get_features,
  .driver_data  = &tja1103_phy_data,
  .probe   = nxp_c45_probe,
  .soft_reset  = nxp_c45_soft_reset,
  .config_aneg  = genphy_c45_config_aneg,
  .config_init  = nxp_c45_config_init,
  .config_intr  = tja1103_config_intr,
  .handle_interrupt = nxp_c45_handle_interrupt,
  .read_status  = genphy_c45_read_status,
  .suspend  = genphy_c45_pma_suspend,
  .resume   = genphy_c45_pma_resume,
  .get_sset_count  = nxp_c45_get_sset_count,
  .get_strings  = nxp_c45_get_strings,
  .get_stats  = nxp_c45_get_stats,
  .cable_test_start = nxp_c45_cable_test_start,
  .cable_test_get_status = nxp_c45_cable_test_get_status,
  .set_loopback  = genphy_c45_loopback,
  .get_sqi  = nxp_c45_get_sqi,
  .get_sqi_max  = nxp_c45_get_sqi_max,
  .remove   = nxp_c45_remove,
  .match_phy_device = tja11xx_no_macsec_match_phy_device,
 },
 {
  PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103),
  .name   = "NXP C45 TJA1104",
  .get_features  = nxp_c45_get_features,
  .driver_data  = &tja1103_phy_data,
  .probe   = nxp_c45_probe,
  .soft_reset  = nxp_c45_soft_reset,
  .config_aneg  = genphy_c45_config_aneg,
  .config_init  = nxp_c45_config_init,
  .config_intr  = tja1103_config_intr,
  .handle_interrupt = nxp_c45_handle_interrupt,
  .read_status  = genphy_c45_read_status,
  .suspend  = genphy_c45_pma_suspend,
  .resume   = genphy_c45_pma_resume,
  .get_sset_count  = nxp_c45_get_sset_count,
  .get_strings  = nxp_c45_get_strings,
  .get_stats  = nxp_c45_get_stats,
  .cable_test_start = nxp_c45_cable_test_start,
  .cable_test_get_status = nxp_c45_cable_test_get_status,
  .set_loopback  = genphy_c45_loopback,
  .get_sqi  = nxp_c45_get_sqi,
  .get_sqi_max  = nxp_c45_get_sqi_max,
  .remove   = nxp_c45_remove,
  .match_phy_device = tja11xx_macsec_match_phy_device,
 },
 {
  PHY_ID_MATCH_MODEL(PHY_ID_TJA_1120),
  .name   = "NXP C45 TJA1120",
  .get_features  = nxp_c45_get_features,
  .driver_data  = &tja1120_phy_data,
  .probe   = nxp_c45_probe,
  .soft_reset  = nxp_c45_soft_reset,
  .config_aneg  = genphy_c45_config_aneg,
  .config_init  = nxp_c45_config_init,
  .config_intr  = tja1120_config_intr,
  .handle_interrupt = nxp_c45_handle_interrupt,
  .read_status  = genphy_c45_read_status,
  .link_change_notify = tja1120_link_change_notify,
  .suspend  = genphy_c45_pma_suspend,
  .resume   = genphy_c45_pma_resume,
  .get_sset_count  = nxp_c45_get_sset_count,
  .get_strings  = nxp_c45_get_strings,
  .get_stats  = nxp_c45_get_stats,
  .cable_test_start = nxp_c45_cable_test_start,
  .cable_test_get_status = nxp_c45_cable_test_get_status,
  .set_loopback  = genphy_c45_loopback,
  .get_sqi  = nxp_c45_get_sqi,
  .get_sqi_max  = nxp_c45_get_sqi_max,
  .remove   = nxp_c45_remove,
  .match_phy_device = tja11xx_no_macsec_match_phy_device,
 },
 {
  PHY_ID_MATCH_MODEL(PHY_ID_TJA_1120),
  .name   = "NXP C45 TJA1121",
  .get_features  = nxp_c45_get_features,
  .driver_data  = &tja1120_phy_data,
  .probe   = nxp_c45_probe,
  .soft_reset  = nxp_c45_soft_reset,
  .config_aneg  = genphy_c45_config_aneg,
  .config_init  = nxp_c45_config_init,
  .config_intr  = tja1120_config_intr,
  .handle_interrupt = nxp_c45_handle_interrupt,
  .read_status  = genphy_c45_read_status,
  .link_change_notify = tja1120_link_change_notify,
  .suspend  = genphy_c45_pma_suspend,
  .resume   = genphy_c45_pma_resume,
  .get_sset_count  = nxp_c45_get_sset_count,
  .get_strings  = nxp_c45_get_strings,
  .get_stats  = nxp_c45_get_stats,
  .cable_test_start = nxp_c45_cable_test_start,
  .cable_test_get_status = nxp_c45_cable_test_get_status,
  .set_loopback  = genphy_c45_loopback,
  .get_sqi  = nxp_c45_get_sqi,
  .get_sqi_max  = nxp_c45_get_sqi_max,
  .remove   = nxp_c45_remove,
  .match_phy_device = tja11xx_macsec_match_phy_device,
 },
};

module_phy_driver(nxp_c45_driver);

static const struct mdio_device_id __maybe_unused nxp_c45_tbl[] = {
 { PHY_ID_MATCH_MODEL(PHY_ID_TJA_1103) },
 { PHY_ID_MATCH_MODEL(PHY_ID_TJA_1120) },
 { /*sentinel*/ },
};

MODULE_DEVICE_TABLE(mdio, nxp_c45_tbl);

MODULE_AUTHOR("Radu Pirea ");
MODULE_DESCRIPTION("NXP C45 PHY driver");
MODULE_LICENSE("GPL v2");

Messung V0.5
C=99 H=97 G=97

¤ Dauer der Verarbeitung: 0.24 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.