Hi experts,
now i am verifing ptp phy driver for motorcomm 1000M phy which supports 1588v2 & gptp(802.1as),
and facing some issues. pls help to give some suggestion. thank u a lot.
env description:
1. TI am335x disable TI CPU CPTS feature
2. linux kernel version 4.19.94-rt39-ga242ccf3f1
3. ptp4l application and cmd
(master: ./ptp4l -f automotive-master.cfg -i eth0 -m -l 7)
(slave: ./ptp4l -f automotive-slave.cfg -i eth0 -m -l 7)
ptp phy driver debug detail:
1. ptp4l sk_receive() 中 poll() timeout,
console log:
increasing tx_timestamp_timeout may correct this issue, but it is likely caused by a driver bug.
and check errno val: errno: -6
ptp4l source code snipper below:
int sk_receive(int fd, void *buf, int buflen,
struct address *addr, struct hw_timestamp *hwts, int flags)
{
...
if (flags == MSG_ERRQUEUE) {
struct pollfd pfd = { fd, sk_events, 0 };
res = poll(&pfd, 1, sk_tx_timeout);
if (res < 1) {
pr_err(res ? "poll for tx timestamp failed: %m" :
"timed out while polling for tx timestamp");
pr_err("increasing tx_timestamp_timeout may correct "
"this issue, but it is likely caused by a driver bug");
return -errno;
} else if (!(pfd.revents & sk_revents)) {
pr_err("poll for tx timestamp woke up on non ERR event");
return -1;
}
}
cnt = recvmsg(fd, &msg, flags);
...
2. linux kernel: sock_queue_err_skb() log :sk->sk_flags: 0x800100 => socket status is not SOCK_DEAD;
3. linux kernel: sock_queue_err_skb(), before invoking skb_queue_tail()
check skb->sk->sk_error_queue.qlen: 0x0, after invoking skb_queue_tail(),
check skb->sk->sk_error_queue.qlen: 0x1, => skb attached hardware tx timestamp enqueued into err queue.
4. linux kernel: sk->sk_error_report(sk) callback sock_def_error_report() is called,
=> err report is triggered to wake up the poll in ptp4l application.
5. master application(ptp4l) socket fd: 14
6. linux kernel: skb->tstamp assigned to ktime_get_real()
7. linux kernel: skb->ip_summed assigned to CHECKSUM_UNNECESSARY
8. linux kernel: before skb enqueued, skb->cb is cleared and
before skb_complete_tx_timestamp() called, skb->cb is cleared again.
9. strace -e ioctl ./ptp4l -i eth0 -m
ioctl(SIOCSHWTSTAMP, ...) = 0
motorcomm phy ptp driver source code attached.
// SPDX-License-Identifier: GPL-2.0+
/*
* drivers/net/phy/motorcomm.c
*
* Driver for Motorcomm PHYs
*
* Author: Jie Han<jie.han@motor-comm.com>
*
* Copyright (c) 2025 Motorcomm, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* Support Motorcomm Automotive Phys:
* 1000M Phys: YT8011
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#ifndef LINUX_VERSION_CODE
#include <linux/version.h>
#else
#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
#endif
#include <linux/netdevice.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/ethtool.h>
#include <linux/list.h>
#include <linux/mii.h>
#include <linux/net_tstamp.h>
#include <linux/ptp_classify.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/math64.h>
//#include <linux/skbuff.h>
//#include <linux/net.h> // sock_put()
//#include <linux/socket.h> // SCM_TSTAMP_SND
//#include <linux/skbuff.h> // __skb_complete_tx_timestamp()
#include <net/sock.h>
//#include <linux/compiler-attributes.h> //fallthrough
#include <linux/rtc.h>
#include <linux/time64.h>
//#include <linux/sock.h>
//debug (wrapper to unify log format)
#define DEBUG_PTP
#if defined DEBUG_PTP
#define dbg_ptp(fmt, ...) pr_info(fmt, ##__VA_ARGS__)
#else
#define dbg_ptp(fmt, ...)
#endif
#define YTPHY_LINUX_VERSION "2.2.45591"
#define MODULE_NAME "yt"
#define MOTORCOMM_PHY_ID_MASK 0xffffffff
#define PHY_ID_YT8011 0x4f51eb01
#define REG_PHY_SPEC_STATUS 0x11
#define REG_DEBUG_ADDR_OFFSET 0x1e
#define REG_DEBUG_DATA 0x1f
#define REG_MII_MMD_CTRL 0x0D
#define REG_MII_MMD_DATA 0x0E
#define YTXXXX_SPEED_MODE 0xc000
#define YTXXXX_SPEED_MODE_BIT 14
#define YTXXXX_DUPLEX_BIT 13
#define YTXXXX_LINK_STATUS_BIT 10
#define YTPHY_UTP_INTR_REG 0x12
#define YTPHY_UTP_INTR_STATUS_REG 0x13
#define YTPHY_INTR_LINK_STATUS (BIT(11) | BIT(10))
#define YTPHY_REG_SPACE_UTP 0
#define YT801X_REG_SMI_MUX 0x9000
enum GPTP_ROLE {
SLAVE = 0,
MASTER
};
struct yt8xxx_priv {
u8 chip_mode;
u8 role;
struct yt_ptp_private *ptp_priv;
};
enum yt8011_reg_space_type {
YT8011_REG_SPACE_UTP,
YT8011_REG_SPACE_SDS,
};
/* ext reg 0x8000 bit1:0 PTP_CLK_TYPE
* 2'b00 Ordinary/boundary two-step clock;
* 2'b01 Ordinary/boundary one-step clock;
* 2'b10 Transparent two-step clock;
* 2'b11 Transparent one-step clock;
*/
enum GPTP_CLK_MODE {
OC_2_STEPS = 0,
OC_1_STEPS,
TC_2_STEPS,
TC_1_STEPS
};
/* ext reg 0x8000 bit3:2 RTC_CLK_SEL
* 2'b00: ADC clock, after rotator;
* 2'b01: DAC clock;
* 2'b10: ADC clock, before rotator;
* 2'b11: SYNC_IN;
*/
enum CLK_SOURCE {
ADC_CLK_AFTER_ROTATOR = 0,
DAC_CLK,
ADC_CLK_BEFORE_ROTATOR,
SYNC_IN
};
#define VLAN_HLEN 4
#define SYNC 0x0
#define DELAY_REQ 0x1
#define PDELAY_REQ 0x2
#define PDELAY_RESP 0x3
#define FOLLOW_UP 0x8
#define DELAY_RESP 0x9
#define PDELAY_RESP_FOLLOW_UP 0xA
#define ANNOUNCE 0xB
#define SIGNALING 0xC
#define MANAGEMENT 0xD
static int yt8011_select_page(struct phy_device *phydev, int page);
static int yt8011_restore_page(struct phy_device *phydev, int oldpage, int ret);
static long yt8011_ptp_do_aux_work(struct ptp_clock_info *clock_info);
#if (KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE)
static inline void phy_lock_mdio_bus(struct phy_device *phydev)
{
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
mutex_lock(&phydev->bus->mdio_lock);
#else
mutex_lock(&phydev->mdio.bus->mdio_lock);
#endif
}
static inline void phy_unlock_mdio_bus(struct phy_device *phydev)
{
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
mutex_unlock(&phydev->bus->mdio_lock);
#else
mutex_unlock(&phydev->mdio.bus->mdio_lock);
#endif
}
#endif
#if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE)
static inline int __phy_read(struct phy_device *phydev, u32 regnum)
{
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
struct mii_bus *bus = phydev->bus;
int addr = phydev->addr;
return bus->read(bus, phydev->addr, regnum);
#else
struct mii_bus *bus = phydev->mdio.bus;
int addr = phydev->mdio.addr;
#endif
return bus->read(bus, addr, regnum);
}
static inline int __phy_write(struct phy_device *phydev, u32 regnum, u16 val)
{
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
struct mii_bus *bus = phydev->bus;
int addr = phydev->addr;
#else
struct mii_bus *bus = phydev->mdio.bus;
int addr = phydev->mdio.addr;
#endif
return bus->write(bus, addr, regnum, val);
}
#endif
static int __ytphy_read_ext(struct phy_device *phydev,
u32 regnum)
{
int ret;
ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
return __phy_read(phydev, REG_DEBUG_DATA);
}
static int ytphy_read_ext(struct phy_device *phydev, u32 regnum)
{
int ret;
phy_lock_mdio_bus(phydev);
ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
goto err_handle;
ret = __phy_read(phydev, REG_DEBUG_DATA);
if (ret < 0)
goto err_handle;
err_handle:
phy_unlock_mdio_bus(phydev);
return ret;
}
static int ytphy_write_ext(struct phy_device *phydev, u32 regnum, u16 val)
{
int ret;
phy_lock_mdio_bus(phydev);
ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
goto err_handle;
ret = __phy_write(phydev, REG_DEBUG_DATA, val);
if (ret < 0)
goto err_handle;
err_handle:
phy_unlock_mdio_bus(phydev);
return ret;
}
static int __ytphy_write_ext(struct phy_device *phydev,
u32 regnum, u16 val)
{
int ret;
ret = __phy_write(phydev, REG_DEBUG_ADDR_OFFSET, regnum);
if (ret < 0)
return ret;
ret = __phy_write(phydev, REG_DEBUG_DATA, val);
if (ret < 0)
return ret;
return 0;
}
static int ytphy_read_mmd(struct phy_device* phydev,
u16 device, u16 reg)
{
int val;
phy_lock_mdio_bus(phydev);
__phy_write(phydev, REG_MII_MMD_CTRL, device);
__phy_write(phydev, REG_MII_MMD_DATA, reg);
__phy_write(phydev, REG_MII_MMD_CTRL, device | 0x4000);
val = __phy_read(phydev, REG_MII_MMD_DATA);
if (val < 0) {
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
dev_err(&phydev->dev, "error read mmd device(%u) reg (%u)\n",
device, reg);
#else
dev_err(&phydev->mdio.dev,
"error read mmd device(%u) reg (%u)\n", device, reg);
#endif
goto err_handle;
}
err_handle:
phy_unlock_mdio_bus(phydev);
return val;
}
__attribute__((unused)) static int ytphy_write_mmd(struct phy_device* phydev,
u16 device, u16 reg,
u16 value)
{
int ret = 0;
phy_lock_mdio_bus(phydev);
__phy_write(phydev, REG_MII_MMD_CTRL, device);
__phy_write(phydev, REG_MII_MMD_DATA, reg);
__phy_write(phydev, REG_MII_MMD_CTRL, device | 0x4000);
__phy_write(phydev, REG_MII_MMD_DATA, value);
phy_unlock_mdio_bus(phydev);
return ret;
}
__attribute__((unused)) static int __ytphy_read_mmd(struct phy_device* phydev,
u16 device, u16 reg)
{
int val;
__phy_write(phydev, REG_MII_MMD_CTRL, device);
__phy_write(phydev, REG_MII_MMD_DATA, reg);
__phy_write(phydev, REG_MII_MMD_CTRL, device | 0x4000);
val = __phy_read(phydev, REG_MII_MMD_DATA);
return val;
}
__attribute__((unused)) static int __ytphy_write_mmd(struct phy_device* phydev,
u16 device, u16 reg,
u16 value)
{
__phy_write(phydev, REG_MII_MMD_CTRL, device);
__phy_write(phydev, REG_MII_MMD_DATA, reg);
__phy_write(phydev, REG_MII_MMD_CTRL, device | 0x4000);
__phy_write(phydev, REG_MII_MMD_DATA, value);
return 0;
}
static int __ytphy_soft_reset(struct phy_device *phydev)
{
int ret = 0, val = 0;
val = __phy_read(phydev, MII_BMCR);
if (val < 0)
return val;
ret = __phy_write(phydev, MII_BMCR, val | BMCR_RESET);
if (ret < 0)
return ret;
return ret;
}
struct yt_ptp_private {
struct phy_device *phydev;
#if (KERNEL_VERSION(5, 5, 19) < LINUX_VERSION_CODE)
struct mii_timestamper mii_ts;
#endif
struct ptp_clock *clock;
struct ptp_clock_info clock_info;
struct mutex mutex;
struct sk_buff_head tx_queue;
int tx_type; //ioctl()
bool hwts_rx; //ioctl()
int layer;
int version;
};
struct yt_ptp_skb_cb {
unsigned long timeout;
u16 seq_id;
u8 msgtype;
};
struct yt_ptp_capture {
ktime_t hwtstamp;
u16 seq_id;
u8 msgtype;
};
#if (KERNEL_VERSION(5, 5, 19) < LINUX_VERSION_CODE)
static struct yt_ptp_private *miits2ptppriv(struct mii_timestamper *mii_ts)
{
return container_of(mii_ts, struct yt_ptp_private, mii_ts);
}
#endif
static struct yt_ptp_private *clkinfo2ptppriv(struct ptp_clock_info *clock_info)
{
return container_of(clock_info, struct yt_ptp_private, clock_info);
}
static int yt8011_ptp_get_ts_paged(struct phy_device *phydev,
struct timespec64 *ts, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
/* ext reg 0x8049 rtc_real_time_i bit63:48(s_h) */
ret = __ytphy_read_ext(phydev, 0x8049);
if (ret < 0)
goto err_restore_page;
ts->tv_sec = (ret << 16);
/* ext reg 0x804a rtc_real_time_i bit47:32(s_l) */
ret = __ytphy_read_ext(phydev, 0x804a);
if (ret < 0)
goto err_restore_page;
ts->tv_sec |= ret;
/* ext reg 0x804b rtc_real_time_i bit31:16(ns_h) */
ret = __ytphy_read_ext(phydev, 0x804b);
if (ret < 0)
goto err_restore_page;
ts->tv_nsec = (ret << 16);
/* ext reg 0x804c rtc_real_time_i bit15:0(ns_l) */
ret = __ytphy_read_ext(phydev, 0x804c);
if (ret < 0)
goto err_restore_page;
ts->tv_nsec |= ret;
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
static int yt8011_ptp_set_ts_paged(struct phy_device *phydev,
const struct timespec64 *ts, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
/* ext reg 0x8062(sec) RTC_PRELOAD_VAL bit63:48 */
ret = __ytphy_write_ext(phydev, 0x8062, (ts->tv_sec >> 16) & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ext reg 0x8063(sec) RTC_PRELOAD_VAL bit47:32 */
ret = __ytphy_write_ext(phydev, 0x8063, ts->tv_sec & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ext reg 0x8064(nsec) RTC_PRELOAD_VAL bit31:16 */
ret = __ytphy_write_ext(phydev, 0x8064, (ts->tv_nsec >> 16) & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ext reg 0x8065(nsec) RTC_PRELOAD_VAL bit15:0 */
ret = __ytphy_write_ext(phydev, 0x8065, ts->tv_nsec & 0xffff);
if (ret < 0)
goto err_restore_page;
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
static int yt8011_ptp_rtc_load_paged(struct phy_device *phydev, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
/*
* YT8011 load RTC val, ext reg 0x8060 bit0 1'b1
* Setting it to 1 loads value 8061~8065 RTC_PRELOAD to RTC.
* This bit is self-clearing and always read back as 0.
*/
ret = __ytphy_read_ext(phydev, 0x8060);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x8060, ret | BIT(0));
if (ret < 0)
goto err_restore_page;
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
#if (KERNEL_VERSION(4, 20, 17) < LINUX_VERSION_CODE)
static int yt8011_ptp_gettimex(struct ptp_clock_info *clock_info,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct yt_ptp_private *ptp_priv = clkinfo2ptppriv(clock_info);
int err;
mutex_lock(&ptp_priv->mutex);
err = yt8011_ptp_get_ts_paged(ptp_priv->phydev, ts,
YT8011_REG_SPACE_UTP);
mutex_unlock(&ptp_priv->mutex);
return err < 0 ? err : 0;
}
#else
static int yt8011_ptp_gettime(struct ptp_clock_info *clock_info,
struct timespec64 *ts)
{
struct yt_ptp_private *ptp_priv = clkinfo2ptppriv(clock_info);
int err;
mutex_lock(&ptp_priv->mutex);
err = yt8011_ptp_get_ts_paged(ptp_priv->phydev, ts,
YT8011_REG_SPACE_UTP);
mutex_unlock(&ptp_priv->mutex);
return err < 0 ? err : 0;
}
#endif
static int yt8011_ptp_settime(struct ptp_clock_info *clock_info,
const struct timespec64 *ts)
{
struct yt_ptp_private *ptp_priv = clkinfo2ptppriv(clock_info);
int err;
mutex_lock(&ptp_priv->mutex);
err = yt8011_ptp_set_ts_paged(ptp_priv->phydev, ts,
YT8011_REG_SPACE_UTP);
if (err < 0)
goto out;
err = yt8011_ptp_rtc_load_paged(ptp_priv->phydev,
YT8011_REG_SPACE_UTP);
out:
mutex_unlock(&ptp_priv->mutex);
return err < 0 ? err : 0;
}
static int yt8011_ptp_adjtime_paged(struct phy_device *phydev,
struct timespec64* ts, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
/* ext reg 0x8045 OFFSET_SEC bit31:16 */
ret = __ytphy_write_ext(phydev, 0x8045, (ts->tv_sec >> 16) & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ext reg 0x8046 OFFSET_SEC bit15:0 */
ret = __ytphy_write_ext(phydev, 0x8046, ts->tv_sec & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ext reg 0x8042 OFFSET_NANO bit31:16 */
ret = __ytphy_write_ext(phydev, 0x8042, (ts->tv_nsec >> 16) & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ext reg 0x8043 OFFSET_NANO bit15:0 */
ret = __ytphy_write_ext(phydev, 0x8043, ts->tv_nsec & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ext reg 0x8047 EN_OFFSET bit0
* 1'b1 adjust the RTC with the value OFFSET_SEC and OFFSET_NANO.
*/
ret = __ytphy_read_ext(phydev, 0x8047);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x8047, ret | BIT(0));
if (ret < 0)
goto err_restore_page;
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
static int yt8011_ptp_adjtime(struct ptp_clock_info *clock_info, s64 delta_ns)
{
struct yt_ptp_private *ptp_priv = clkinfo2ptppriv(clock_info);
struct phy_device *phydev = ptp_priv->phydev;
struct timespec64 ts;
int err;
ts = ns_to_timespec64(delta_ns);
mutex_lock(&ptp_priv->mutex);
err = yt8011_ptp_adjtime_paged(phydev, &ts, YT8011_REG_SPACE_UTP);
mutex_unlock(&ptp_priv->mutex);
return err < 0 ? err : 0;
}
static int yt8011_ptp_adjfine_paged(struct phy_device *phydev,
unsigned long adj_step, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
/*
* ext reg 0x8040 RTC_STEP bit31:16
* ext reg 0x8041 RTC_STEP bit15:0
*/
ret = __ytphy_write_ext(phydev, 0x8040, (adj_step >> 16) & 0xffff);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x8041, adj_step & 0xffff);
if (ret < 0)
goto err_restore_page;
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
/* rtc clk 375MHz, rtc step = 1 000 000 000 / 375 000 000 = 2.66ns
* fractional part(0.66 << 26 = 0x2AA AAAA), integer part(2 << 26 = 0x800 0000)
* so, the frequency adjustment base is 0xAAA AAAA(0x2AA AAAA | 0x800 0000),
* or 2.6666667 * (2^26) = (2.6666667 << 26)
*
* Frequency adjustment is
* adj = {scaled_ppm * 0xAAAAAAA} / (10^6 * 2^16)
* adj = {scaled_ppm * [2.6666667 * (2^26)]} / (10^6 * 2^16)
* adj = {scaled_ppm * [2.6666667 * (2^26)]} / ((2 * 5)^6 * 2^16)
* adj = {scaled_ppm * [2.6666667 * (2^26)]} / (5^6 * 2^22)
* adj = {scaled_ppm * [2.6666667 * (2^4)]} / (5^6)
* adj = {scaled_ppm * [2.6666667 * (2^4)]} / 15625
* scaled_ppm: long(unit: ppm) and Q16.16 format
* #define RTC_DEFAULT_CYCLE_RATE 0xAAAAAAA(2.6666667*(2^26))
* double rate = (1000000 + scaled_ppm) / 10^6;
* double rate = (1000000 + scaled_ppm) / 1000000;
* double step = ((double)RTC_DEFAULT_CYCLE_RATE / 2^26) * rate;
* double step = ((double)RTC_DEFAULT_CYCLE_RATE / 0x4000000) * rate;
* unsigned long adj_step = (unsigned long)(step << 26 = step * 2^26);
* unsigned long adj_step = (unsigned long)(step * 0x4000000);
*/
static int yt8011_ptp_adjfine(struct ptp_clock_info *clock_info,
long scaled_ppm)
{
#define RTC_DEFAULT_CYCLE_RATE 0xAAAAAAA
struct yt_ptp_private *ptp_priv = clkinfo2ptppriv(clock_info);
unsigned long adj_step;
int neg_adj = 0;
u32 diff;
u64 adj;
dbg_ptp("@jie.han %s, %s, %d scaled_ppm: %ld\n",
__FILE__, __func__, __LINE__, scaled_ppm);
if (scaled_ppm < 0) {
neg_adj = 1;
scaled_ppm = -scaled_ppm;
}
//adj = (scaled_ppm * 266667LL) / 100000LL;
adj = (u64)scaled_ppm * 266667LL;
adj = div64_u64(adj, 100000LL);
adj <<= 4;
diff = div_u64(adj, 15625);
//diff = div64_u64(adj, 15625);
adj_step = RTC_DEFAULT_CYCLE_RATE + (neg_adj ? -diff : diff);
mutex_lock(&ptp_priv->mutex);
yt8011_ptp_adjfine_paged(ptp_priv->phydev, adj_step,
YT8011_REG_SPACE_UTP);
mutex_unlock(&ptp_priv->mutex);
return 0;
}
#define YT_SKB_CB(skb) ((struct yt_ptp_skb_cb *)(skb)->cb)
static const struct ptp_clock_info yt8011_ptp_clock_info = {
.owner = THIS_MODULE,
.name = KBUILD_MODNAME,
.max_adj = 100000000,
#if (KERNEL_VERSION(4, 20, 17) < LINUX_VERSION_CODE)
.gettimex64 = yt8011_ptp_gettimex,
#else
.gettime64 = yt8011_ptp_gettime,
#endif
.settime64 = yt8011_ptp_settime,
.adjtime = yt8011_ptp_adjtime,
.adjfine = yt8011_ptp_adjfine,
.do_aux_work = yt8011_ptp_do_aux_work,
};
static int yt_ptp_get_txtstamp_paged(struct phy_device *phydev,
struct yt_ptp_capture *capts, u8 page)
{
int ret = 0, oldpage;
uint32_t sec, nsec;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
ret = __ytphy_read_ext(phydev, 0x8020);
if (ret < 0)
goto err_restore_page;
capts->seq_id = ret;
ret = (__ytphy_read_ext(phydev, 0x802b) & 0xf000) >> 12;
if (ret < 0)
goto err_restore_page;
capts->msgtype = ret;
ret = __ytphy_read_ext(phydev, 0x8027);
if (ret < 0)
goto err_restore_page;
sec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x8028);
if (ret < 0)
goto err_restore_page;
sec |= ret;
ret = __ytphy_read_ext(phydev, 0x8029);
if (ret < 0)
goto err_restore_page;
nsec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x802a);
if (ret < 0)
goto err_restore_page;
nsec |= ret;
capts->hwtstamp = ktime_set(sec, nsec);
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret < 0 ? ret : 0);
}
static bool yt8011_ptp_get_txtstamp(struct yt_ptp_private *ptp_priv,
struct yt_ptp_capture *capts)
{
struct phy_device *phydev = ptp_priv->phydev;
int ret;
//dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
mutex_lock(&ptp_priv->mutex);
ret = yt_ptp_get_txtstamp_paged(phydev, capts, YT8011_REG_SPACE_UTP);
mutex_unlock(&ptp_priv->mutex);
dbg_ptp("@jie.han %s, %s, %d yt_ptp_get_txtstamp_paged() ret: %d, "
"capts->seq_id: %d, capts->msgtype: %s\n",
__FILE__, __func__, __LINE__, ret,
capts->seq_id,
capts->msgtype == SYNC ? "SYNC" :
capts->msgtype == DELAY_REQ ? "DELAY_REQ" :
capts->msgtype == PDELAY_REQ ? "PDELAY_REQ" :
capts->msgtype == PDELAY_RESP ? "PDELAY_RESP" :
capts->msgtype == FOLLOW_UP ? "FOLLOW_UP" :
capts->msgtype == DELAY_RESP ? "DELAY_RESP" :
capts->msgtype == PDELAY_RESP_FOLLOW_UP ? "PDELAY_RESP_FOLLOW_UP" :
capts->msgtype == ANNOUNCE ? "ANNOUNCE" :
capts->msgtype == SIGNALING ? "SIGNALING" :
capts->msgtype == MANAGEMENT ? "MANAGEMENT" : "other");
return (ret < 0 ? false : true);
}
static bool yt8011_ptp_match_txtstamp(struct yt_ptp_private *ptp_priv,
struct yt_ptp_capture *capts)
{
//struct skb_shared_hwtstamps hwts;
struct skb_shared_hwtstamps *shhwtstamps;
//struct sk_buff *skb, *ts_skb;
unsigned long now = jiffies;
struct sk_buff *skb, *skb_tmp;
bool matched = false;
unsigned long flags;
//bool first = false;
u8 msgtype;
u16 seqid;
//dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
//ts_skb = NULL;
spin_lock_irqsave(&ptp_priv->tx_queue.lock, flags);
skb_queue_walk_safe(&ptp_priv->tx_queue, skb, skb_tmp) {
dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_flags: %#lx\n",
__FILE__, __func__, __LINE__, skb->sk->sk_flags);
//dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_refcnt: %d\n",
//__FILE__, __func__, __LINE__, atomic_read(&skb->sk->sk_refcnt)); //compile err
//__FILE__, __func__, __LINE__, refcount_read(&skb->sk->sk_refcnt));//compile err
//__FILE__, __func__, __LINE__, skb->sk->sk_refcnt.refs.counter);
dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_refcnt: %d\n",
__FILE__, __func__, __LINE__, skb->sk->sk_refcnt.refs.counter);
if (time_after(now, YT_SKB_CB(skb)->timeout)) {
dbg_ptp("@jie.han %s, %s, %d time_after(skb) timeout, then free it.\n",
__FILE__, __func__, __LINE__);
__skb_unlink(skb, &ptp_priv->tx_queue);
if (skb->sk)
sock_put(skb->sk);
kfree_skb(skb);
continue;
}
seqid = YT_SKB_CB(skb)->seq_id;
msgtype = YT_SKB_CB(skb)->msgtype;
if (seqid == capts->seq_id && msgtype == capts->msgtype) {
__skb_unlink(skb, &ptp_priv->tx_queue);
dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_flags: %#lx\n",
__FILE__, __func__, __LINE__, skb->sk->sk_flags);
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(*shhwtstamps));
shhwtstamps->hwtstamp = capts->hwtstamp;
//skb->tstamp = ktime_to_ns(capts->hwtstamp);
skb->tstamp = ktime_get_real();
skb->ip_summed = CHECKSUM_UNNECESSARY;
dbg_ptp("@jie.han %s, %s, %d matched, seqid: %d, msgtype: %s\n",
__FILE__, __func__, __LINE__,
seqid,
msgtype == SYNC ? "SYNC" :
msgtype == DELAY_REQ ? "DELAY_REQ" :
msgtype == PDELAY_REQ ? "PDELAY_REQ" :
msgtype == PDELAY_RESP ? "PDELAY_RESP" :
msgtype == FOLLOW_UP ? "FOLLOW_UP" :
msgtype == DELAY_RESP ? "DELAY_RESP" :
msgtype == PDELAY_RESP_FOLLOW_UP ? "PDELAY_RESP_FOLLOW_UP" :
msgtype == ANNOUNCE ? "ANNOUNCE" :
msgtype == SIGNALING ? "SIGNALING" :
msgtype == MANAGEMENT ? "MANAGEMENT" : "other");
if (skb->sk) {
//__skb_complete_tx_timestamp(skb, skb->sk,
// SCM_TSTAMP_SND, false);
//sock_put(skb->sk);
dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_flags: %#lx\n",
__FILE__, __func__, __LINE__, skb->sk->sk_flags);
memset(YT_SKB_CB(skb), 0x0, sizeof(struct yt_ptp_skb_cb));
skb_complete_tx_timestamp(skb, shhwtstamps);
} else
kfree_skb(skb);
matched = true;
break;
}
}
spin_unlock_irqrestore(&ptp_priv->tx_queue.lock, flags);
return matched;
}
void yt8011_ptp_purge_tx_queue(struct yt_ptp_private *ptp_priv)
{
struct sk_buff *skb, *tmp;
unsigned long flags;
spin_lock_irqsave(&ptp_priv->tx_queue.lock, flags);
skb_queue_walk_safe(&ptp_priv->tx_queue, skb, tmp) {
__skb_unlink(skb, &ptp_priv->tx_queue);
if (skb->sk)
sock_put(skb->sk);
kfree_skb(skb);
}
spin_unlock_irqrestore(&ptp_priv->tx_queue.lock, flags);
}
static long yt8011_ptp_do_aux_work(struct ptp_clock_info *clock_info)
{
struct yt_ptp_private *ptp_priv = clkinfo2ptppriv(clock_info);
struct yt_ptp_capture capts;
bool reschedule = false;
int limit = 8;
memset(&capts, 0x0, sizeof(capts));
//dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
/* ref to bcm and nxp in kernel v6.9 */
while (!skb_queue_empty_lockless(&ptp_priv->tx_queue) && limit--) {
if (!yt8011_ptp_get_txtstamp(ptp_priv, &capts)) {
reschedule = true;
break;
}
if (!yt8011_ptp_match_txtstamp(ptp_priv, &capts)) {
pr_warn("@jie.han %s, %s, %d "
"failed to match TX timestamp, msgtype = 0x%x, seqid = %d\n",
__FILE__, __func__, __LINE__, capts.msgtype, capts.seq_id);
//skb_queue_purge(&ptp_priv->tx_queue);
yt8011_ptp_purge_tx_queue(ptp_priv);
reschedule = true;
break;
}
}
/* delay of the next auxiliary work scheduling time (>=0) or
* negative value in case further scheduling is not required.
*/
dbg_ptp("@jie.han %s, %s, %d reschedule: %s\n",
__FILE__, __func__, __LINE__, reschedule == true ? "true" : "false");
return reschedule ? 1 : -1;
}
static int yt8011_ptp_get_rxtstamp_paged(struct phy_device *phydev,
u8 eventtype, u16 seqid,
u32 *sec, u32 *nsec,
u8 page)
{
int ret = 0, oldpage;
uint16_t id;
int type;
int idx;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
for (idx = 0; idx < 4; idx++) {
if (idx == 0) {
ret = __ytphy_read_ext(phydev, 0x8010);
if (ret < 0)
goto err_restore_page;
id = ret;
ret = __ytphy_read_ext(phydev, 0x801b);
if (ret < 0)
goto err_restore_page;
type = (ret & 0xf000) >> 12;
if (id == seqid && type == eventtype) {
ret = __ytphy_read_ext(phydev, 0x8017);
if (ret < 0)
goto err_restore_page;
*sec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x8018);
if (ret < 0)
goto err_restore_page;
*sec |= ret;
ret = __ytphy_read_ext(phydev, 0x8019);
if (ret < 0)
goto err_restore_page;
*nsec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x801a);
if (ret < 0)
goto err_restore_page;
*nsec |= ret;
break;
}
} else if (idx == 1) {
ret = __ytphy_read_ext(phydev, 0x8100);
if (ret < 0)
goto err_restore_page;
id = ret;
ret = __ytphy_read_ext(phydev, 0x810b);
if (ret < 0)
goto err_restore_page;
type = (ret & 0xf000) >> 12;
if (id == seqid && type == eventtype) {
ret = __ytphy_read_ext(phydev, 0x8107);
if (ret < 0)
goto err_restore_page;
*sec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x8108);
if (ret < 0)
goto err_restore_page;
*sec |= ret;
ret = __ytphy_read_ext(phydev, 0x8109);
if (ret < 0)
goto err_restore_page;
*nsec = ret << 16;
ret= __ytphy_read_ext(phydev, 0x810a);
if (ret < 0)
goto err_restore_page;
*nsec |= ret;
break;
}
} else if (idx == 2) {
ret = __ytphy_read_ext(phydev, 0x8110);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_read_ext(phydev, 0x811b);
if (ret < 0)
goto err_restore_page;
type = (ret & 0xf000) >> 12;
if (id == seqid && type == eventtype) {
ret = __ytphy_read_ext(phydev, 0x8117);
if (ret < 0)
goto err_restore_page;
*sec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x8118);
if (ret < 0)
goto err_restore_page;
*sec |= ret;
ret = __ytphy_read_ext(phydev, 0x8119);
if (ret < 0)
goto err_restore_page;
*nsec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x811a);
if (ret < 0)
goto err_restore_page;
*nsec |= ret;
break;
}
} else if (idx == 3) {
ret = __ytphy_read_ext(phydev, 0x8120);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_read_ext(phydev, 0x812b);
if (ret < 0)
goto err_restore_page;
type = (ret & 0xf000) >> 12;
if (id == seqid && type == eventtype) {
ret = __ytphy_read_ext(phydev, 0x8127);
if (ret < 0)
goto err_restore_page;
*sec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x8128);
if (ret < 0)
goto err_restore_page;
*sec |= ret;
ret = __ytphy_read_ext(phydev, 0x8129);
if (ret < 0)
goto err_restore_page;
*nsec = ret << 16;
ret = __ytphy_read_ext(phydev, 0x812a);
if (ret < 0)
goto err_restore_page;
*nsec |= ret;
break;
}
}
}
dbg_ptp("@jie.han %s, %s, %d idx: %d\n",
__FILE__, __func__, __LINE__, idx);
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
#if (KERNEL_VERSION(5, 5, 19) < LINUX_VERSION_CODE)
static bool yt8011_ptp_mii_rxtstamp(struct mii_timestamper *mii_ts,
struct sk_buff *skb, int type)
{
struct yt_ptp_private *ptp_priv = miits2ptppriv(mii_ts);
struct phy_device *phydev = ptp_priv->phydev;
struct skb_shared_hwtstamps *hwts;
struct ptp_header *header;
u16 sequence_id;
u32 sec, nsec;
u8 msg_type;
int ret;
dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
if (!ptp_priv->hwts_rx)
return false;
header = ptp_parse_header(skb, type);
if (!header)
return false;
msg_type = ptp_get_msgtype(header, type);
sequence_id = header->sequence_id;
ret = yt8011_ptp_get_rxtstamp_paged(phydev, msg_type,
sequence_id, &sec, &nsec,
YT8011_REG_SPACE_UTP);
if (ret > 0) {
//get the rx timestamp and assigned skb's hwts->hwtstamp
hwts = skb_hwtstamps(skb);
hwts->hwtstamp = ktime_set(sec, nsec);
netif_rx(skb);
return true;
} else
return false;
}
static void yt8011_ptp_mii_txtstamp(struct mii_timestamper *mii_ts,
struct sk_buff *skb, int type)
{
struct yt_ptp_private *ptp_priv = miits2ptppriv(mii_ts);
struct ptp_header *hdr;
int msgtype;
dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
hdr = ptp_parse_header(skb, type);
if (!hdr)
goto out;
msgtype = ptp_get_msgtype(hdr, type);
dbg_ptp("@jie.han %s, %s, %d ptp_priv->tx_type: %d\n",
__FILE__, __func__, __LINE__, ptp_priv->tx_type);
switch (ptp_priv->tx_type) {
case HWTSTAMP_TX_ONESTEP_P2P:
case HWTSTAMP_TX_ONESTEP_SYNC:
case HWTSTAMP_TX_ON:
YT_SKB_CB(skb)->timeout = jiffies + SKB_TIMESTAMP_TIMEOUT;
YT_SKB_CB(skb)->seq_id = be16_to_cpu(hdr->sequence_id);
YT_SKB_CB(skb)->msgtype = msgtype;
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
skb_queue_tail(&ptp_priv->tx_queue, skb);
ptp_schedule_worker(ptp_priv->clock, 0);
return;
default:
break;
}
out:
kfree_skb(skb);
}
static int yt8011_ptp_mii_hwtstamp(struct mii_timestamper *mii_ts,
struct kernel_hwtstamp_config *cfg,
struct netlink_ext_ack *extack)
{
struct yt_ptp_private *ptp_priv = miits2ptppriv(mii_ts);
struct yt8xxx_priv *priv;
u16 cfg0 = 0, cfg1 = 0;
dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
priv = container_of(ptp_priv, struct yt8xxx_priv, ptp_priv);
switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE:
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
ptp_priv->hwts_rx = 0;
ptp_priv->layer = 0;
ptp_priv->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:
ptp_priv->hwts_rx = 1;
ptp_priv->layer = PTP_CLASS_L4;
ptp_priv->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:
ptp_priv->hwts_rx = 1;
ptp_priv->layer = PTP_CLASS_L2;
ptp_priv->version = PTP_CLASS_V2;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
ptp_priv->hwts_rx = 1;
ptp_priv->layer = PTP_CLASS_L4 | PTP_CLASS_L2;
ptp_priv->version = PTP_CLASS_V2;
break;
default:
return -ERANGE;
}
ptp_priv->tx_type = cfg->tx_type;
cfg0 |= OC_2_STEPS;
if (priv->role == MASTER)
cfg0 |= (DAC_CLK << 2);
else
cfg0 |= (ADC_CLK_AFTER_ROTATOR << 2);
/* ext reg 0x8000 bit4 BP_1588
* 1'b0 = PTP timestamp engine normal operation.
* 1'b1 = Bypass IEEE1588v2 related functions.
*/
cfg0 &= ~BIT(4);
/* ext reg 0x8000 bit7 EN_GATE_1588
* 1'b0 = Disable gating 1588 clock domain.
* 1'b1 = Enable gating 1588 clock domain.
*/
cfg0 &= ~BIT(7);
/* ext reg 0x8001 bit4
* 1'b1: PTP mode 1'b0:gPTP mode(802.1AS)
*/
if (ptp_priv->layer & PTP_CLASS_L4)
cfg1 |= BIT(4);
else
cfg1 &= ~BIT(4);
mutex_lock(&ptp_priv->mutex);
yt8011_ptp_init_paged(ptp_priv->phydev, cfg0, cfg1,
YT8011_REG_SPACE_UTP);
mutex_unlock(&ptp_priv->mutex);
/* purge existing data */
skb_queue_purge(&priv->tx_queue);
return 0;
}
static int yt8011_ptp_mii_ts_info(struct mii_timestamper *mii_ts,
struct ethtool_ts_info *ts_info)
{
struct yt_ptp_private *ptp_priv = miits2ptppriv(mii_ts);
dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
ts_info->phc_index = ptp_clock_index(ptp_priv->clock);
ts_info->so_timestamping = //software and hardware ts support
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
//v5.15 support HWTSTAMP_TX_ONESTEP_P2P
ts_info->tx_types = //hardware ts support only
BIT(HWTSTAMP_TX_ON) |
BIT(HWTSTAMP_TX_OFF) |
//BIT(HWTSTAMP_TX_ONESTEP_SYNC) |
BIT(HWTSTAMP_TX_ONESTEP_P2P);
ts_info->rx_filters = //hardware ts support only
BIT(HWTSTAMP_FILTER_NONE) |
BIT(HWTSTAMP_FILTER_PTP_V2_EVENT);
return 0;
}
#endif
time64_t read_rtc_time_sec(void) {
struct rtc_device *rtc;
struct rtc_time tm;
int ret;
time64_t seconds = 0;
//u64 nanoseconds;
// 1. �� RTC �豸(�����豸��Ϊ "rtc0")
//https://elixir.bootlin.com/linux/v3.8.13/A/ident/rtc_class_open
//https://elixir.bootlin.com/linux/v6.15-rc5/A/ident/rtc_class_open
rtc = rtc_class_open("rtc0");
if (IS_ERR(rtc)) {
pr_err("Failed to open RTC device");
return seconds;
}
// 2. ��ȡʱ�䵽 tm �ṹ��
//https://elixir.bootlin.com/linux/v3.8.13/A/ident/rtc_read_time
//https://elixir.bootlin.com/linux/v6.15-rc5/A/ident/rtc_read_time
ret = rtc_read_time(rtc, &tm);
if (ret) {
pr_err("Failed to read RTC time");
goto out;
}
// 3. �� rtc_time ת��Ϊ Unix ʱ���(��)
//https://elixir.bootlin.com/linux/v3.19/A/ident/rtc_tm_to_time64
//https://elixir.bootlin.com/linux/v6.15-rc5/A/ident/rtc_tm_to_time64
seconds = rtc_tm_to_time64(&tm);
// 4. ת��Ϊ����
//nanoseconds = seconds * NSEC_PER_SEC;
//pr_info("RTC Time: %lld.%09d seconds since 1970-01-01 00:00:00 UTC",
// (long long)seconds, (int)(nanoseconds % NSEC_PER_SEC));
out:
rtc_class_close(rtc); // �ر��豸
return seconds;
}
static int yt8011_ptp_init_paged(struct phy_device *phydev,
u16 cfg0, u16 cfg1, u8 page)
{
int ret = 0, oldpage;
time64_t seconds;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x8000, cfg0);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x8001, cfg1);
if (ret < 0)
goto err_restore_page;
/* rtc step cfg
* rtc clk f = 375MHz, T = 2.6666667ns
* rtc step = T << 26 = 0xaaa aaaa
* ext reg 0x8040 RTC_STEP
* bit31:16 MSB 16bits of part of the RTC step.
* ext reg 0x8041 RTC_STEP
* bit15:0 LSB 16bits of part of the RTC step.
*/
ret = __ytphy_write_ext(phydev, 0x8040, 0x0aaa);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x8041, 0xaaaa);
if (ret < 0)
goto err_restore_page;
/* rtc cfg
* eg, 2023-01-01 00h00m00 s -> 1672531200 s -> 0x63b0 cd00
* write ext reg 0x8061(sec) RTC_PRELOAD_VAL bit79:64
* write ext reg 0x8062(sec) RTC_PRELOAD_VAL bit63:48
* write ext reg 0x8063(sec) RTC_PRELOAD_VAL bit47:32
* write ext reg 0x8064(nsec) RTC_PRELOAD_VAL bit31:16
* write ext reg 0x8065(nsec) RTC_PRELOAD_VAL bit15:0
*/
seconds = read_rtc_time_sec();
/* s_h */
ret = __ytphy_write_ext(phydev, 0x8061, 0x0);
if (ret < 0)
goto err_restore_page;
/* s_m */
//ret = __ytphy_write_ext(phydev, 0x8062, 0x63b0);
ret = __ytphy_write_ext(phydev, 0x8062, (seconds >> 16) & 0xffff);
if (ret < 0)
goto err_restore_page;
/* s_l */
//ret = __ytphy_write_ext(phydev, 0x8063, 0xcd00);
ret = __ytphy_write_ext(phydev, 0x8063, seconds & 0xffff);
if (ret < 0)
goto err_restore_page;
/* ns_h */
ret = __ytphy_write_ext(phydev, 0x8064, 0x0);
if (ret < 0)
goto err_restore_page;
/* ns_l */
ret = __ytphy_write_ext(phydev, 0x8065, 0x0);
if (ret < 0)
goto err_restore_page;
/* load rtc
* ext reg 0x8060 bit0 LOAD_RTC
* 1'b1 loads value 0x8061~0x8065 RTC_PRELOAD to RTC.
* This bit is self-clearing and always read back as 0.
*/
ret = __ytphy_read_ext(phydev, 0x8060);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x8060, ret | BIT(0));
if (ret < 0)
goto err_restore_page;
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
struct yt_ptp_private *yt8011_ptp_priv(struct phy_device *phydev)
{
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
struct device *dev = &phydev->dev;
#else
struct device *dev = &phydev->mdio.dev;
#endif
struct yt_ptp_private *ptp_priv;
struct ptp_clock *clock;
ptp_priv = devm_kzalloc(dev, sizeof(*ptp_priv), GFP_KERNEL);
if (!ptp_priv)
return ERR_PTR(-ENOMEM);
//return -ENOMEM;
ptp_priv->clock_info = yt8011_ptp_clock_info;
clock = ptp_clock_register(&ptp_priv->clock_info, dev);
if (IS_ERR(clock))
return ERR_CAST(clock);
ptp_priv->clock = clock;
ptp_priv->phydev = phydev;
mutex_init(&ptp_priv->mutex);
skb_queue_head_init(&ptp_priv->tx_queue);
#if (KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE)
#else
ptp_priv->mii_ts.rxtstamp = yt8011_ptp_mii_rxtstamp;
ptp_priv->mii_ts.txtstamp = yt8011_ptp_mii_txtstamp;
ptp_priv->mii_ts.hwtstamp = yt8011_ptp_mii_hwtstamp;
ptp_priv->mii_ts.ts_info = yt8011_ptp_mii_ts_info;
ptp_priv->phydev->mii_ts = &ptp_priv->mii_ts;
#endif
return ptp_priv;
}
static int yt8011_probe(struct phy_device *phydev)
{
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
struct device *dev = &phydev->dev;
#else
struct device *dev = &phydev->mdio.dev;
#endif
struct yt8xxx_priv *priv;
int chip_config;
u16 role;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
//return ERR_PTR(-ENOMEM);
return -ENOMEM;
phydev->priv = priv;
/* ext reg 0x9030 bit0
* 0 = chip works in RGMII mode; 1 = chip works in SGMII mode
*/
chip_config = ytphy_read_ext(phydev, 0x9030);
priv->chip_mode = chip_config & 0x1;
role = ytphy_read_mmd(phydev, 0x1, 0x834);
priv->role = role & BIT(14) ? MASTER : SLAVE;
priv->ptp_priv = yt8011_ptp_priv(phydev);
if (IS_ERR(priv->ptp_priv))
return PTR_ERR(priv->ptp_priv);
return 0;
}
static int yt8011_soft_reset_paged(struct phy_device *phydev, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage >= 0)
ret = __ytphy_soft_reset(phydev);
return yt8011_restore_page(phydev, oldpage, ret);
}
static int yt8011_soft_reset(struct phy_device *phydev)
{
struct yt8xxx_priv *priv = phydev->priv;
/* utp */
yt8011_soft_reset_paged(phydev, YT8011_REG_SPACE_UTP);
if (priv->chip_mode) { /* sgmii */
yt8011_soft_reset_paged(phydev, YT8011_REG_SPACE_SDS);
}
return 0;
}
static int yt8011_config_aneg(struct phy_device *phydev)
{
return 0;
}
#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE)
static int yt8011_aneg_done(struct phy_device *phydev)
{
int link_utp = 0;
/* UTP */
ytphy_write_ext(phydev, 0x9000, 0);
link_utp = !!(phy_read(phydev, REG_PHY_SPEC_STATUS) &
(BIT(YTXXXX_LINK_STATUS_BIT)));
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
netdev_info(phydev->attached_dev, "%s, phy addr: %d, link_utp: %d\n",
__func__, phydev->addr, link_utp);
#else
netdev_info(phydev->attached_dev, "%s, phy addr: %d, link_utp: %d\n",
__func__, phydev->mdio.addr, link_utp);
#endif
return !!(link_utp);
}
#endif
static int yt8011_config_paged(struct phy_device *phydev, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1008, 0x2119);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1092, 0x712);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x90bc, 0x7676);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x90b9, 0x620b);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x2001, 0x6418);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1019, 0x3712);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x101a, 0x3713);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x2005, 0x810);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x2015, 0x1012);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x2013, 0xff06);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x3017, 0x4);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x3027, 0xffe8);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x3026, 0x1b58);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x301e, 0xb40);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x3019, 0xffd4);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x3014, 0x1115);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x301a, 0x7800);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1000, 0x28);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1053, 0xf);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x105e, 0xa46c);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1088, 0x2b);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1088, 0x2b);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x1088, 0xb);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x3008, 0x141);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x3009, 0x1918);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9095, 0x1a1a);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9096, 0x1a10);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9097, 0x101a);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9098, 0x01ff);
if (ret < 0)
goto err_restore_page;
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
__attribute__((unused))
static int yt8011_config_rgmii_dvddio_3v3_paged(struct phy_device *phydev, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
if (page == YT8011_REG_SPACE_SDS) {
ret = __ytphy_write_ext(phydev, 0x0062, 0x0000);
if (ret < 0)
goto err_restore_page;
} else if (page == YT8011_REG_SPACE_UTP) {
ret = __ytphy_write_ext(phydev, 0x9031, 0xb200);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x903b, 0x0040);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x903e, 0x3b3b);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x903c, 0xf);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x903d, 0x1000);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9038, 0x0000);
if (ret < 0)
goto err_restore_page;
}
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
__attribute__((unused))
static int yt8011_config_rgmii_dvddio_2v5_paged(struct phy_device *phydev, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
if (page == YT8011_REG_SPACE_SDS) {
ret = __ytphy_write_ext(phydev, 0x0062, 0x0000);
if (ret < 0)
goto err_restore_page;
} else if (page == YT8011_REG_SPACE_UTP) {
ret = __ytphy_write_ext(phydev, 0x9031, 0xb200);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9111, 0x5);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9114, 0x3939);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9112, 0xf);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9110, 0x0);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9113, 0x10);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x903d, 0x2);
if (ret < 0)
goto err_restore_page;
}
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
__attribute__((unused))
static int yt8011_config_rgmii_dvddio_1v8_paged(struct phy_device *phydev, int page)
{
int ret = 0, oldpage;
oldpage = yt8011_select_page(phydev, page);
if (oldpage < 0)
goto err_restore_page;
if (page == YT8011_REG_SPACE_SDS) {
ret = __ytphy_write_ext(phydev, 0x0062, 0x0000);
if (ret < 0)
goto err_restore_page;
} else if (page == YT8011_REG_SPACE_UTP) {
ret = __ytphy_write_ext(phydev, 0x9031, 0xb200);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9116, 0x6);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9119, 0x3939);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9117, 0xf);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9115, 0x0);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x9118, 0x20);
if (ret < 0)
goto err_restore_page;
ret = __ytphy_write_ext(phydev, 0x903d, 0x3);
if (ret < 0)
goto err_restore_page;
}
err_restore_page:
return yt8011_restore_page(phydev, oldpage, ret);
}
/* #define YT801X_RGMII_DVDDIO_3V3 */
/* #define YT801X_RGMII_DVDDIO_2V5 */
/* #define YT801X_RGMII_DVDDIO_1V8 */
static int yt8011_config_init(struct phy_device *phydev)
{
struct yt8xxx_priv *priv = phydev->priv;
int ret;
phydev->autoneg = AUTONEG_DISABLE;
ret = yt8011_config_paged(phydev, YT8011_REG_SPACE_UTP);
if (ret < 0)
return ret;
if (!(priv->chip_mode)) { /* rgmii config */
#if defined (YT801X_RGMII_DVDDIO_3V3)
ret = yt8011_config_rgmii_dvddio_3v3_paged(phydev,
YT8011_REG_SPACE_SDS);
if (ret < 0)
return ret;
ret = yt8011_config_rgmii_dvddio_3v3_paged(phydev,
YT8011_REG_SPACE_UTP);
if (ret < 0)
return ret;
#elif defined (YT801X_RGMII_DVDDIO_2V5)
ret = yt8011_config_rgmii_dvddio_2v5_paged(phydev,
YT8011_REG_SPACE_SDS);
if (ret < 0)
return ret;
ret = yt8011_config_rgmii_dvddio_2v5_paged(phydev,
YT8011_REG_SPACE_UTP);
if (ret < 0)
return ret;
#elif defined (YT801X_RGMII_DVDDIO_1V8)
ret = yt8011_config_rgmii_dvddio_1v8_paged(phydev,
YT8011_REG_SPACE_SDS);
if (ret < 0)
return ret;
ret = yt8011_config_rgmii_dvddio_1v8_paged(phydev,
YT8011_REG_SPACE_UTP);
if (ret < 0)
return ret;
#endif
}
if (phydev->drv->txtstamp)
dbg_ptp("@jie.han %s, %s, %d phydev->drv->txtstamp registed.\n",
__FILE__, __func__, __LINE__);
else
dbg_ptp("@jie.han %s, %s, %d phydev->drv->txtstamp not registed.\n",
__FILE__, __func__, __LINE__);
if (phydev->drv->rxtstamp)
dbg_ptp("@jie.han %s, %s, %d phydev->drv->rxtstamp registed.\n",
__FILE__, __func__, __LINE__);
else
dbg_ptp("@jie.han %s, %s, %d phydev->drv->rxtstamp not registed.\n",
__FILE__, __func__, __LINE__);
yt8011_soft_reset(phydev);
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n",
__func__, phydev->addr);
#else
netdev_info(phydev->attached_dev, "%s done, phy addr: %d\n",
__func__, phydev->mdio.addr);
#endif
return 0;
}
static int ytxxxx_automotive_adjust_status(struct phy_device *phydev, int val)
{
int speed_mode;
#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE)
int speed = -1;
#else
int speed = SPEED_UNKNOWN;
#endif
speed_mode = (val & YTXXXX_SPEED_MODE) >> YTXXXX_SPEED_MODE_BIT;
switch (speed_mode) {
case 1:
speed = SPEED_100;
break;
case 2:
speed = SPEED_1000;
break;
default:
#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE)
speed = -1;
#else
speed = SPEED_UNKNOWN;
#endif
break;
}
phydev->speed = speed;
phydev->duplex = DUPLEX_FULL;
return 0;
}
static int yt8011_read_status(struct phy_device *phydev)
{
int ret;
int val;
int link;
int link_utp = 0;
/* UTP */
ret = ytphy_write_ext(phydev, 0x9000, 0x0);
if (ret < 0)
return ret;
val = phy_read(phydev, REG_PHY_SPEC_STATUS);
if (val < 0)
return val;
link = val & (BIT(YTXXXX_LINK_STATUS_BIT));
if (link) {
link_utp = 1;
ytxxxx_automotive_adjust_status(phydev, val);
} else {
link_utp = 0;
}
if (link_utp) {
if (phydev->link == 0)
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
netdev_info(phydev->attached_dev,
"%s, phy addr: %d, link up, media: UTP, mii reg 0x11 = 0x%x\n",
__func__, phydev->addr, (unsigned int)val);
#else
netdev_info(phydev->attached_dev,
"%s, phy addr: %d, link up, media: UTP, mii reg 0x11 = 0x%x\n",
__func__, phydev->mdio.addr,
(unsigned int)val);
#endif
phydev->link = 1;
} else {
if (phydev->link == 1)
#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE)
netdev_info(phydev->attached_dev,
"%s, phy addr: %d, link down\n",
__func__, phydev->addr);
#else
netdev_info(phydev->attached_dev,
"%s, phy addr: %d, link down\n",
__func__, phydev->mdio.addr);
#endif
phydev->link = 0;
}
if (link_utp)
ytphy_write_ext(phydev, 0x9000, 0x0);
return 0;
}
#if (KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE)
static int yt8011_ptp_hwtstamp(struct phy_device *phydev, struct ifreq *ifr)
{
struct yt8xxx_priv *priv = phydev->priv;
struct yt_ptp_private *ptp_priv = priv->ptp_priv;
struct hwtstamp_config cfg;
u16 cfg0 = 0, cfg1 = 0;
//u16 role;
//dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
if (cfg.flags) /* reserved for future extensions */
return -EINVAL;
if (cfg.tx_type < 0 || cfg.tx_type > HWTSTAMP_TX_ONESTEP_SYNC)
return -ERANGE;
ptp_priv->tx_type = cfg.tx_type;
dbg_ptp("@jie.han %s, %s, %d cfg.rx_filter: %s\n",
__FILE__, __func__, __LINE__,
cfg.rx_filter == HWTSTAMP_FILTER_PTP_V2_EVENT ? "HWTSTAMP_FILTER_PTP_V2_EVENT" : "other");
switch (cfg.rx_filter) {
case HWTSTAMP_FILTER_NONE:
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
ptp_priv->hwts_rx = 0;
ptp_priv->layer = 0;
ptp_priv->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:
ptp_priv->hwts_rx = 1;
ptp_priv->layer = PTP_CLASS_L4;
ptp_priv->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:
//YT8011 default 802.1as enable
ptp_priv->hwts_rx = 1;
ptp_priv->layer = PTP_CLASS_L2;
ptp_priv->version = PTP_CLASS_V2;
break;
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
ptp_priv->hwts_rx = 1;
ptp_priv->layer = PTP_CLASS_L4 | PTP_CLASS_L2;
ptp_priv->version = PTP_CLASS_V2;
break;
default:
return -ERANGE;
}
cfg0 |= OC_2_STEPS;
dbg_ptp("@jie.han %s, %s, %d role: %s\n",
__FILE__, __func__, __LINE__,
priv->role == MASTER ? "MASTER" : "SLAVE");
if (priv->role == MASTER)
cfg0 |= (DAC_CLK << 2);
else
cfg0 |= (ADC_CLK_AFTER_ROTATOR << 2);
/* ext reg 0x8000 bit4 BP_1588
* 1'b0 = PTP timestamp engine normal operation as default.
* 1'b1 = Bypass IEEE1588v2 related functions.
*/
cfg0 &= ~BIT(4);
/* ext reg 0x8000 bit7 EN_GATE_1588
* 1'b0 = Disable gating 1588 clock domain.
* 1'b1 = Enable gating 1588 clock domain as default.
*/
cfg0 &= ~BIT(7);
/* ext reg 0x8001 bit4 PTP_MODE_SEL
* 1'b1: PTP mode
* 1'b0: gPTP mode(802.1AS) as default
*/
if (ptp_priv->layer & PTP_CLASS_L2)
cfg1 &= ~BIT(4);
mutex_lock(&ptp_priv->mutex);
yt8011_ptp_init_paged(phydev, cfg0, cfg1, YT8011_REG_SPACE_UTP);
mutex_unlock(&ptp_priv->mutex);
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
}
static int parsing_ptp_msgtype_seqid(struct sk_buff *skb,
u8 *msgtype, u16 *seqid, int type)
{
u8 *data = skb_mac_header(skb);
unsigned int offset = 0;
u8 *msg_type;
//u16 *seq_id;
//dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
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 (skb->len + ETH_HLEN < offset + OFF_PTP_SEQUENCE_ID + sizeof(*seqid))
return 0;
if (unlikely(type & PTP_CLASS_V1)) {
if (offset + OFF_PTP_CONTROL >= skb->len)
return 0;
msg_type = data + offset + OFF_PTP_CONTROL;
} else {
if (offset >= skb->len)
return 0;
msg_type = data + offset;
}
//memcpy(msgtype, msg_type, sizeof(*msgtype));
*msgtype = (*msg_type) & 0xf;
//seq_id = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID);
//memcpy(seqid, seq_id, sizeof(*seqid));
//*seq_id = ntohs(*(u16 *)(data + offset + OFF_PTP_SEQUENCE_ID));
*seqid = ntohs(*(u16 *)(data + offset + OFF_PTP_SEQUENCE_ID));
dbg_ptp("@jie.han %s, %s, %d msgtype: %s, seq_id: %d\n",
__FILE__, __func__, __LINE__,
*msgtype == SYNC ? "SYNC" :
*msgtype == DELAY_REQ ? "DELAY_REQ" :
*msgtype == PDELAY_REQ ? "PDELAY_REQ" :
*msgtype == PDELAY_RESP ? "PDELAY_RESP" :
*msgtype == FOLLOW_UP ? "FOLLOW_UP" :
*msgtype == DELAY_RESP ? "DELAY_RESP" :
*msgtype == PDELAY_RESP_FOLLOW_UP ? "PDELAY_RESP_FOLLOW_UP" :
*msgtype == ANNOUNCE ? "ANNOUNCE" :
*msgtype == SIGNALING ? "SIGNALING" :
*msgtype == MANAGEMENT ? "MANAGEMENT" : "others", *seqid);
return 1;
}
static bool yt8011_ptp_rxtstamp(struct phy_device *phydev,
struct sk_buff *skb, int type)
{
struct skb_shared_hwtstamps *shhwtstamps;
struct yt8xxx_priv *priv = phydev->priv;
u16 sequence_id;
u32 sec = 0, nsec = 0;
u8 msg_type;
int ret;
dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
if (!priv->ptp_priv->hwts_rx)
return false;
if ((type & priv->ptp_priv->version) == 0 ||
(type & priv->ptp_priv->layer) == 0)
return false;
dbg_ptp("@jie.han %s, %s, %d call parsing_ptp_msgtype_seqid()\n",
__FILE__, __func__, __LINE__);
parsing_ptp_msgtype_seqid(skb, &msg_type, &sequence_id, type);
dbg_ptp("@jie.han %s, %s, %d after parsing_ptp_msgtype_seqid() "
"msg_type: %s, sequence_id: %d\n",
__FILE__, __func__, __LINE__,
msg_type == SYNC ? "SYNC" :
msg_type == DELAY_REQ ? "DELAY_REQ" :
msg_type == PDELAY_REQ ? "PDELAY_REQ" :
msg_type == PDELAY_RESP ? "PDELAY_RESP" :
msg_type == FOLLOW_UP ? "FOLLOW_UP" :
msg_type == DELAY_RESP ? "DELAY_RESP" :
msg_type == PDELAY_RESP_FOLLOW_UP ? "PDELAY_RESP_FOLLOW_UP" :
msg_type == ANNOUNCE ? "ANNOUNCE" :
msg_type == SIGNALING ? "SIGNALING" :
msg_type == MANAGEMENT ? "MANAGEMENT" : "other", sequence_id);
dbg_ptp("@jie.han %s, %s, %d call yt8011_ptp_get_rxtstamp_paged()\n",
__FILE__, __func__, __LINE__);
ret = yt8011_ptp_get_rxtstamp_paged(phydev, msg_type,
sequence_id, &sec, &nsec,
YT8011_REG_SPACE_UTP);
if (ret > 0) {
shhwtstamps = skb_hwtstamps(skb);
shhwtstamps->hwtstamp = ktime_set(sec, nsec);
dbg_ptp("@jie.han %s, %s, %d "
"shhwtstamps->hwtstamp: %lld then call netif_rx()\n",
__FILE__, __func__, __LINE__, shhwtstamps->hwtstamp);
netif_rx(skb);
return true;
} else
return false;
}
#define SKB_TIMESTAMP_TIMEOUT 10 /* jiffies */
static void yt8011_ptp_txtstamp(struct phy_device *phydev,
struct sk_buff *skb, int type)
{
struct yt8xxx_priv *priv = phydev->priv;
struct yt_ptp_private *ptp_priv = priv->ptp_priv;
u8 msgtype;
u16 seqid;
#if 0
dbg_ptp("@jie.han %s, %s, %d ptp_priv->tx_type: %s",
__FILE__, __func__, __LINE__,
ptp_priv->tx_type == HWTSTAMP_TX_ON ? "HWTSTAMP_TX_ON" :
ptp_priv->tx_type == HWTSTAMP_TX_ONESTEP_SYNC ? "HWTSTAMP_TX_ONESTEP_SYNC" :
ptp_priv->tx_type == HWTSTAMP_TX_OFF ? "HWTSTAMP_TX_OFF" : "others");
#endif
//parsing_ptp_msgtype_seqid(skb, &msgtype, &seqid, type);
switch (ptp_priv->tx_type) {
case HWTSTAMP_TX_ON:
dbg_ptp("@jie.han %s, %s, %d ptp_priv->tx_type: HWTSTAMP_TX_ON\n",
__FILE__, __func__, __LINE__);
dbg_ptp("@jie.han %s, %s, %d skb: %p, skb->sk: %p, "
"skb->sk->sk_flags: %#lx, tx_flags: 0x%x\n",
__FILE__, __func__, __LINE__,
skb, skb->sk, skb->sk->sk_flags, skb_shinfo(skb)->tx_flags);
if (!skb->sk) {
kfree_skb(skb);
return;
}
if (!parsing_ptp_msgtype_seqid(skb, &msgtype, &seqid, type)) {
pr_err("@jie.han %s, %s, %d parsing skb failed, drop skb.\n",
__FILE__, __func__, __LINE__);
kfree_skb(skb);
return;
}
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
memset(YT_SKB_CB(skb), 0x0, sizeof(struct yt_ptp_skb_cb));
YT_SKB_CB(skb)->timeout = jiffies + SKB_TIMESTAMP_TIMEOUT;
YT_SKB_CB(skb)->seq_id = seqid;
YT_SKB_CB(skb)->msgtype = msgtype;
//if (skb->sk)
/* increment the reference count to prevent premature release */
sock_hold(skb->sk);
dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_flags: %#lx\n",
__FILE__, __func__, __LINE__, skb->sk->sk_flags);
//dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_refcnt: %d\n",
// __FILE__, __func__, __LINE__, atomic_read(&skb->sk->sk_refcnt));
dbg_ptp("@jie.han %s, %s, %d skb->sk->sk_refcnt: %d\n",
__FILE__, __func__, __LINE__, skb->sk->sk_refcnt.refs.counter);
skb_queue_tail(&ptp_priv->tx_queue, skb);
ptp_schedule_worker(priv->ptp_priv->clock, 0);
break;
case HWTSTAMP_TX_ONESTEP_SYNC:
dbg_ptp("@jie.han %s, %s, %d ptp_priv->tx_type: HWTSTAMP_TX_ONESTEP_SYNC\n",
__FILE__, __func__, __LINE__);
//__fallthrough;
case HWTSTAMP_TX_OFF:
default:
kfree_skb(skb);
break;
}
}
static int yt8011_ptp_ts_info(struct phy_device *phydev,
struct ethtool_ts_info *info)
{
struct yt8xxx_priv *priv = phydev->priv;
info->so_timestamping =
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_RX_SOFTWARE |
SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
SOF_TIMESTAMPING_RAW_HARDWARE;
info->phc_index = ptp_clock_index(priv->ptp_priv->clock);
dbg_ptp("@jie.han %s, %s, %d info->phc_index: %d\n",
__FILE__, __func__, __LINE__, info->phc_index);
info->tx_types =
(1 << HWTSTAMP_TX_OFF) |
(1 << HWTSTAMP_TX_ON);
info->rx_filters =
(1 << HWTSTAMP_FILTER_NONE) |
(1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) |
(1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) |
(1 << HWTSTAMP_FILTER_PTP_V2_EVENT);
return 0;
}
#endif
static void yt8011_remove(struct phy_device *phydev)
{
struct yt8xxx_priv *priv = phydev->priv;
struct yt_ptp_private *ptp_priv = priv->ptp_priv;
dbg_ptp("@jie.han %s, %s, %d\n", __FILE__, __func__, __LINE__);
if (ptp_priv->clock)
ptp_clock_unregister(ptp_priv->clock);
skb_queue_purge(&ptp_priv->tx_queue);
}
#if (KERNEL_VERSION(4, 4, 302) < LINUX_VERSION_CODE)
static int __yt8011_write_page(struct phy_device *phydev, int page)
{
int ret;
ret = __ytphy_read_ext(phydev, YT801X_REG_SMI_MUX);
if (ret < 0)
return ret;
if (page == YT8011_REG_SPACE_UTP)
return __ytphy_write_ext(phydev, YT801X_REG_SMI_MUX,
ret & (~BIT(15)));
else if (page == YT8011_REG_SPACE_SDS)
return __ytphy_write_ext(phydev, YT801X_REG_SMI_MUX,
ret | BIT(15));
return 0;
}
static int __yt8011_read_page(struct phy_device *phydev)
{
int ret;
ret = __ytphy_read_ext(phydev, YT801X_REG_SMI_MUX);
if (ret < 0)
return ret;
if (ret & BIT(15))
return YT8011_REG_SPACE_SDS;
else
return YT8011_REG_SPACE_UTP;
}
static int yt8011_save_page(struct phy_device *phydev)
{
/* mutex_lock(&phydev->mdio.bus->mdio_lock); */
phy_lock_mdio_bus(phydev);
return __yt8011_read_page(phydev);
}
static int yt8011_select_page(struct phy_device *phydev, int page)
{
int ret, oldpage;
oldpage = ret = yt8011_save_page(phydev);
if (ret < 0)
return ret;
if (oldpage != page) {
ret = __yt8011_write_page(phydev, page);
if (ret < 0)
return ret;
}
return oldpage;
}
static int yt8011_restore_page(struct phy_device *phydev, int oldpage, int ret)
{
int r;
if (oldpage >= 0) {
r = __yt8011_write_page(phydev, oldpage);
/* Propagate the operation return code if the page write
* was successful.
*/
if (ret >= 0 && r < 0)
ret = r;
} else {
/* Propagate the phy page selection error code */
ret = oldpage;
}
/* mutex_unlock(&phydev->mdio.bus->mdio_lock); */
phy_unlock_mdio_bus(phydev);
return ret;
}
#endif
static int yt801x_config_intr(struct phy_device *phydev)
{
int ret;
ret = ytphy_write_ext(phydev, YT801X_REG_SMI_MUX,
YTPHY_REG_SPACE_UTP);
if (ret < 0)
return ret;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
ret = phy_write(phydev, YTPHY_UTP_INTR_REG,
YTPHY_INTR_LINK_STATUS);
return ret;
}
#if (KERNEL_VERSION(5, 10, 232) < LINUX_VERSION_CODE)
static irqreturn_t ytphy_handle_interrupt(struct phy_device *phydev)
{
int ret;
ret = phy_read(phydev, YTPHY_UTP_INTR_STATUS_REG);
if (ret > 0) {
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
else
return IRQ_NONE;
}
static irqreturn_t yt801x_handle_interrupt(struct phy_device *phydev)
{
int ret;
ret = ytphy_write_ext(phydev, YT801X_REG_SMI_MUX,
YTPHY_REG_SPACE_UTP);
if (ret < 0)
return IRQ_NONE;
ret = phy_read(phydev, YTPHY_UTP_INTR_STATUS_REG);
if (ret > 0) {
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
else
return IRQ_NONE;
}
#else
static int yt801x_ack_interrupt(struct phy_device *phydev)
{
int ret;
ret = ytphy_write_ext(phydev, YT801X_REG_SMI_MUX,
YTPHY_REG_SPACE_UTP);
if (ret < 0)
return ret;
ret = phy_read(phydev, YTPHY_UTP_INTR_STATUS_REG);
return ret < 0 ? ret : 0;
}
#endif
static struct phy_driver ytphy_drvs[] = {
{
.phy_id = PHY_ID_YT8011,
.name = "YT8011 Automotive Gigabit Ethernet",
.phy_id_mask = MOTORCOMM_PHY_ID_MASK,
.features = PHY_GBIT_FEATURES,
.config_intr = yt801x_config_intr,
#if (KERNEL_VERSION(5, 10, 232) < LINUX_VERSION_CODE)
.handle_interrupt = yt801x_handle_interrupt,
#else
.ack_interrupt = yt801x_ack_interrupt,
#endif
.probe = yt8011_probe,
#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE)
#else
.soft_reset = yt8011_soft_reset,
#endif
.config_aneg = yt8011_config_aneg,
#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE)
.aneg_done = yt8011_aneg_done,
#endif
.config_init = yt8011_config_init,
.read_status = yt8011_read_status,
#if (KERNEL_VERSION(5, 6, 0) > LINUX_VERSION_CODE)
.ts_info = yt8011_ptp_ts_info,
.hwtstamp = yt8011_ptp_hwtstamp,
.rxtstamp = yt8011_ptp_rxtstamp,
.txtstamp = yt8011_ptp_txtstamp,
#endif
.remove = yt8011_remove,
//.suspend = yt8011_suspend,
},
};
#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE)
static int ytphy_drivers_register(struct phy_driver *phy_drvs, int size)
{
int i, j;
int ret;
for (i = 0; i < size; i++) {
ret = phy_driver_register(&phy_drvs[i]);
if (ret)
goto err;
}
return 0;
err:
for (j = 0; j < i; j++)
phy_driver_unregister(&phy_drvs[j]);
return ret;
}
static void ytphy_drivers_unregister(struct phy_driver *phy_drvs, int size)
{
int i;
for (i = 0; i < size; i++)
phy_driver_unregister(&phy_drvs[i]);
}
static int __init ytphy_init(void)
{
return ytphy_drivers_register(ytphy_drvs, ARRAY_SIZE(ytphy_drvs));
}
static void __exit ytphy_exit(void)
{
pr_info(MODULE_NAME ": Module unloaded\n");
ytphy_drivers_unregister(ytphy_drvs, ARRAY_SIZE(ytphy_drvs));
}
module_init(ytphy_init);
module_exit(ytphy_exit);
#else
/* for linux 4.x ~ */
/* module_phy_driver(ytphy_drvs);*/
static int __init phy_module_init(void)
{
return phy_drivers_register(ytphy_drvs, ARRAY_SIZE(ytphy_drvs),
THIS_MODULE);
}
static void __exit phy_module_exit(void)
{
pr_info(MODULE_NAME ": Module unloaded\n");
phy_drivers_unregister(ytphy_drvs, ARRAY_SIZE(ytphy_drvs));
}
module_init(phy_module_init);
module_exit(phy_module_exit);
#endif
MODULE_DESCRIPTION("Motorcomm PHY driver");
MODULE_VERSION(YTPHY_LINUX_VERSION); /* for modinfo xxxx.ko in userspace */
MODULE_AUTHOR("Jie Han");
MODULE_LICENSE("GPL");
static struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
{ PHY_ID_YT8011, MOTORCOMM_PHY_ID_MASK },
{ }
};
MODULE_DEVICE_TABLE(mdio, motorcomm_tbl);