net: stmmac: Add hardware supported cross-timestamp

Cross timestamping is supported on Integrated Ethernet Controller in
Intel SoC such as EHL and TGL with Always Running Timer.

The hardware cross-timestamp result is made available to
applications through the PTP_SYS_OFFSET_PRECISE ioctl which calls
stmmac_getcrosststamp().

Device time is stored in the MAC Auxiliary register. The 64-bit System
time (ART timestamp) is stored in registers that are only addressable
by using MDIO space.

Signed-off-by: Tan Tee Min <tee.min.tan@intel.com>
Co-developed-by: Wong Vee Khee <vee.khee.wong@linux.intel.com>
Signed-off-by: Wong Vee Khee <vee.khee.wong@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Tan Tee Min 2021-03-23 19:07:34 +08:00 committed by David S. Miller
parent bef32aa8e4
commit 341f67e424
9 changed files with 193 additions and 0 deletions

View file

@ -388,6 +388,8 @@ struct dma_features {
unsigned int estsel;
unsigned int fpesel;
unsigned int tbssel;
/* Numbers of Auxiliary Snapshot Inputs */
unsigned int aux_snapshot_n;
};
/* RX Buffer size must be multiple of 4/8/16 bytes */

View file

@ -8,6 +8,7 @@
#include "dwmac-intel.h"
#include "dwmac4.h"
#include "stmmac.h"
#include "stmmac_ptp.h"
#define INTEL_MGBE_ADHOC_ADDR 0x15
#define INTEL_MGBE_XPCS_ADDR 0x16
@ -240,6 +241,108 @@ static void intel_mgbe_ptp_clk_freq_config(void *npriv)
writel(gpio_value, priv->ioaddr + GMAC_GPIO_STATUS);
}
static void get_arttime(struct mii_bus *mii, int intel_adhoc_addr,
u64 *art_time)
{
u64 ns;
ns = mdiobus_read(mii, intel_adhoc_addr, PMC_ART_VALUE3);
ns <<= GMAC4_ART_TIME_SHIFT;
ns |= mdiobus_read(mii, intel_adhoc_addr, PMC_ART_VALUE2);
ns <<= GMAC4_ART_TIME_SHIFT;
ns |= mdiobus_read(mii, intel_adhoc_addr, PMC_ART_VALUE1);
ns <<= GMAC4_ART_TIME_SHIFT;
ns |= mdiobus_read(mii, intel_adhoc_addr, PMC_ART_VALUE0);
*art_time = ns;
}
static int intel_crosststamp(ktime_t *device,
struct system_counterval_t *system,
void *ctx)
{
struct intel_priv_data *intel_priv;
struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
void __iomem *ptpaddr = priv->ptpaddr;
void __iomem *ioaddr = priv->hw->pcsr;
unsigned long flags;
u64 art_time = 0;
u64 ptp_time = 0;
u32 num_snapshot;
u32 gpio_value;
u32 acr_value;
int ret;
u32 v;
int i;
if (!boot_cpu_has(X86_FEATURE_ART))
return -EOPNOTSUPP;
intel_priv = priv->plat->bsp_priv;
/* Enable Internal snapshot trigger */
acr_value = readl(ptpaddr + PTP_ACR);
acr_value &= ~PTP_ACR_MASK;
switch (priv->plat->int_snapshot_num) {
case AUX_SNAPSHOT0:
acr_value |= PTP_ACR_ATSEN0;
break;
case AUX_SNAPSHOT1:
acr_value |= PTP_ACR_ATSEN1;
break;
case AUX_SNAPSHOT2:
acr_value |= PTP_ACR_ATSEN2;
break;
case AUX_SNAPSHOT3:
acr_value |= PTP_ACR_ATSEN3;
break;
default:
return -EINVAL;
}
writel(acr_value, ptpaddr + PTP_ACR);
/* Clear FIFO */
acr_value = readl(ptpaddr + PTP_ACR);
acr_value |= PTP_ACR_ATSFC;
writel(acr_value, ptpaddr + PTP_ACR);
/* Trigger Internal snapshot signal
* Create a rising edge by just toggle the GPO1 to low
* and back to high.
*/
gpio_value = readl(ioaddr + GMAC_GPIO_STATUS);
gpio_value &= ~GMAC_GPO1;
writel(gpio_value, ioaddr + GMAC_GPIO_STATUS);
gpio_value |= GMAC_GPO1;
writel(gpio_value, ioaddr + GMAC_GPIO_STATUS);
/* Poll for time sync operation done */
ret = readl_poll_timeout(priv->ioaddr + GMAC_INT_STATUS, v,
(v & GMAC_INT_TSIE), 100, 10000);
if (ret == -ETIMEDOUT) {
pr_err("%s: Wait for time sync operation timeout\n", __func__);
return ret;
}
num_snapshot = (readl(ioaddr + GMAC_TIMESTAMP_STATUS) &
GMAC_TIMESTAMP_ATSNS_MASK) >>
GMAC_TIMESTAMP_ATSNS_SHIFT;
/* Repeat until the timestamps are from the FIFO last segment */
for (i = 0; i < num_snapshot; i++) {
spin_lock_irqsave(&priv->ptp_lock, flags);
stmmac_get_ptptime(priv, ptpaddr, &ptp_time);
*device = ns_to_ktime(ptp_time);
spin_unlock_irqrestore(&priv->ptp_lock, flags);
get_arttime(priv->mii, intel_priv->mdio_adhoc_addr, &art_time);
*system = convert_art_to_tsc(art_time);
}
return 0;
}
static void common_default_data(struct plat_stmmacenet_data *plat)
{
plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
@ -384,6 +487,11 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
plat->mdio_bus_data->phy_mask = 1 << INTEL_MGBE_ADHOC_ADDR;
plat->mdio_bus_data->phy_mask |= 1 << INTEL_MGBE_XPCS_ADDR;
plat->int_snapshot_num = AUX_SNAPSHOT1;
plat->has_crossts = true;
plat->crosststamp = intel_crosststamp;
return 0;
}

View file

@ -50,6 +50,7 @@
#define GMAC_L4_ADDR(reg) (0x904 + (reg) * 0x30)
#define GMAC_L3_ADDR0(reg) (0x910 + (reg) * 0x30)
#define GMAC_L3_ADDR1(reg) (0x914 + (reg) * 0x30)
#define GMAC_TIMESTAMP_STATUS 0x00000b20
/* RX Queues Routing */
#define GMAC_RXQCTRL_AVCPQ_MASK GENMASK(2, 0)
@ -144,6 +145,7 @@
#define GMAC_INT_PCS_PHYIS BIT(3)
#define GMAC_INT_PMT_EN BIT(4)
#define GMAC_INT_LPI_EN BIT(5)
#define GMAC_INT_TSIE BIT(12)
#define GMAC_PCS_IRQ_DEFAULT (GMAC_INT_RGSMIIS | GMAC_INT_PCS_LINK | \
GMAC_INT_PCS_ANE)
@ -260,6 +262,7 @@ enum power_event {
#define GMAC_HW_RXFIFOSIZE GENMASK(4, 0)
/* MAC HW features2 bitmap */
#define GMAC_HW_FEAT_AUXSNAPNUM GENMASK(30, 28)
#define GMAC_HW_FEAT_PPSOUTNUM GENMASK(26, 24)
#define GMAC_HW_FEAT_TXCHCNT GENMASK(21, 18)
#define GMAC_HW_FEAT_RXCHCNT GENMASK(15, 12)
@ -305,6 +308,11 @@ enum power_event {
#define GMAC_L4DP0_SHIFT 16
#define GMAC_L4SP0 GENMASK(15, 0)
/* MAC Timestamp Status */
#define GMAC_TIMESTAMP_AUXTSTRIG BIT(2)
#define GMAC_TIMESTAMP_ATSNS_MASK GENMASK(29, 25)
#define GMAC_TIMESTAMP_ATSNS_SHIFT 25
/* MTL registers */
#define MTL_OPERATION_MODE 0x00000c00
#define MTL_FRPE BIT(15)

View file

@ -412,6 +412,8 @@ static void dwmac4_get_hw_feature(void __iomem *ioaddr,
/* IEEE 1588-2002 */
dma_cap->time_stamp = 0;
/* Number of Auxiliary Snapshot Inputs */
dma_cap->aux_snapshot_n = (hw_cap & GMAC_HW_FEAT_AUXSNAPNUM) >> 28;
/* MAC HW feature3 */
hw_cap = readl(ioaddr + GMAC_HW_FEATURE3);

View file

@ -508,6 +508,7 @@ struct stmmac_hwtimestamp {
int (*adjust_systime) (void __iomem *ioaddr, u32 sec, u32 nsec,
int add_sub, int gmac4);
void (*get_systime) (void __iomem *ioaddr, u64 *systime);
void (*get_ptptime)(void __iomem *ioaddr, u64 *ptp_time);
};
#define stmmac_config_hw_tstamping(__priv, __args...) \
@ -522,6 +523,8 @@ struct stmmac_hwtimestamp {
stmmac_do_callback(__priv, ptp, adjust_systime, __args)
#define stmmac_get_systime(__priv, __args...) \
stmmac_do_void_callback(__priv, ptp, get_systime, __args)
#define stmmac_get_ptptime(__priv, __args...) \
stmmac_do_void_callback(__priv, ptp, get_ptptime, __args)
/* Helpers to manage the descriptors for chain and ring modes */
struct stmmac_mode_ops {

View file

@ -153,6 +153,16 @@ static void get_systime(void __iomem *ioaddr, u64 *systime)
*systime = ns;
}
static void get_ptptime(void __iomem *ptpaddr, u64 *ptp_time)
{
u64 ns;
ns = readl(ptpaddr + PTP_ATNR);
ns += readl(ptpaddr + PTP_ATSR) * NSEC_PER_SEC;
*ptp_time = ns;
}
const struct stmmac_hwtimestamp stmmac_ptp = {
.config_hw_tstamping = config_hw_tstamping,
.init_systime = init_systime,
@ -160,4 +170,5 @@ const struct stmmac_hwtimestamp stmmac_ptp = {
.config_addend = config_addend,
.adjust_systime = adjust_systime,
.get_systime = get_systime,
.get_ptptime = get_ptptime,
};

View file

@ -9,6 +9,7 @@
*******************************************************************************/
#include "stmmac.h"
#include "stmmac_ptp.h"
#include "dwmac4.h"
/**
* stmmac_adjust_freq
@ -165,6 +166,36 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
return ret;
}
/**
* stmmac_get_syncdevicetime
* @device: current device time
* @system: system counter value read synchronously with device time
* @ctx: context provided by timekeeping code
* Description: Read device and system clock simultaneously and return the
* corrected clock values in ns.
**/
static int stmmac_get_syncdevicetime(ktime_t *device,
struct system_counterval_t *system,
void *ctx)
{
struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
if (priv->plat->crosststamp)
return priv->plat->crosststamp(device, system, ctx);
else
return -EOPNOTSUPP;
}
static int stmmac_getcrosststamp(struct ptp_clock_info *ptp,
struct system_device_crosststamp *xtstamp)
{
struct stmmac_priv *priv =
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
return get_device_system_crosststamp(stmmac_get_syncdevicetime,
priv, NULL, xtstamp);
}
/* structure describing a PTP hardware clock */
static struct ptp_clock_info stmmac_ptp_clock_ops = {
.owner = THIS_MODULE,
@ -180,6 +211,7 @@ static struct ptp_clock_info stmmac_ptp_clock_ops = {
.gettime64 = stmmac_get_time,
.settime64 = stmmac_set_time,
.enable = stmmac_enable,
.getcrosststamp = stmmac_getcrosststamp,
};
/**

View file

@ -23,6 +23,9 @@
#define PTP_STSUR 0x10 /* System Time Seconds Update Reg */
#define PTP_STNSUR 0x14 /* System Time Nanoseconds Update Reg */
#define PTP_TAR 0x18 /* Timestamp Addend Reg */
#define PTP_ACR 0x40 /* Auxiliary Control Reg */
#define PTP_ATNR 0x48 /* Auxiliary Timestamp - Nanoseconds Reg */
#define PTP_ATSR 0x4c /* Auxiliary Timestamp - Seconds Reg */
#define PTP_STNSUR_ADDSUB_SHIFT 31
#define PTP_DIGITAL_ROLLOVER_MODE 0x3B9ACA00 /* 10e9-1 ns */
@ -64,4 +67,24 @@
#define PTP_SSIR_SSINC_MASK 0xff
#define GMAC4_PTP_SSIR_SSINC_SHIFT 16
/* Auxiliary Control defines */
#define PTP_ACR_ATSFC BIT(0) /* Auxiliary Snapshot FIFO Clear */
#define PTP_ACR_ATSEN0 BIT(4) /* Auxiliary Snapshot 0 Enable */
#define PTP_ACR_ATSEN1 BIT(5) /* Auxiliary Snapshot 1 Enable */
#define PTP_ACR_ATSEN2 BIT(6) /* Auxiliary Snapshot 2 Enable */
#define PTP_ACR_ATSEN3 BIT(7) /* Auxiliary Snapshot 3 Enable */
#define PTP_ACR_MASK GENMASK(7, 4) /* Aux Snapshot Mask */
#define PMC_ART_VALUE0 0x01 /* PMC_ART[15:0] timer value */
#define PMC_ART_VALUE1 0x02 /* PMC_ART[31:16] timer value */
#define PMC_ART_VALUE2 0x03 /* PMC_ART[47:32] timer value */
#define PMC_ART_VALUE3 0x04 /* PMC_ART[63:48] timer value */
#define GMAC4_ART_TIME_SHIFT 16 /* ART TIME 16-bits shift */
enum aux_snapshot {
AUX_SNAPSHOT0 = 0x10,
AUX_SNAPSHOT1 = 0x20,
AUX_SNAPSHOT2 = 0x40,
AUX_SNAPSHOT3 = 0x80,
};
#endif /* __STMMAC_PTP_H__ */

View file

@ -186,6 +186,8 @@ struct plat_stmmacenet_data {
void (*exit)(struct platform_device *pdev, void *priv);
struct mac_device_info *(*setup)(void *priv);
int (*clks_config)(void *priv, bool enabled);
int (*crosststamp)(ktime_t *device, struct system_counterval_t *system,
void *ctx);
void *bsp_priv;
struct clk *stmmac_clk;
struct clk *pclk;
@ -206,5 +208,7 @@ struct plat_stmmacenet_data {
u8 vlan_fail_q;
unsigned int eee_usecs_rate;
struct pci_dev *pdev;
bool has_crossts;
int int_snapshot_num;
};
#endif