mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-04 01:20:01 -04:00
Merge branch 'support-external-snapshots-on-dwmac1000'
Maxime Chevallier says: ==================== Support external snapshots on dwmac1000 The main change since v3 is the move of the fifo flush wait in the ptp_clock_info enable() function within the mutex that protects the ptp registers. Thanks Jakub and Paolo for spotting this. This series also aggregates Daniel's reviews, except for the patch 4 which was modified since then. This series is another take on the previous work [1] done by Alexis Lothoré, that fixes the support for external snapshots timestamping in GMAC3-based devices. Details on why this is needed are mentionned on the cover [2] from V1. [1]: https://lore.kernel.org/netdev/20230616100409.164583-1-alexis.lothore@bootlin.com/ [2]: https://lore.kernel.org/netdev/20241029115419.1160201-1-maxime.chevallier@bootlin.com/ Link to V1: https://lore.kernel.org/netdev/20241029115419.1160201-1-maxime.chevallier@bootlin.com/ Link to V2: https://lore.kernel.org/netdev/20241104170251.2202270-1-maxime.chevallier@bootlin.com/ Link to V3: https://lore.kernel.org/netdev/20241106090331.56519-1-maxime.chevallier@bootlin.com/ ==================== Link: https://patch.msgid.link/20241112170658.2388529-1-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -549,8 +549,12 @@ extern const struct stmmac_desc_ops ndesc_ops;
|
||||
struct mac_device_info;
|
||||
|
||||
extern const struct stmmac_hwtimestamp stmmac_ptp;
|
||||
extern const struct stmmac_hwtimestamp dwmac1000_ptp;
|
||||
extern const struct stmmac_mode_ops dwmac4_ring_mode_ops;
|
||||
|
||||
extern const struct ptp_clock_info stmmac_ptp_clock_ops;
|
||||
extern const struct ptp_clock_info dwmac1000_ptp_clock_ops;
|
||||
|
||||
struct mac_link {
|
||||
u32 caps;
|
||||
u32 speed_mask;
|
||||
|
||||
@@ -485,6 +485,7 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
|
||||
plat_dat->pcs_init = socfpga_dwmac_pcs_init;
|
||||
plat_dat->pcs_exit = socfpga_dwmac_pcs_exit;
|
||||
plat_dat->select_pcs = socfpga_dwmac_select_pcs;
|
||||
plat_dat->has_gmac = true;
|
||||
|
||||
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
||||
if (ret)
|
||||
|
||||
@@ -329,5 +329,17 @@ enum rtc_control {
|
||||
#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208
|
||||
#define GMAC_EXTHASH_BASE 0x500
|
||||
|
||||
/* PTP and timestamping registers */
|
||||
|
||||
#define GMAC3_X_ATSNS GENMASK(19, 16)
|
||||
#define GMAC3_X_ATSNS_SHIFT 16
|
||||
|
||||
#define GMAC_PTP_TCR_ATSFC BIT(24)
|
||||
#define GMAC_PTP_TCR_ATSEN0 BIT(25)
|
||||
|
||||
#define GMAC3_X_TIMESTAMP_STATUS 0x28
|
||||
#define GMAC_PTP_ATNR 0x30
|
||||
#define GMAC_PTP_ATSR 0x34
|
||||
|
||||
extern const struct stmmac_dma_ops dwmac1000_dma_ops;
|
||||
#endif /* __DWMAC1000_H__ */
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/io.h>
|
||||
#include "stmmac.h"
|
||||
#include "stmmac_pcs.h"
|
||||
#include "stmmac_ptp.h"
|
||||
#include "dwmac1000.h"
|
||||
|
||||
static void dwmac1000_core_init(struct mac_device_info *hw,
|
||||
@@ -551,3 +552,103 @@ int dwmac1000_setup(struct stmmac_priv *priv)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* DWMAC 1000 HW Timestaming ops */
|
||||
|
||||
void dwmac1000_get_ptptime(void __iomem *ptpaddr, u64 *ptp_time)
|
||||
{
|
||||
u64 ns;
|
||||
|
||||
ns = readl(ptpaddr + GMAC_PTP_ATNR);
|
||||
ns += readl(ptpaddr + GMAC_PTP_ATSR) * NSEC_PER_SEC;
|
||||
|
||||
*ptp_time = ns;
|
||||
}
|
||||
|
||||
void dwmac1000_timestamp_interrupt(struct stmmac_priv *priv)
|
||||
{
|
||||
struct ptp_clock_event event;
|
||||
u32 ts_status, num_snapshot;
|
||||
unsigned long flags;
|
||||
u64 ptp_time;
|
||||
int i;
|
||||
|
||||
/* Clears the timestamp interrupt */
|
||||
ts_status = readl(priv->ptpaddr + GMAC3_X_TIMESTAMP_STATUS);
|
||||
|
||||
if (!(priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN))
|
||||
return;
|
||||
|
||||
num_snapshot = (ts_status & GMAC3_X_ATSNS) >> GMAC3_X_ATSNS_SHIFT;
|
||||
|
||||
for (i = 0; i < num_snapshot; i++) {
|
||||
read_lock_irqsave(&priv->ptp_lock, flags);
|
||||
stmmac_get_ptptime(priv, priv->ptpaddr, &ptp_time);
|
||||
read_unlock_irqrestore(&priv->ptp_lock, flags);
|
||||
|
||||
event.type = PTP_CLOCK_EXTTS;
|
||||
event.index = 0;
|
||||
event.timestamp = ptp_time;
|
||||
ptp_clock_event(priv->ptp_clock, &event);
|
||||
}
|
||||
}
|
||||
|
||||
/* DWMAC 1000 ptp_clock_info ops */
|
||||
|
||||
static void dwmac1000_timestamp_interrupt_cfg(struct stmmac_priv *priv, bool en)
|
||||
{
|
||||
void __iomem *ioaddr = priv->ioaddr;
|
||||
|
||||
u32 intr_mask = readl(ioaddr + GMAC_INT_MASK);
|
||||
|
||||
if (en)
|
||||
intr_mask &= ~GMAC_INT_DISABLE_TIMESTAMP;
|
||||
else
|
||||
intr_mask |= GMAC_INT_DISABLE_TIMESTAMP;
|
||||
|
||||
writel(intr_mask, ioaddr + GMAC_INT_MASK);
|
||||
}
|
||||
|
||||
int dwmac1000_ptp_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *rq, int on)
|
||||
{
|
||||
struct stmmac_priv *priv =
|
||||
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
|
||||
void __iomem *ptpaddr = priv->ptpaddr;
|
||||
int ret = -EOPNOTSUPP;
|
||||
u32 tcr_val;
|
||||
|
||||
switch (rq->type) {
|
||||
case PTP_CLK_REQ_EXTTS:
|
||||
mutex_lock(&priv->aux_ts_lock);
|
||||
tcr_val = readl(ptpaddr + PTP_TCR);
|
||||
|
||||
if (on) {
|
||||
tcr_val |= GMAC_PTP_TCR_ATSEN0;
|
||||
tcr_val |= GMAC_PTP_TCR_ATSFC;
|
||||
priv->plat->flags |= STMMAC_FLAG_EXT_SNAPSHOT_EN;
|
||||
} else {
|
||||
tcr_val &= ~GMAC_PTP_TCR_ATSEN0;
|
||||
priv->plat->flags &= ~STMMAC_FLAG_EXT_SNAPSHOT_EN;
|
||||
}
|
||||
|
||||
netdev_dbg(priv->dev, "Auxiliary Snapshot %s.\n",
|
||||
on ? "enabled" : "disabled");
|
||||
writel(tcr_val, ptpaddr + PTP_TCR);
|
||||
|
||||
/* wait for auxts fifo clear to finish */
|
||||
ret = readl_poll_timeout(ptpaddr + PTP_TCR, tcr_val,
|
||||
!(tcr_val & GMAC_PTP_TCR_ATSFC),
|
||||
10, 10000);
|
||||
|
||||
mutex_unlock(&priv->aux_ts_lock);
|
||||
|
||||
dwmac1000_timestamp_interrupt_cfg(priv, on);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ static const struct stmmac_hwif_entry {
|
||||
const void *dma;
|
||||
const void *mac;
|
||||
const void *hwtimestamp;
|
||||
const void *ptp;
|
||||
const void *mode;
|
||||
const void *tc;
|
||||
const void *mmc;
|
||||
@@ -133,7 +134,8 @@ static const struct stmmac_hwif_entry {
|
||||
.desc = NULL,
|
||||
.dma = &dwmac100_dma_ops,
|
||||
.mac = &dwmac100_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.hwtimestamp = &dwmac1000_ptp,
|
||||
.ptp = &dwmac1000_ptp_clock_ops,
|
||||
.mode = NULL,
|
||||
.tc = NULL,
|
||||
.mmc = &dwmac_mmc_ops,
|
||||
@@ -151,7 +153,8 @@ static const struct stmmac_hwif_entry {
|
||||
.desc = NULL,
|
||||
.dma = &dwmac1000_dma_ops,
|
||||
.mac = &dwmac1000_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.hwtimestamp = &dwmac1000_ptp,
|
||||
.ptp = &dwmac1000_ptp_clock_ops,
|
||||
.mode = NULL,
|
||||
.tc = NULL,
|
||||
.mmc = &dwmac_mmc_ops,
|
||||
@@ -171,6 +174,7 @@ static const struct stmmac_hwif_entry {
|
||||
.dma = &dwmac4_dma_ops,
|
||||
.mac = &dwmac4_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.ptp = &stmmac_ptp_clock_ops,
|
||||
.mode = NULL,
|
||||
.tc = &dwmac4_tc_ops,
|
||||
.mmc = &dwmac_mmc_ops,
|
||||
@@ -192,6 +196,7 @@ static const struct stmmac_hwif_entry {
|
||||
.dma = &dwmac4_dma_ops,
|
||||
.mac = &dwmac410_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.ptp = &stmmac_ptp_clock_ops,
|
||||
.mode = &dwmac4_ring_mode_ops,
|
||||
.tc = &dwmac510_tc_ops,
|
||||
.mmc = &dwmac_mmc_ops,
|
||||
@@ -213,6 +218,7 @@ static const struct stmmac_hwif_entry {
|
||||
.dma = &dwmac410_dma_ops,
|
||||
.mac = &dwmac410_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.ptp = &stmmac_ptp_clock_ops,
|
||||
.mode = &dwmac4_ring_mode_ops,
|
||||
.tc = &dwmac510_tc_ops,
|
||||
.mmc = &dwmac_mmc_ops,
|
||||
@@ -234,6 +240,7 @@ static const struct stmmac_hwif_entry {
|
||||
.dma = &dwmac410_dma_ops,
|
||||
.mac = &dwmac510_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.ptp = &stmmac_ptp_clock_ops,
|
||||
.mode = &dwmac4_ring_mode_ops,
|
||||
.tc = &dwmac510_tc_ops,
|
||||
.mmc = &dwmac_mmc_ops,
|
||||
@@ -256,6 +263,7 @@ static const struct stmmac_hwif_entry {
|
||||
.dma = &dwxgmac210_dma_ops,
|
||||
.mac = &dwxgmac210_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.ptp = &stmmac_ptp_clock_ops,
|
||||
.mode = NULL,
|
||||
.tc = &dwxgmac_tc_ops,
|
||||
.mmc = &dwxgmac_mmc_ops,
|
||||
@@ -278,6 +286,7 @@ static const struct stmmac_hwif_entry {
|
||||
.dma = &dwxgmac210_dma_ops,
|
||||
.mac = &dwxlgmac2_ops,
|
||||
.hwtimestamp = &stmmac_ptp,
|
||||
.ptp = &stmmac_ptp_clock_ops,
|
||||
.mode = NULL,
|
||||
.tc = &dwxgmac_tc_ops,
|
||||
.mmc = &dwxgmac_mmc_ops,
|
||||
@@ -362,6 +371,8 @@ int stmmac_hwif_init(struct stmmac_priv *priv)
|
||||
priv->fpe_cfg.reg = entry->regs.fpe_reg;
|
||||
priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
|
||||
priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
|
||||
memcpy(&priv->ptp_clock_ops, entry->ptp,
|
||||
sizeof(struct ptp_clock_info));
|
||||
if (entry->est)
|
||||
priv->estaddr = priv->ioaddr + entry->regs.est_off;
|
||||
|
||||
|
||||
@@ -18,9 +18,22 @@
|
||||
#include "dwmac4.h"
|
||||
#include "stmmac.h"
|
||||
|
||||
#define STMMAC_HWTS_CFG_MASK (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | \
|
||||
PTP_TCR_TSINIT | PTP_TCR_TSUPDT | \
|
||||
PTP_TCR_TSCTRLSSR | PTP_TCR_SNAPTYPSEL_1 | \
|
||||
PTP_TCR_TSIPV4ENA | PTP_TCR_TSIPV6ENA | \
|
||||
PTP_TCR_TSEVNTENA | PTP_TCR_TSMSTRENA | \
|
||||
PTP_TCR_TSVER2ENA | PTP_TCR_TSIPENA | \
|
||||
PTP_TCR_TSTRIG | PTP_TCR_TSENALL)
|
||||
|
||||
static void config_hw_tstamping(void __iomem *ioaddr, u32 data)
|
||||
{
|
||||
writel(data, ioaddr + PTP_TCR);
|
||||
u32 regval = readl(ioaddr + PTP_TCR);
|
||||
|
||||
regval &= ~STMMAC_HWTS_CFG_MASK;
|
||||
regval |= data;
|
||||
|
||||
writel(regval, ioaddr + PTP_TCR);
|
||||
}
|
||||
|
||||
static void config_sub_second_increment(void __iomem *ioaddr,
|
||||
@@ -269,3 +282,14 @@ const struct stmmac_hwtimestamp stmmac_ptp = {
|
||||
.timestamp_interrupt = timestamp_interrupt,
|
||||
.hwtstamp_correct_latency = hwtstamp_correct_latency,
|
||||
};
|
||||
|
||||
const struct stmmac_hwtimestamp dwmac1000_ptp = {
|
||||
.config_hw_tstamping = config_hw_tstamping,
|
||||
.init_systime = init_systime,
|
||||
.config_sub_second_increment = config_sub_second_increment,
|
||||
.config_addend = config_addend,
|
||||
.adjust_systime = adjust_systime,
|
||||
.get_systime = get_systime,
|
||||
.get_ptptime = dwmac1000_get_ptptime,
|
||||
.timestamp_interrupt = dwmac1000_timestamp_interrupt,
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*******************************************************************************/
|
||||
#include "stmmac.h"
|
||||
#include "stmmac_ptp.h"
|
||||
#include "dwmac4.h"
|
||||
|
||||
/**
|
||||
* stmmac_adjust_freq
|
||||
@@ -265,7 +264,7 @@ static int stmmac_getcrosststamp(struct ptp_clock_info *ptp,
|
||||
}
|
||||
|
||||
/* structure describing a PTP hardware clock */
|
||||
static struct ptp_clock_info stmmac_ptp_clock_ops = {
|
||||
const struct ptp_clock_info stmmac_ptp_clock_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "stmmac ptp",
|
||||
.max_adj = 62500000,
|
||||
@@ -282,6 +281,24 @@ static struct ptp_clock_info stmmac_ptp_clock_ops = {
|
||||
.getcrosststamp = stmmac_getcrosststamp,
|
||||
};
|
||||
|
||||
/* structure describing a PTP hardware clock */
|
||||
const struct ptp_clock_info dwmac1000_ptp_clock_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "stmmac ptp",
|
||||
.max_adj = 62500000,
|
||||
.n_alarm = 0,
|
||||
.n_ext_ts = 1,
|
||||
.n_per_out = 0,
|
||||
.n_pins = 0,
|
||||
.pps = 0,
|
||||
.adjfine = stmmac_adjust_freq,
|
||||
.adjtime = stmmac_adjust_time,
|
||||
.gettime64 = stmmac_get_time,
|
||||
.settime64 = stmmac_set_time,
|
||||
.enable = dwmac1000_ptp_enable,
|
||||
.getcrosststamp = stmmac_getcrosststamp,
|
||||
};
|
||||
|
||||
/**
|
||||
* stmmac_ptp_register
|
||||
* @priv: driver private structure
|
||||
@@ -298,20 +315,25 @@ void stmmac_ptp_register(struct stmmac_priv *priv)
|
||||
priv->pps[i].available = true;
|
||||
}
|
||||
|
||||
if (priv->plat->ptp_max_adj)
|
||||
stmmac_ptp_clock_ops.max_adj = priv->plat->ptp_max_adj;
|
||||
|
||||
/* Calculate the clock domain crossing (CDC) error if necessary */
|
||||
priv->plat->cdc_error_adj = 0;
|
||||
if (priv->plat->has_gmac4 && priv->plat->clk_ptp_rate)
|
||||
priv->plat->cdc_error_adj = (2 * NSEC_PER_SEC) / priv->plat->clk_ptp_rate;
|
||||
|
||||
stmmac_ptp_clock_ops.n_per_out = priv->dma_cap.pps_out_num;
|
||||
stmmac_ptp_clock_ops.n_ext_ts = priv->dma_cap.aux_snapshot_n;
|
||||
/* Update the ptp clock parameters based on feature discovery, when
|
||||
* available
|
||||
*/
|
||||
if (priv->dma_cap.pps_out_num)
|
||||
priv->ptp_clock_ops.n_per_out = priv->dma_cap.pps_out_num;
|
||||
|
||||
if (priv->dma_cap.aux_snapshot_n)
|
||||
priv->ptp_clock_ops.n_ext_ts = priv->dma_cap.aux_snapshot_n;
|
||||
|
||||
if (priv->plat->ptp_max_adj)
|
||||
priv->ptp_clock_ops.max_adj = priv->plat->ptp_max_adj;
|
||||
|
||||
rwlock_init(&priv->ptp_lock);
|
||||
mutex_init(&priv->aux_ts_lock);
|
||||
priv->ptp_clock_ops = stmmac_ptp_clock_ops;
|
||||
|
||||
priv->ptp_clock = ptp_clock_register(&priv->ptp_clock_ops,
|
||||
priv->device);
|
||||
|
||||
@@ -94,4 +94,14 @@ enum aux_snapshot {
|
||||
AUX_SNAPSHOT3 = 0x80,
|
||||
};
|
||||
|
||||
struct ptp_clock_info;
|
||||
struct ptp_clock_request;
|
||||
struct stmmac_priv;
|
||||
|
||||
int dwmac1000_ptp_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *rq, int on);
|
||||
|
||||
void dwmac1000_get_ptptime(void __iomem *ptpaddr, u64 *ptp_time);
|
||||
void dwmac1000_timestamp_interrupt(struct stmmac_priv *priv);
|
||||
|
||||
#endif /* __STMMAC_PTP_H__ */
|
||||
|
||||
Reference in New Issue
Block a user