All of lore.kernel.org
 help / color / mirror / Atom feed
From: Divya Koppera <Divya.Koppera@microchip.com>
To: <andrew@lunn.ch>, <hkallweit1@gmail.com>, <linux@armlinux.org.uk>,
	<davem@davemloft.net>, <kuba@kernel.org>, <marex@denx.de>,
	<netdev@vger.kernel.org>, <linux-kernel@vger.kernel.org>
Cc: <UNGLinuxDriver@microchip.com>
Subject: [PATCH v2 net-next] net: phy: mchp: Add 1588 support for LAN8814 Quad PHY
Date: Mon, 14 Dec 2020 23:26:58 +0530	[thread overview]
Message-ID: <20201214175658.11138-1-Divya.Koppera@microchip.com> (raw)

This patch add supports for 1588 Hardware Timestamping support
to LAN8814 Quad Phy. It supports L2 and Ipv4 encapsulations.

Signed-off-by: Divya Koppera<divya.koppera@microchip.com>
---
v1 -> v2
* Fixed warnings
  Reported-by: kernel test robot <lkp@intel.com>
---
 drivers/net/phy/micrel.c | 1054 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 1048 insertions(+), 6 deletions(-)

diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 54e0d75203da..61b637567180 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -27,6 +27,14 @@
 #include <linux/of.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/ptp_clock_kernel.h>
+#include <linux/ptp_clock.h>
+#include <linux/ptp_classify.h>
+#include <linux/net_tstamp.h>
+#include <linux/netdevice.h>
+#include <linux/if_vlan.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
 
 /* Operation Mode Strap Override */
 #define MII_KSZPHY_OMSO				0x16
@@ -53,6 +61,136 @@
 #define	KSZPHY_INTCS_STATUS			(KSZPHY_INTCS_LINK_DOWN_STATUS |\
 						 KSZPHY_INTCS_LINK_UP_STATUS)
 
+/* Lan8814 general Interrupt control/status reg in GPHY specific block. */
+#define LAN8814_INTC				0x18
+#define LAN8814_INTC_JABBER			BIT(7)
+#define LAN8814_INTC_RECEIVE_ERR		BIT(6)
+#define LAN8814_INTC_PAGE_RECEIVE		BIT(5)
+#define LAN8814_INTC_PARELLEL			BIT(4)
+#define LAN8814_INTC_LINK_PARTNER_ACK		BIT(3)
+#define LAN8814_INTC_LINK_DOWN			BIT(2)
+#define LAN8814_INTC_REMOTE_FAULT		BIT(1)
+#define LAN8814_INTC_LINK_UP			BIT(0)
+#define LAN8814_INTC_ALL			(LAN8814_INTC_LINK_UP |\
+						 LAN8814_INTC_LINK_DOWN)
+
+#define LAN8814_INTS				0x1B
+#define LAN8814_INTS_JABBER			BIT(7)
+#define LAN8814_INTS_RECEIVE_ERR		BIT(6)
+#define LAN8814_INTS_PAGE_RECEIVE		BIT(5)
+#define LAN8814_INTS_PARELLEL			BIT(4)
+#define LAN8814_INTS_LINK_PARTNER_ACK		BIT(3)
+#define LAN8814_INTS_LINK_DOWN			BIT(2)
+#define LAN8814_INTS_REMOTE_FAULT		BIT(1)
+#define LAN8814_INTS_LINK_UP			BIT(0)
+#define LAN8814_INTS_ALL			(LAN8814_INTS_LINK_UP |\
+						 LAN8814_INTS_LINK_DOWN)
+
+#define LAN8814_PTP_MAX_FREQ_ADJ_IN_PPB		(31249999)
+#define LAN8814_PTP_MAX_FINE_ADJ_IN_SCALED_PPM	(2047999934)
+
+#define PTP_RX_MOD				0x024F
+#define PTP_RX_RSVD_BYTE_CFG			0x0250
+#define PTP_RX_TIMESTAMP_EN			0x024D
+#define PTP_TX_TIMESTAMP_EN			0x028D
+
+#define PTP_TX_PARSE_L2_ADDR_EN			0x0284
+#define PTP_RX_PARSE_L2_ADDR_EN			0x0244
+#define PTP_FLAG_ISR_ENABLED			BIT(2)
+#define PTP_FLAG_PTP_CLOCK_REGISTERED		BIT(1)
+
+#define LTC_HARD_RESET				0x023F
+#define LTC_HARD_RESET_				BIT(0)
+
+#define TSU_HARD_RESET				0x02C1
+#define TSU_HARD_RESET_				BIT(0)
+
+#define PTP_CMD_CTL				0x0200
+#define PTP_CMD_CTL_PTP_DISABLE_		BIT(0)
+#define PTP_CMD_CTL_PTP_ENABLE_			BIT(1)
+#define PTP_CMD_CTL_PTP_CLOCK_READ_		BIT(3)
+#define PTP_CMD_CTL_PTP_CLOCK_LOAD_		BIT(4)
+#define PTP_CMD_CTL_PTP_LTC_STEP_SEC_		BIT(5)
+#define PTP_CMD_CTL_PTP_LTC_STEP_NSEC_		BIT(6)
+
+#define PTP_CLOCK_SET_SEC_HI			(0x0205)
+#define PTP_CLOCK_SET_SEC_MID			(0x0206)
+#define PTP_CLOCK_SET_SEC_LO			(0x0207)
+#define PTP_CLOCK_SET_NS_HI			(0x0208)
+#define PTP_CLOCK_SET_NS_LO			(0x0209)
+#define PTP_CLOCK_SET_SUBNS_LO			(0x020B)
+#define PTP_CLOCK_SET_SUBNS_HI			(0x020A)
+
+#define PTP_CLOCK_READ_SEC_HI			(0x0229)
+#define PTP_CLOCK_READ_SEC_MID			(0x022A)
+#define PTP_CLOCK_READ_SEC_LO			(0x022B)
+#define PTP_CLOCK_READ_NS_HI			(0x022C)
+#define PTP_CLOCK_READ_NS_LO			(0x022D)
+#define PTP_CLOCK_READ_SUBNS_HI			(0x022E)
+#define PTP_CLOCK_READ_SUBNS_LO			(0x022F)
+
+#define PTP_OPERATING_MODE			(0x0241)
+#define PTP_OPERATING_MODE_STANDALONE		0x01
+
+#define PTP_TX_MOD				(0x028F)
+#define PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_	BIT(12)
+#define PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_ BIT(3)
+
+#define PTP_RX_PARSE_CONFIG			(0x0242)
+#define PTP_RX_PARSE_CONFIG_LAYER2_EN_		BIT(0)
+#define PTP_RX_PARSE_CONFIG_IPV4_EN_		BIT(1)
+
+#define PTP_TX_PARSE_CONFIG			(0x0282)
+#define PTP_TX_PARSE_CONFIG_LAYER2_EN_		BIT(0)
+#define PTP_TX_PARSE_CONFIG_IPV4_EN_		BIT(1)
+
+#define PTP_CLOCK_RATE_ADJ_HI_			(0x020C)
+#define PTP_CLOCK_RATE_ADJ_LO_			(0x020D)
+#define PTP_CLOCK_RATE_ADJ_DIR_			BIT(15)
+
+#define PTP_LTC_STEP_ADJ_HI			(0x0212)
+#define PTP_LTC_STEP_ADJ_LO			(0x0213)
+#define PTP_LTC_STEP_ADJ_DIR_			BIT(15)
+
+#define PTP_TX_MSG_HEADER1			(0x0298)
+#define PTP_TX_MSG_HEADER1_MSG_TYPE_		(0x000F)
+#define PTP_TX_MSG_HEADER1_MSG_TYPE_SYNC_	(0x00000000)
+
+#define LAN8814_INTR_CTRL_REG			(0x34)
+#define LAN8814_INTR_CTRL_REG_POLARITY		BIT(1)
+#define LAN8814_INTR_CTRL_REG_INTR_ENABLE	BIT(0)
+
+#define LAN8814_INTR_STS_REG			(0x33)
+#define LAN8814_INTR_STS_REG_1588_TSU0		BIT(0)
+
+#define PTP_CAP_INFO				(0x022A)
+#define PTP_CAP_INFO_TX_TS_CNT_GET_(reg_val)	(((reg_val) & 0x0f00) >> 8)
+
+#define PTP_TX_EGRESS_SEC_HI			(0x0296)
+#define PTP_TX_EGRESS_SEC_LO			(0x0297)
+#define PTP_TX_EGRESS_NS_HI			(0x0294)
+#define PTP_TX_EGRESS_NS_HI_TX_TS_VALID_	BIT(15)
+#define PTP_TX_EGRESS_NS_LO			(0x0295)
+#define PTP_TX_MSG_HEADER2			(0x0299)
+
+#define PTP_RX_INGRESS_SEC_HI			(0x0256)
+#define PTP_RX_INGRESS_SEC_LO			(0x0257)
+#define PTP_RX_INGRESS_NS_HI			(0x0254)
+#define PTP_RX_INGRESS_NS_LO			(0x0255)
+#define PTP_RX_MSG_HEADER2			(0x0259)
+
+#define PTP_TSU_INT_EN				(0x200)
+#define PTP_TSU_INT_EN_PTP_TX_TS_OVRFL_EN	BIT(3)
+#define PTP_TSU_INT_EN_PTP_TX_TS_EN		BIT(2)
+#define PTP_TSU_INT_EN_PTP_RX_TS_OVRFL_EN	BIT(1)
+#define PTP_TSU_INT_EN_PTP_RX_TS_EN		BIT(0)
+
+#define PTP_TSU_INT_STS				(0x201)
+#define PTP_TSU_INT_STS_PTP_TX_TS_OVRFL_INT	BIT(3)
+#define PTP_TSU_INT_STS_PTP_TX_TS_EN		BIT(2)
+#define PTP_TSU_INT_STS_PTP_RX_TS_OVRFL_INT	BIT(1)
+#define PTP_TSU_INT_STS_PTP_RX_TS_EN		BIT(0)
+
 /* PHY Control 1 */
 #define	MII_KSZPHY_CTRL_1			0x1e
 
@@ -76,6 +214,9 @@
 #define MII_KSZPHY_TX_DATA_PAD_SKEW             0x106
 
 #define PS_TO_REG				200
+#define KSZ_EXT_PAGE_ACCESS_CONTROL		0x16
+#define KSZ_EXT_PAGE_ACCESS_ADDRESS_DATA	0x17
+#define OFF_PTP_CONTROL				32 /* PTPv1 only */
 
 struct kszphy_hw_stat {
 	const char *string;
@@ -96,6 +237,47 @@ struct kszphy_type {
 	bool has_rmii_ref_clk_sel;
 };
 
+struct lan8814_ptp {
+	int flags;
+	struct ptp_clock *ptp_clock;
+	struct ptp_clock_info ptp_clock_info;
+	struct ptp_pin_desc pin_config[1];
+	enum hwtstamp_tx_types tx_type;
+	enum hwtstamp_rx_filters rx_filter;
+	struct sk_buff_head tx_queue;
+};
+
+struct lan8814_ptphdr {
+	u8 tsmt; /* transportSpecific | messageType */
+	u8 ver;  /* reserved0 | versionPTP */
+	__be16 msglen;
+	u8 domain;
+	u8 rsrvd1;
+	__be16 flags;
+	__be64 correction;
+	__be32 rsrvd2;
+	__be64 clk_identity;
+	__be16 src_port_id;
+	__be16 seq_id;
+	u8 ctrl;
+	u8 log_interval;
+} __attribute__((__packed__));
+
+struct lan8814_priv {
+	const struct kszphy_type *type;
+	int led_mode;
+	bool rmii_ref_clk_sel;
+	bool rmii_ref_clk_sel_val;
+	u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
+	struct mii_timestamper mii_ts;
+	struct phy_device *phydev;
+	struct lan8814_ptp ptp;
+	int hwts_tx_en;
+	int hwts_rx_en;
+	int layer;
+	int version;
+};
+
 struct kszphy_priv {
 	const struct kszphy_type *type;
 	int led_mode;
@@ -135,6 +317,38 @@ static const struct kszphy_type ksz9021_type = {
 	.interrupt_level_mask	= BIT(14),
 };
 
+static void lan8814_ptp_clock_get(struct phy_device *phydev,
+				  u32 *seconds, u32 *nano_seconds,
+				  u32 *sub_nano_seconds);
+
+static int lan8814_read_page_reg(struct phy_device *phydev,
+				 int page, u32 addr)
+{
+	u32 data;
+
+	phy_write(phydev, KSZ_EXT_PAGE_ACCESS_CONTROL, page);
+	phy_write(phydev, KSZ_EXT_PAGE_ACCESS_ADDRESS_DATA, addr);
+	phy_write(phydev, KSZ_EXT_PAGE_ACCESS_CONTROL, (page | 0x4000));
+	data = phy_read(phydev, KSZ_EXT_PAGE_ACCESS_ADDRESS_DATA);
+
+	return data;
+}
+
+static int lan8814_write_page_reg(struct phy_device *phydev,
+				  int page, u16 addr, u16 val)
+{
+	phy_write(phydev, KSZ_EXT_PAGE_ACCESS_CONTROL, page);
+	phy_write(phydev, KSZ_EXT_PAGE_ACCESS_ADDRESS_DATA, addr);
+	phy_write(phydev, KSZ_EXT_PAGE_ACCESS_CONTROL, (page | 0x4000));
+	val = phy_write(phydev, KSZ_EXT_PAGE_ACCESS_ADDRESS_DATA, val);
+	if (val != 0) {
+		phydev_err(phydev, "Error: phy_write_mmd has returned error %d\n",
+			   val);
+		return val;
+	}
+	return 0;
+}
+
 static int kszphy_extended_write(struct phy_device *phydev,
 				u32 regnum, u16 val)
 {
@@ -149,16 +363,196 @@ static int kszphy_extended_read(struct phy_device *phydev,
 	return phy_read(phydev, MII_KSZPHY_EXTREG_READ);
 }
 
-static int kszphy_ack_interrupt(struct phy_device *phydev)
+static void lan8814_ptp_tx_ts_get(struct phy_device *phydev,
+				  u32 *seconds, u32 *nano_seconds,
+				  u32 *seq_id);
+
+static struct lan8814_ptphdr *get_ptp_header_l4(struct sk_buff *skb,
+						struct iphdr *iphdr,
+						struct udphdr *udphdr)
+{
+	if (iphdr->version != 4 || iphdr->protocol != IPPROTO_UDP)
+		return NULL;
+
+	return (struct lan8814_ptphdr *)(((unsigned char *)udphdr) + UDP_HLEN);
+}
+
+static struct lan8814_ptphdr *get_ptp_header_tx(struct sk_buff *skb)
+{
+	struct ethhdr *ethhdr = eth_hdr(skb);
+	struct udphdr *udphdr;
+	struct iphdr *iphdr;
+
+	if (ethhdr->h_proto == htons(ETH_P_1588))
+		return (struct lan8814_ptphdr *)(((unsigned char *)ethhdr) +
+				skb_mac_header_len(skb));
+
+	if (ethhdr->h_proto != htons(ETH_P_IP))
+		return NULL;
+
+	iphdr = ip_hdr(skb);
+	udphdr = udp_hdr(skb);
+
+	return get_ptp_header_l4(skb, iphdr, udphdr);
+}
+
+static int get_sig(struct sk_buff *skb, u8 *sig)
+{
+	struct lan8814_ptphdr *ptphdr = get_ptp_header_tx(skb);
+	struct ethhdr *ethhdr = eth_hdr(skb);
+	unsigned int i;
+
+	if (!ptphdr)
+		return -EOPNOTSUPP;
+
+	sig[0] = (__force u16)ptphdr->seq_id >> 8;
+	sig[1] = (__force u16)ptphdr->seq_id & GENMASK(7, 0);
+	sig[2] = ptphdr->domain;
+	sig[3] = ptphdr->tsmt & GENMASK(3, 0);
+
+	memcpy(&sig[4], ethhdr->h_dest, ETH_ALEN);
+
+	/* Fill the last bytes of the signature to reach a 16B signature */
+	for (i = 10; i < 16; i++)
+		sig[i] = ptphdr->tsmt & GENMASK(3, 0);
+
+	return 0;
+}
+
+static void lan8814_dequeue_skb(struct lan8814_ptp *ptp)
+{
+	struct lan8814_priv *priv = container_of(ptp, struct lan8814_priv, ptp);
+	struct phy_device *phydev = priv->phydev;
+	struct skb_shared_hwtstamps shhwtstamps;
+	struct sk_buff *skb;
+	u8 skb_sig[16];
+	int len;
+	u32 reg, cnt;
+	u32 seconds, nsec, seq_id;
+
+	reg = lan8814_read_page_reg(phydev, 5, PTP_CAP_INFO);
+	cnt = PTP_CAP_INFO_TX_TS_CNT_GET_(reg);
+
+	/* FIFO is Empty*/
+	if (cnt == 0)
+		return;
+
+	len = skb_queue_len(&ptp->tx_queue);
+	if (len < 1)
+		return;
+
+	while (len--) {
+		skb = __skb_dequeue(&ptp->tx_queue);
+		if (!skb)
+			return;
+
+		/* Can't get the signature of the packet, won't ever
+		 * be able to have one so let's dequeue the packet.
+		 */
+		if (get_sig(skb, skb_sig) < 0) {
+			kfree_skb(skb);
+			continue;
+		}
+
+		lan8814_ptp_tx_ts_get(phydev, &seconds, &nsec, &seq_id);
+
+		/* Check if we found the signature we were looking for. */
+		if (memcmp(skb_sig, &seq_id, sizeof(skb_sig))) {
+			memset(&shhwtstamps, 0, sizeof(shhwtstamps));
+			shhwtstamps.hwtstamp = ktime_set(seconds, nsec);
+			skb_complete_tx_timestamp(skb, &shhwtstamps);
+
+			return;
+		}
+
+		/* Valid signature but does not match the one of the
+		 * packet in the FIFO right now, reschedule it for later
+		 * packets.
+		 */
+		__skb_queue_tail(&ptp->tx_queue, skb);
+	}
+}
+
+static void lan8814_get_tx_ts(struct lan8814_ptp *ptp)
 {
-	/* bit[7..0] int status, which is a read and clear register. */
+	u32 reg;
+	struct lan8814_priv *priv = container_of(ptp, struct lan8814_priv, ptp);
+	struct phy_device *phydev = priv->phydev;
+
+	do {
+		lan8814_dequeue_skb(ptp);
+
+		/* If other timestamps are available in the FIFO, process them. */
+		reg = lan8814_read_page_reg(phydev, 5, PTP_CAP_INFO);
+	} while (PTP_CAP_INFO_TX_TS_CNT_GET_(reg) > 1);
+}
+
+static irqreturn_t lan8814_handle_ptp_interrupt(struct phy_device *phydev)
+{
+	struct lan8814_priv *lan8814 = phydev->priv;
 	int rc;
 
-	rc = phy_read(phydev, MII_KSZPHY_INTCS);
+	rc = lan8814_read_page_reg(phydev, 5, PTP_TSU_INT_STS);
+
+	if (!(rc & PTP_TSU_INT_STS_PTP_TX_TS_EN))
+		return IRQ_NONE;
+
+	lan8814_get_tx_ts(&lan8814->ptp);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev)
+{
+	int irq_status;
+
+	irq_status = lan8814_read_page_reg(phydev, 4, LAN8814_INTR_STS_REG);
+	if (irq_status < 0)
+		return IRQ_NONE;
+
+	if (irq_status & LAN8814_INTR_STS_REG_1588_TSU0)
+		return lan8814_handle_ptp_interrupt(phydev);
+
+	irq_status = phy_read(phydev, LAN8814_INTS);
+	if (irq_status < 0)
+		return IRQ_NONE;
+
+	if (irq_status & LAN8814_INTS_ALL)
+		phy_mac_interrupt(phydev);
+
+	return IRQ_HANDLED;
+}
+
+static int kszphy_ack_interrupt(struct phy_device *phydev)
+{
+	if (phy_read(phydev, MII_KSZPHY_INTCS) < 0)
+		return -1;
+
+	return 0;
+}
 
-	return (rc < 0) ? rc : 0;
+static int lan8814_config_ts_intr(struct phy_device *phydev)
+{
+	return lan8814_write_page_reg(phydev, 5, PTP_TSU_INT_EN, PTP_TSU_INT_EN_PTP_TX_TS_EN |
+					  PTP_TSU_INT_EN_PTP_TX_TS_OVRFL_EN);
 }
 
+static int lan8814_config_intr(struct phy_device *phydev)
+{
+	int temp;
+
+	lan8814_write_page_reg(phydev, 4, LAN8814_INTR_CTRL_REG,
+			       LAN8814_INTR_CTRL_REG_POLARITY |
+			       LAN8814_INTR_CTRL_REG_INTR_ENABLE);
+
+	/* enable / disable interrupts */
+	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+		temp = LAN8814_INTC_ALL;
+	else
+		temp = 0;
+
+	return phy_write(phydev, LAN8814_INTC, temp);
+}
 static int kszphy_config_intr(struct phy_device *phydev)
 {
 	const struct kszphy_type *type = phydev->drv->driver_data;
@@ -304,7 +698,7 @@ static int kszphy_nand_tree_disable(struct phy_device *phydev)
 /* Some config bits need to be set again on resume, handle them here. */
 static int kszphy_config_reset(struct phy_device *phydev)
 {
-	struct kszphy_priv *priv = phydev->priv;
+	struct lan8814_priv *priv = phydev->priv;
 	int ret;
 
 	if (priv->rmii_ref_clk_sel) {
@@ -1124,6 +1518,569 @@ static int kszphy_resume(struct phy_device *phydev)
 	return 0;
 }
 
+static bool lan8814_ptp_is_enable(struct phy_device *phydev)
+{
+	if (phy_read(phydev, PTP_CMD_CTL) & PTP_CMD_CTL_PTP_ENABLE_)
+		return true;
+
+	return false;
+}
+
+static void lan8814_ptp_rx_ts_get(struct phy_device *phydev,
+				  u32 *seconds, u32 *nano_seconds, u32 *seq_id)
+{
+	if (seconds) {
+		(*seconds) = lan8814_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_HI);
+		(*seconds) = ((*seconds) << 16) |
+			     lan8814_read_page_reg(phydev, 5, PTP_RX_INGRESS_SEC_LO);
+	}
+
+	if (nano_seconds) {
+		(*nano_seconds) = lan8814_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_HI);
+		(*nano_seconds) = (((*nano_seconds) & 0x3fff) << 16)
+				   | lan8814_read_page_reg(phydev, 5, PTP_RX_INGRESS_NS_LO);
+	}
+
+	if (seq_id)
+		(*seq_id) = lan8814_read_page_reg(phydev, 5, PTP_RX_MSG_HEADER2);
+}
+
+static void lan8814_ptp_tx_ts_get(struct phy_device *phydev,
+				  u32 *seconds, u32 *nano_seconds, u32 *seq_id)
+{
+	if (seconds) {
+		(*seconds) = lan8814_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_HI);
+		(*seconds) = ((*seconds) << 16) |
+			     lan8814_read_page_reg(phydev, 5, PTP_TX_EGRESS_SEC_LO);
+	}
+
+	if (nano_seconds) {
+		(*nano_seconds) = lan8814_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_HI);
+		(*nano_seconds) = (((*nano_seconds) & 0x3fff) << 16) |
+			lan8814_read_page_reg(phydev, 5, PTP_TX_EGRESS_NS_LO);
+	}
+
+	if (seq_id)
+		(*seq_id) = lan8814_read_page_reg(phydev, 5, PTP_TX_MSG_HEADER2);
+}
+
+static int lan8814_ts_info(struct mii_timestamper *mii_ts, struct ethtool_ts_info *info)
+{
+	struct lan8814_priv *ksz9031 = container_of(mii_ts, struct lan8814_priv, mii_ts);
+
+	info->so_timestamping =
+		SOF_TIMESTAMPING_TX_HARDWARE |
+		SOF_TIMESTAMPING_RX_HARDWARE |
+		SOF_TIMESTAMPING_RAW_HARDWARE;
+
+	info->phc_index = ptp_clock_index(ksz9031->ptp.ptp_clock);
+
+	info->tx_types =
+		(1 << HWTSTAMP_TX_OFF) |
+		(1 << HWTSTAMP_TX_ON) |
+		(1 << HWTSTAMP_TX_ONESTEP_SYNC);
+
+	info->rx_filters =
+		(1 << HWTSTAMP_FILTER_NONE) |
+		(1 << HWTSTAMP_FILTER_PTP_V1_L4_EVENT) |
+		(1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
+		(1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
+		(1 << HWTSTAMP_FILTER_PTP_V2_EVENT);
+
+	return 0;
+}
+
+static int lan8814_hwtstamp(struct mii_timestamper *mii_ts, struct ifreq *ifr)
+{
+	struct lan8814_priv *lan8814 = container_of(mii_ts, struct lan8814_priv, mii_ts);
+	struct hwtstamp_config config;
+	int txcfg, rxcfg;
+
+	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
+		return -EFAULT;
+
+	lan8814->hwts_tx_en = config.tx_type;
+
+	lan8814->ptp.rx_filter = config.rx_filter;
+	lan8814->ptp.tx_type = config.tx_type;
+
+	switch (config.rx_filter) {
+	case HWTSTAMP_FILTER_NONE:
+		lan8814->hwts_rx_en = 0;
+		lan8814->layer = 0;
+		lan8814->version = 0;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+		lan8814->hwts_rx_en = 1;
+		lan8814->layer = PTP_CLASS_L4;
+		lan8814->version = PTP_CLASS_V2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+		lan8814->hwts_rx_en = 1;
+		lan8814->layer = PTP_CLASS_L2;
+		lan8814->version = PTP_CLASS_V2;
+		break;
+	case HWTSTAMP_FILTER_PTP_V2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+		lan8814->hwts_rx_en = 1;
+		lan8814->layer = PTP_CLASS_L4 | PTP_CLASS_L2;
+		lan8814->version = PTP_CLASS_V2;
+		break;
+	default:
+		return -ERANGE;
+	}
+
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_RX_PARSE_CONFIG, 0);
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_TX_PARSE_CONFIG, 0);
+
+	if (lan8814->hwts_rx_en && (lan8814->layer & PTP_CLASS_L2))
+		rxcfg = PTP_RX_PARSE_CONFIG_LAYER2_EN_;
+
+	if (lan8814->hwts_tx_en && (lan8814->layer & PTP_CLASS_L2))
+		txcfg = PTP_TX_PARSE_CONFIG_LAYER2_EN_;
+
+	if (lan8814->hwts_rx_en && (lan8814->layer & PTP_CLASS_L4))
+		rxcfg |= PTP_RX_PARSE_CONFIG_IPV4_EN_;
+
+	if (lan8814->hwts_tx_en && (lan8814->layer & PTP_CLASS_L4))
+		txcfg |= PTP_TX_PARSE_CONFIG_IPV4_EN_;
+
+	if (lan8814_ptp_is_enable(lan8814->phydev))
+		lan8814_write_page_reg(lan8814->phydev, 4, PTP_CMD_CTL,
+				       PTP_CMD_CTL_PTP_DISABLE_);
+
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_RX_PARSE_CONFIG, rxcfg);
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_TX_PARSE_CONFIG, txcfg);
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_RX_TIMESTAMP_EN, 0x3);
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_TX_TIMESTAMP_EN, 0x3);
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_TX_PARSE_L2_ADDR_EN, 0);
+	lan8814_write_page_reg(lan8814->phydev, 5, PTP_RX_PARSE_L2_ADDR_EN, 0);
+
+	lan8814_write_page_reg(lan8814->phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_ENABLE_);
+	if (lan8814->hwts_tx_en == HWTSTAMP_TX_ONESTEP_SYNC) {
+		lan8814_write_page_reg(lan8814->phydev, 5, PTP_TX_MOD,
+				       PTP_TX_MOD_TX_PTP_SYNC_TS_INSERT_);
+	} else if (lan8814->hwts_tx_en == HWTSTAMP_TX_ON) {
+		/* Enabling 2 step offloading and all option for TS insertion/correction fields */
+		lan8814_write_page_reg(lan8814->phydev, 5, PTP_TX_MOD, 0x800);
+		lan8814_config_ts_intr(lan8814->phydev);
+	}
+
+	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ? -EFAULT : 0;
+}
+
+static bool lan8814_rxtstamp(struct mii_timestamper *mii_ts, struct sk_buff *skb, int type)
+{
+	struct lan8814_priv *ksz9031 = container_of(mii_ts, struct lan8814_priv, mii_ts);
+	struct skb_shared_hwtstamps *shhwtstamps = NULL;
+	u32 seconds, nsec, seq_id;
+
+	if (!ksz9031->hwts_rx_en)
+		return false;
+
+	if (!skb)
+		return false;
+
+	if ((type & ksz9031->version) == 0 || (type & ksz9031->layer) == 0)
+		return false;
+
+	if (ksz9031->ptp.rx_filter == HWTSTAMP_FILTER_NONE ||
+	    type == PTP_CLASS_NONE)
+		return false;
+
+	lan8814_ptp_rx_ts_get(ksz9031->phydev, &seconds, &nsec, &seq_id);
+
+	/* Saving timestamp and sending through skbuff */
+	shhwtstamps = skb_hwtstamps(skb);
+	memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+	shhwtstamps->hwtstamp = ktime_set(seconds, nsec);
+
+	netif_rx_ni(skb);
+
+	return true;
+}
+
+static int is_sync(struct sk_buff *skb, int type)
+{
+	u8 *data = skb->data, *msgtype;
+	unsigned int offset = 0;
+
+	if (type & PTP_CLASS_VLAN)
+		offset += VLAN_HLEN;
+
+	switch (type & PTP_CLASS_PMASK) {
+	case PTP_CLASS_IPV4:
+		offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
+		break;
+	case PTP_CLASS_IPV6:
+		offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
+		break;
+	case PTP_CLASS_L2:
+		offset += ETH_HLEN;
+		break;
+	default:
+		return 0;
+	}
+
+	if (type & PTP_CLASS_V1)
+		offset += OFF_PTP_CONTROL;
+
+	if (skb->len < offset + 1)
+		return 0;
+
+	msgtype = data + offset;
+
+	return (*msgtype & 0xf) == 0;
+}
+
+static void lan8814_txtstamp(struct mii_timestamper *mii_ts,
+			     struct sk_buff *skb, int type)
+{
+	struct lan8814_priv *kszphy = container_of(mii_ts, struct lan8814_priv, mii_ts);
+
+	switch (kszphy->hwts_tx_en) {
+	case HWTSTAMP_TX_ONESTEP_SYNC:
+		if (is_sync(skb, type)) {
+			kfree_skb(skb);
+			return;
+		}
+		break;
+
+	case HWTSTAMP_TX_ON:
+		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+		skb_queue_tail(&kszphy->ptp.tx_queue, skb);
+		break;
+
+	case HWTSTAMP_TX_OFF:
+	default:
+		kfree_skb(skb);
+		break;
+	}
+}
+
+static void lan8814_ptp_clock_set(struct phy_device *phydev,
+				  u32 seconds, u32 nano_seconds,
+				 u32 sub_nano_seconds)
+{
+	u32 sec_low, sec_high, nsec_low, nsec_high, snsec_low, snsec_high;
+
+	sec_low = seconds & 0xffff;
+	sec_high = ((seconds >> 16) & 0xffff);
+	nsec_low = nano_seconds & 0xffff;
+	nsec_high = ((nano_seconds >> 16) & 0x3fff);
+	snsec_low = sub_nano_seconds & 0xffff;
+	snsec_high = ((sub_nano_seconds >> 16) & 0xffff);
+
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_LO, sec_low);
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_SET_SEC_MID, sec_high);
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_LO, nsec_low);
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_SET_NS_HI, nsec_high);
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_SET_SUBNS_LO, snsec_low);
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_SET_SUBNS_HI, snsec_high);
+
+	lan8814_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_LOAD_);
+}
+
+static void lan8814_ptp_clock_get(struct phy_device *phydev,
+				  u32 *seconds, u32 *nano_seconds,
+				  u32 *sub_nano_seconds)
+{
+	lan8814_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_CLOCK_READ_);
+
+	if (seconds) {
+		(*seconds) = lan8814_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_MID);
+		(*seconds) = ((*seconds) << 16) |
+			     lan8814_read_page_reg(phydev, 4, PTP_CLOCK_READ_SEC_LO);
+	}
+
+	if (nano_seconds) {
+		(*nano_seconds) = lan8814_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_HI);
+		(*nano_seconds) = (((*nano_seconds) & 0x3fff) << 16) |
+				  lan8814_read_page_reg(phydev, 4, PTP_CLOCK_READ_NS_LO);
+	}
+
+	if (sub_nano_seconds) {
+		(*sub_nano_seconds) = lan8814_read_page_reg(phydev, 4,
+								PTP_CLOCK_READ_SUBNS_HI);
+		(*sub_nano_seconds) = ((*sub_nano_seconds) << 16) |
+				       lan8814_read_page_reg(phydev, 4,
+							     PTP_CLOCK_READ_SUBNS_LO);
+	}
+}
+
+static int lan8814_ptpci_gettime64(struct ptp_clock_info *ptpci,
+				   struct timespec64 *ts)
+{
+	struct lan8814_ptp *ptp =
+		container_of(ptpci, struct lan8814_ptp, ptp_clock_info);
+	struct lan8814_priv *priv =
+		container_of(ptp, struct lan8814_priv, ptp);
+	struct phy_device *phydev = priv->phydev;
+	u32 nano_seconds = 0;
+	u32 seconds = 0;
+
+	lan8814_ptp_clock_get(phydev, &seconds, &nano_seconds, NULL);
+	ts->tv_sec = seconds;
+	ts->tv_nsec = nano_seconds;
+
+	return 0;
+}
+
+static void lan8814_ptp_clock_step(struct phy_device *phydev,
+				   s64 time_step_ns)
+{
+	u32 nano_seconds_step	= 0;
+	u64 abs_time_step_ns	= 0;
+	u32 unsigned_seconds	= 0;
+	u32 nano_seconds	= 0;
+	u32 remainder		= 0;
+	s32 seconds		= 0;
+
+	if (time_step_ns >  15000000000LL) {
+		/* convert to clock set */
+		lan8814_ptp_clock_get(phydev, &unsigned_seconds,	&nano_seconds, NULL);
+		unsigned_seconds += div_u64_rem(time_step_ns, 1000000000LL,
+				&remainder);
+		nano_seconds += remainder;
+		if (nano_seconds >= 1000000000) {
+			unsigned_seconds++;
+			nano_seconds -= 1000000000;
+		}
+		lan8814_ptp_clock_set(phydev, unsigned_seconds,
+				      nano_seconds, 0);
+		return;
+	} else if (time_step_ns < -15000000000LL) {
+		/* convert to clock set */
+		time_step_ns = -time_step_ns;
+
+		lan8814_ptp_clock_get(phydev, &unsigned_seconds,
+				      &nano_seconds, NULL);
+		unsigned_seconds -= div_u64_rem(time_step_ns, 1000000000LL,
+				&remainder);
+		nano_seconds_step = remainder;
+		if (nano_seconds < nano_seconds_step) {
+			unsigned_seconds--;
+			nano_seconds += 1000000000;
+		}
+		nano_seconds -= nano_seconds_step;
+		lan8814_ptp_clock_set(phydev, unsigned_seconds,
+				      nano_seconds, 0);
+		return;
+	}
+
+	/* do clock step */
+	if (time_step_ns >= 0) {
+		abs_time_step_ns = (u64)(time_step_ns);
+		seconds = (s32)div_u64_rem(abs_time_step_ns, 1000000000,
+				&remainder);
+		nano_seconds = (u32)remainder;
+	} else {
+		abs_time_step_ns = (u64)(-time_step_ns);
+		seconds = -((s32)div_u64_rem(abs_time_step_ns, 1000000000,
+					&remainder));
+		nano_seconds = (u32)remainder;
+		if (nano_seconds > 0) {
+			/* subtracting nano seconds is not allowed
+			 * convert to subtracting from seconds,
+			 * and adding to nanoseconds
+			 */
+			seconds--;
+			nano_seconds = (1000000000 - nano_seconds);
+		}
+	}
+
+	if (nano_seconds > 0) {
+		/* add 8 ns to cover the likely normal increment */
+		nano_seconds += 8;
+	}
+
+	if (nano_seconds >= 1000000000) {
+		/* carry into seconds */
+		seconds++;
+		nano_seconds -= 1000000000;
+	}
+
+	while (seconds) {
+		if (seconds > 0) {
+			u32 adjustment_value = (u32)seconds;
+			u16 adjustment_value_lo, adjustment_value_hi;
+
+			adjustment_value_lo = adjustment_value & 0xffff;
+			adjustment_value_hi = (adjustment_value >> 16) & 0x3fff;
+
+			if (adjustment_value > 0xF)
+				adjustment_value = 0xF;
+			lan8814_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO,
+					       adjustment_value_lo);
+			lan8814_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI,
+					       PTP_LTC_STEP_ADJ_DIR_ |
+					       adjustment_value_hi);
+			seconds -= ((s32)adjustment_value);
+		} else {
+			u32 adjustment_value = (u32)(-seconds);
+			u16 adjustment_value_lo, adjustment_value_hi;
+
+			adjustment_value_lo = adjustment_value & 0xffff;
+			adjustment_value_hi = (adjustment_value >> 16) & 0x3fff;
+			if (adjustment_value > 0xF)
+				adjustment_value = 0xF;
+			lan8814_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO,
+					       adjustment_value_lo);
+			lan8814_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI,
+					       PTP_LTC_STEP_ADJ_DIR_ |
+					       adjustment_value_hi);
+			seconds += ((s32)adjustment_value);
+		}
+		lan8814_write_page_reg(phydev, 4, PTP_CMD_CTL,
+				       PTP_CMD_CTL_PTP_LTC_STEP_SEC_);
+	}
+	if (nano_seconds) {
+		u16 nano_seconds_lo;
+		u16 nano_seconds_hi;
+
+		nano_seconds_lo = nano_seconds & 0xffff;
+		nano_seconds_hi = (nano_seconds >> 16) & 0x3fff;
+
+		lan8814_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_LO,
+				       nano_seconds_lo);
+		lan8814_write_page_reg(phydev, 4, PTP_LTC_STEP_ADJ_HI,
+				       PTP_LTC_STEP_ADJ_DIR_ |
+				       nano_seconds_hi);
+		lan8814_write_page_reg(phydev, 4, PTP_CMD_CTL,
+				       PTP_CMD_CTL_PTP_LTC_STEP_NSEC_);
+	}
+}
+
+static int lan8814_ptpci_adjtime(struct ptp_clock_info *ptpci, s64 delta)
+{
+	struct lan8814_ptp *ptp =
+		container_of(ptpci, struct lan8814_ptp, ptp_clock_info);
+	struct lan8814_priv *priv =
+		container_of(ptp, struct lan8814_priv, ptp);
+	struct phy_device *phydev = priv->phydev;
+
+	lan8814_ptp_clock_step(phydev, delta);
+
+	return 0;
+}
+
+static int lan8814_ptpci_adjfreq(struct ptp_clock_info *ptpci, s32 delta_ppb)
+{
+	struct lan8814_ptp *ptp =
+		container_of(ptpci, struct lan8814_ptp, ptp_clock_info);
+	struct lan8814_priv *priv =
+		container_of(ptp, struct lan8814_priv, ptp);
+	struct phy_device *phydev = priv->phydev;
+	u32 kszphy_rate_adj = 0;
+	u16 kszphy_rate_adj_lo = 0, kszphy_rate_adj_hi = 0;
+	bool positive = true;
+	u32 u32_delta = 0;
+	u64 u64_delta = 0;
+
+	if (delta_ppb > 0) {
+		u32_delta = (u32)delta_ppb;
+		positive = true;
+	} else {
+		u32_delta = (u32)(-delta_ppb);
+		positive = false;
+	}
+	u64_delta = (((u64)u32_delta) << 35);
+	kszphy_rate_adj = div_u64(u64_delta, 1000000000);
+
+	kszphy_rate_adj_lo = kszphy_rate_adj & 0xffff;
+	kszphy_rate_adj_hi = (kszphy_rate_adj >> 16) & 0x3fff;
+
+	if (positive)
+		kszphy_rate_adj_hi |= PTP_CLOCK_RATE_ADJ_DIR_;
+
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_HI_, kszphy_rate_adj_hi);
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_LO_, kszphy_rate_adj_lo);
+
+	return 0;
+}
+
+static int lan8814_ptpci_adjfine(struct ptp_clock_info *ptpci, long scaled_ppm)
+{
+	struct lan8814_ptp *ptp =
+		container_of(ptpci, struct lan8814_ptp, ptp_clock_info);
+	struct lan8814_priv *priv = container_of(ptp, struct lan8814_priv, ptp);
+	struct phy_device *phydev = priv->phydev;
+	u32 kszphy_rate_adj = 0;
+	u16 kszphy_rate_adj_lo = 0, kszphy_rate_adj_hi = 0;
+	bool positive = true;
+	u64 u64_delta = 0;
+
+	if ((scaled_ppm < (-LAN8814_PTP_MAX_FINE_ADJ_IN_SCALED_PPM)) ||
+	    scaled_ppm > LAN8814_PTP_MAX_FINE_ADJ_IN_SCALED_PPM) {
+		return -EINVAL;
+	}
+	if (scaled_ppm > 0) {
+		u64_delta = (u64)scaled_ppm;
+		positive = true;
+	} else {
+		u64_delta = (u64)(-scaled_ppm);
+		positive = false;
+	}
+	u64_delta = (u64_delta << 13);
+	kszphy_rate_adj = div_u64(u64_delta, 15625);
+
+	kszphy_rate_adj_lo = kszphy_rate_adj & 0xffff;
+	kszphy_rate_adj_hi = (kszphy_rate_adj >> 16) & 0x3fff;
+
+	if (positive)
+		kszphy_rate_adj_hi |= PTP_CLOCK_RATE_ADJ_DIR_;
+
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_HI_, kszphy_rate_adj_hi);
+	lan8814_write_page_reg(phydev, 4, PTP_CLOCK_RATE_ADJ_LO_, kszphy_rate_adj_lo);
+
+	return 0;
+}
+
+static void lan8814_ptp_reset(struct phy_device *phydev)
+{
+	if (lan8814_ptp_is_enable(phydev))
+		lan8814_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_DISABLE_);
+
+	lan8814_write_page_reg(phydev, 4, LTC_HARD_RESET, LTC_HARD_RESET_);
+	lan8814_write_page_reg(phydev, 5, TSU_HARD_RESET, TSU_HARD_RESET_);
+}
+
+static void lan8814_ptp_enable(struct phy_device *phydev)
+{
+	if (lan8814_ptp_is_enable(phydev))
+		return;
+
+	lan8814_write_page_reg(phydev, 4, PTP_CMD_CTL, PTP_CMD_CTL_PTP_ENABLE_);
+}
+
+static void lan8814_ptp_sync_to_system_clock(struct phy_device *phydev)
+{
+	struct timespec64 ts;
+
+	ktime_get_clocktai_ts64(&ts);
+
+	lan8814_ptp_clock_set(phydev, ts.tv_sec, ts.tv_nsec, 0);
+}
+
+static void lan8814_ptp_init(struct phy_device *phydev)
+{
+	u32 temp;
+
+	lan8814_ptp_reset(phydev);
+	lan8814_ptp_sync_to_system_clock(phydev);
+	temp = lan8814_read_page_reg(phydev, 5, PTP_TX_MOD);
+	temp |= PTP_TX_MOD_BAD_UDPV4_CHKSUM_FORCE_FCS_DIS_;
+	lan8814_write_page_reg(phydev, 5, PTP_TX_MOD, temp);
+	lan8814_write_page_reg(phydev, 4, PTP_OPERATING_MODE,
+			       PTP_OPERATING_MODE_STANDALONE);
+	lan8814_ptp_enable(phydev);
+}
+
 static int kszphy_probe(struct phy_device *phydev)
 {
 	const struct kszphy_type *type = phydev->drv->driver_data;
@@ -1185,6 +2142,89 @@ static int kszphy_probe(struct phy_device *phydev)
 	return 0;
 }
 
+static int lan8814_probe(struct phy_device *phydev)
+{
+	struct lan8814_priv *priv;
+	struct clk *clk;
+	const struct kszphy_type *type = phydev->drv->driver_data;
+	const struct device_node *np = phydev->mdio.dev.of_node;
+
+	priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	skb_queue_head_init(&priv->ptp.tx_queue);
+
+	priv->phydev = phydev;
+
+	priv->type = type;
+
+	priv->mii_ts.rxtstamp = lan8814_rxtstamp;
+	priv->mii_ts.txtstamp = lan8814_txtstamp;
+	priv->mii_ts.hwtstamp = lan8814_hwtstamp;
+	priv->mii_ts.ts_info  = lan8814_ts_info;
+
+	phydev->mii_ts = &priv->mii_ts;
+
+	phydev->priv = priv;
+
+	lan8814_ptp_init(phydev);
+
+	priv->ptp.flags |= PTP_FLAG_ISR_ENABLED;
+
+	if (!IS_ENABLED(CONFIG_PTP_1588_CLOCK))
+		return 0;
+
+	clk = devm_clk_get(&phydev->mdio.dev, "rmii-ref");
+	/* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */
+	if (!IS_ERR_OR_NULL(clk)) {
+		unsigned long rate = clk_get_rate(clk);
+		bool rmii_ref_clk_sel_25_mhz;
+
+		priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel;
+		rmii_ref_clk_sel_25_mhz = of_property_read_bool(np,
+				"micrel,rmii-reference-clock-select-25-mhz");
+
+		if (rate > 24500000 && rate < 25500000) {
+			priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz;
+		} else if (rate > 49500000 && rate < 50500000) {
+			priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz;
+		} else {
+			phydev_err(phydev, "Clock rate out of range: %ld\n",
+				   rate);
+			return -EINVAL;
+		}
+	}
+
+	priv->ptp.ptp_clock_info.owner = THIS_MODULE;
+	snprintf(priv->ptp.ptp_clock_info.name, 30, "%s",
+		 phydev->drv->name);
+	priv->ptp.ptp_clock_info.max_adj = LAN8814_PTP_MAX_FREQ_ADJ_IN_PPB;
+	priv->ptp.ptp_clock_info.n_alarm = 0;
+	priv->ptp.ptp_clock_info.n_ext_ts = 0;
+	priv->ptp.ptp_clock_info.n_per_out = 1;
+	priv->ptp.ptp_clock_info.n_pins = 0;
+	priv->ptp.ptp_clock_info.pps = 0;
+	priv->ptp.ptp_clock_info.pin_config = NULL;
+	priv->ptp.ptp_clock_info.adjfine = lan8814_ptpci_adjfine;
+	priv->ptp.ptp_clock_info.adjfreq = lan8814_ptpci_adjfreq;
+	priv->ptp.ptp_clock_info.adjtime = lan8814_ptpci_adjtime;
+	priv->ptp.ptp_clock_info.gettime64 = lan8814_ptpci_gettime64;
+	priv->ptp.ptp_clock_info.getcrosststamp = NULL;
+
+	priv->ptp.ptp_clock = ptp_clock_register(&priv->ptp.ptp_clock_info,
+						 &phydev->mdio.dev);
+
+	if (IS_ERR_OR_NULL(priv->ptp.ptp_clock))
+		phydev_err(phydev, "ptp_clock_register failed %lu\n",
+			   PTR_ERR(priv->ptp.ptp_clock));
+
+	priv->ptp.flags |= PTP_FLAG_PTP_CLOCK_REGISTERED;
+	phydev_info(phydev, "successfully registered ptp clock\n");
+
+	return 0;
+}
+
 static struct phy_driver ksphy_driver[] = {
 {
 	.phy_id		= PHY_ID_KS8737,
@@ -1352,7 +2392,7 @@ static struct phy_driver ksphy_driver[] = {
 	.phy_id_mask	= MICREL_PHY_ID_MASK,
 	.name		= "Microchip INDY Gigabit Quad PHY",
 	.driver_data	= &ksz9021_type,
-	.probe		= kszphy_probe,
+	.probe		= lan8814_probe,
 	.soft_reset	= genphy_soft_reset,
 	.read_status	= ksz9031_read_status,
 	.get_sset_count	= kszphy_get_sset_count,
@@ -1360,6 +2400,8 @@ static struct phy_driver ksphy_driver[] = {
 	.get_stats	= kszphy_get_stats,
 	.suspend	= genphy_suspend,
 	.resume		= kszphy_resume,
+	.config_intr	= lan8814_config_intr,
+	.handle_interrupt = lan8814_handle_interrupt,
 }, {
 	.phy_id		= PHY_ID_KSZ9131,
 	.phy_id_mask	= MICREL_PHY_ID_MASK,
-- 
2.17.1


             reply	other threads:[~2020-12-14 17:59 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-14 17:56 Divya Koppera [this message]
2020-12-14 19:06 ` [PATCH v2 net-next] net: phy: mchp: Add 1588 support for LAN8814 Quad PHY Andrew Lunn
2020-12-14 19:20 ` Andrew Lunn
2020-12-14 19:27 ` Andrew Lunn
2020-12-14 23:12 ` Jakub Kicinski
2020-12-18  2:42 ` kernel test robot
2020-12-18  2:42   ` kernel test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201214175658.11138-1-Divya.Koppera@microchip.com \
    --to=divya.koppera@microchip.com \
    --cc=UNGLinuxDriver@microchip.com \
    --cc=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=hkallweit1@gmail.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=marex@denx.de \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.