On 15.09.2022 01:39:43, Matej Vasilevski wrote: > This patch adds support for retrieving hardware timestamps to RX and > error CAN frames. It uses timecounter and cyclecounter structures, > because the timestamping counter width depends on the IP core integration > (it might not always be 64-bit). > For platform devices, you should specify "ts-clk" clock in device tree. > For PCI devices, the timestamping frequency is assumed to be the same > as bus frequency. Looks quite good now. Thanks for your work! Comments inline. regards, Marc > > Signed-off-by: Matej Vasilevski <matej.vasilevski@xxxxxxxxx> > --- > drivers/net/can/ctucanfd/Makefile | 2 +- > drivers/net/can/ctucanfd/ctucanfd.h | 20 ++ > drivers/net/can/ctucanfd/ctucanfd_base.c | 239 ++++++++++++++++-- > drivers/net/can/ctucanfd/ctucanfd_pci.c | 5 +- > drivers/net/can/ctucanfd/ctucanfd_platform.c | 5 +- > drivers/net/can/ctucanfd/ctucanfd_timestamp.c | 70 +++++ > 6 files changed, 318 insertions(+), 23 deletions(-) > create mode 100644 drivers/net/can/ctucanfd/ctucanfd_timestamp.c > > diff --git a/drivers/net/can/ctucanfd/Makefile b/drivers/net/can/ctucanfd/Makefile > index 8078f1f2c30f..a36e66f2cea7 100644 > --- a/drivers/net/can/ctucanfd/Makefile > +++ b/drivers/net/can/ctucanfd/Makefile > @@ -4,7 +4,7 @@ > # > > obj-$(CONFIG_CAN_CTUCANFD) := ctucanfd.o > -ctucanfd-y := ctucanfd_base.o > +ctucanfd-y := ctucanfd_base.o ctucanfd_timestamp.o > > obj-$(CONFIG_CAN_CTUCANFD_PCI) += ctucanfd_pci.o > obj-$(CONFIG_CAN_CTUCANFD_PLATFORM) += ctucanfd_platform.o > diff --git a/drivers/net/can/ctucanfd/ctucanfd.h b/drivers/net/can/ctucanfd/ctucanfd.h > index 0e9904f6a05d..b3ee583234b0 100644 > --- a/drivers/net/can/ctucanfd/ctucanfd.h > +++ b/drivers/net/can/ctucanfd/ctucanfd.h > @@ -23,6 +23,10 @@ > #include <linux/netdevice.h> > #include <linux/can/dev.h> > #include <linux/list.h> > +#include <linux/timecounter.h> > +#include <linux/workqueue.h> > + > +#define CTUCANFD_MAX_WORK_DELAY_SEC 3600U > > enum ctu_can_fd_can_registers; > > @@ -51,6 +55,15 @@ struct ctucan_priv { > u32 rxfrm_first_word; > > struct list_head peers_on_pdev; > + > + struct cyclecounter cc; > + struct timecounter tc; > + spinlock_t tc_lock; /* spinlock to guard access tc->cycle_last */ > + struct delayed_work timestamp; > + struct clk *timestamp_clk; > + unsigned long work_delay_jiffies; > + bool timestamp_enabled; > + bool timestamp_possible; > }; > > /** > @@ -78,5 +91,12 @@ int ctucan_probe_common(struct device *dev, void __iomem *addr, > > int ctucan_suspend(struct device *dev) __maybe_unused; > int ctucan_resume(struct device *dev) __maybe_unused; > +int ctucan_runtime_resume(struct device *dev) __maybe_unused; > +int ctucan_runtime_suspend(struct device *dev) __maybe_unused; These functions are exported, so they are always "used". > +u64 ctucan_read_timestamp_cc_wrapper(const struct cyclecounter *cc); > +u64 ctucan_read_timestamp_counter(struct ctucan_priv *priv); > +void ctucan_skb_set_timestamp(struct ctucan_priv *priv, struct sk_buff *skb, u64 timestamp); > +void ctucan_timestamp_init(struct ctucan_priv *priv); > +void ctucan_timestamp_stop(struct ctucan_priv *priv); > #endif /*__CTUCANFD__*/ > diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c > index 3c18d028bd8c..ba1a27c62ff1 100644 > --- a/drivers/net/can/ctucanfd/ctucanfd_base.c > +++ b/drivers/net/can/ctucanfd/ctucanfd_base.c > @@ -18,6 +18,7 @@ > ******************************************************************************/ > > #include <linux/clk.h> > +#include <linux/clocksource.h> > #include <linux/errno.h> > #include <linux/ethtool.h> > #include <linux/init.h> > @@ -25,6 +26,7 @@ > #include <linux/interrupt.h> > #include <linux/io.h> > #include <linux/kernel.h> > +#include <linux/math64.h> > #include <linux/module.h> > #include <linux/skbuff.h> > #include <linux/string.h> > @@ -148,6 +150,27 @@ static void ctucan_write_txt_buf(struct ctucan_priv *priv, enum ctu_can_fd_can_r > priv->write_reg(priv, buf_base + offset, val); > } > > +static inline u64 ctucan_concat_tstamp(u32 high, u32 low) > +{ > + return ((u64)high << 32) | ((u64)low); > +} > + > +u64 ctucan_read_timestamp_counter(struct ctucan_priv *priv) > +{ > + u32 ts_low; > + u32 ts_high; > + u32 ts_high2; > + > + ts_high = ctucan_read32(priv, CTUCANFD_TIMESTAMP_HIGH); > + ts_low = ctucan_read32(priv, CTUCANFD_TIMESTAMP_LOW); > + ts_high2 = ctucan_read32(priv, CTUCANFD_TIMESTAMP_HIGH); > + > + if (ts_high2 != ts_high) > + ts_low = priv->read_reg(priv, CTUCANFD_TIMESTAMP_LOW); > + > + return ctucan_concat_tstamp(ts_high2, ts_low) & priv->cc.mask; I think you don't need to apply the mask. The tc will take care of this. > +} > + > #define CTU_CAN_FD_TXTNF(priv) (!!FIELD_GET(REG_STATUS_TXNF, ctucan_read32(priv, CTUCANFD_STATUS))) > #define CTU_CAN_FD_ENABLED(priv) (!!FIELD_GET(REG_MODE_ENA, ctucan_read32(priv, CTUCANFD_MODE))) > > @@ -640,12 +663,16 @@ static netdev_tx_t ctucan_start_xmit(struct sk_buff *skb, struct net_device *nde > * @priv: Pointer to CTU CAN FD's private data > * @cf: Pointer to CAN frame struct > * @ffw: Previously read frame format word > + * @skb: Pointer to buffer to store timestamp > * > * Note: Frame format word must be read separately and provided in 'ffw'. > */ > -static void ctucan_read_rx_frame(struct ctucan_priv *priv, struct canfd_frame *cf, u32 ffw) > +static void ctucan_read_rx_frame(struct ctucan_priv *priv, struct canfd_frame *cf, > + u32 ffw, u64 *timestamp) > { > u32 idw; > + u32 tstamp_high; > + u32 tstamp_low; > unsigned int i; > unsigned int wc; > unsigned int len; > @@ -682,9 +709,10 @@ static void ctucan_read_rx_frame(struct ctucan_priv *priv, struct canfd_frame *c > if (unlikely(len > wc * 4)) > len = wc * 4; > > - /* Timestamp - Read and throw away */ > - ctucan_read32(priv, CTUCANFD_RX_DATA); > - ctucan_read32(priv, CTUCANFD_RX_DATA); > + /* Timestamp */ > + tstamp_low = ctucan_read32(priv, CTUCANFD_RX_DATA); > + tstamp_high = ctucan_read32(priv, CTUCANFD_RX_DATA); > + *timestamp = ctucan_concat_tstamp(tstamp_high, tstamp_low) & priv->cc.mask; same here > > /* Data */ > for (i = 0; i < len; i += 4) { > @@ -713,6 +741,7 @@ static int ctucan_rx(struct net_device *ndev) > struct net_device_stats *stats = &ndev->stats; > struct canfd_frame *cf; > struct sk_buff *skb; > + u64 timestamp; > u32 ffw; > > if (test_bit(CTUCANFD_FLAG_RX_FFW_BUFFERED, &priv->drv_flags)) { > @@ -736,7 +765,9 @@ static int ctucan_rx(struct net_device *ndev) > return 0; > } > > - ctucan_read_rx_frame(priv, cf, ffw); > + ctucan_read_rx_frame(priv, cf, ffw, ×tamp); > + if (priv->timestamp_enabled) > + ctucan_skb_set_timestamp(priv, skb, timestamp); > > stats->rx_bytes += cf->len; > stats->rx_packets++; > @@ -906,6 +937,11 @@ static void ctucan_err_interrupt(struct net_device *ndev, u32 isr) > if (skb) { > stats->rx_packets++; > stats->rx_bytes += cf->can_dlc; > + if (priv->timestamp_enabled) { > + u64 tstamp = ctucan_read_timestamp_counter(priv); > + > + ctucan_skb_set_timestamp(priv, skb, tstamp); > + } > netif_rx(skb); > } > } > @@ -951,6 +987,11 @@ static int ctucan_rx_poll(struct napi_struct *napi, int quota) > cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; > stats->rx_packets++; > stats->rx_bytes += cf->can_dlc; > + if (priv->timestamp_enabled) { > + u64 tstamp = ctucan_read_timestamp_counter(priv); > + > + ctucan_skb_set_timestamp(priv, skb, tstamp); > + } > netif_rx(skb); > } > > @@ -1200,9 +1241,9 @@ static int ctucan_open(struct net_device *ndev) > struct ctucan_priv *priv = netdev_priv(ndev); > int ret; > > - ret = pm_runtime_get_sync(priv->dev); > + ret = pm_runtime_resume_and_get(priv->dev); Note, the semantics of pm_runtime_get_sync() and pm_runtime_get_sync() changed! > if (ret < 0) { > - netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", > + netdev_err(ndev, "%s: pm_runtime_resume_and_get failed(%d)\n", > __func__, ret); > pm_runtime_put_noidle(priv->dev); ...you must not call pm_runtime_put_noidle() if pm_runtime_resume_and_get() fails. Maybe it's worth to move the runtime pm handling in a separate patch. > return ret; > @@ -1231,6 +1272,9 @@ static int ctucan_open(struct net_device *ndev) > goto err_chip_start; > } > > + if (priv->timestamp_possible) > + ctucan_timestamp_init(priv); > + > netdev_info(ndev, "ctu_can_fd device registered\n"); > napi_enable(&priv->napi); > netif_start_queue(ndev); > @@ -1263,6 +1307,8 @@ static int ctucan_close(struct net_device *ndev) > ctucan_chip_stop(ndev); > free_irq(ndev->irq, ndev); > close_candev(ndev); > + if (priv->timestamp_possible) > + ctucan_timestamp_stop(priv); > > pm_runtime_put(priv->dev); > > @@ -1282,9 +1328,9 @@ static int ctucan_get_berr_counter(const struct net_device *ndev, struct can_ber > struct ctucan_priv *priv = netdev_priv(ndev); > int ret; > > - ret = pm_runtime_get_sync(priv->dev); > + ret = pm_runtime_resume_and_get(priv->dev); ...same here > if (ret < 0) { > - netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", __func__, ret); > + netdev_err(ndev, "%s: pm_runtime_resume_and_get failed(%d)\n", __func__, ret); > pm_runtime_put_noidle(priv->dev); ..same here > return ret; > } > @@ -1295,15 +1341,83 @@ static int ctucan_get_berr_counter(const struct net_device *ndev, struct can_ber > return 0; > } > > +static int ctucan_hwtstamp_set(struct net_device *dev, struct ifreq *ifr) > +{ > + struct ctucan_priv *priv = netdev_priv(dev); > + struct hwtstamp_config cfg; > + > + if (!priv->timestamp_possible) > + return -EOPNOTSUPP; > + > + if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) > + return -EFAULT; > + > + if (cfg.flags) > + return -EINVAL; Do we have to add this check to the generic can_eth_ioctl_hwts() function? > + > + if (cfg.rx_filter == HWTSTAMP_FILTER_NONE && cfg.tx_type == HWTSTAMP_TX_OFF) { > + priv->timestamp_enabled = false; > + return 0; > + } else if (cfg.rx_filter == HWTSTAMP_FILTER_ALL && cfg.tx_type == HWTSTAMP_TX_ON) { What happens if the user only configures RX _or_ TX timestamping? > + priv->timestamp_enabled = true; > + return 0; > + } else { > + return -ERANGE; > + } nitpick: please remove else, as they are not needed after the return. > +} > + > +static int ctucan_hwtstamp_get(struct net_device *dev, struct ifreq *ifr) > +{ > + struct ctucan_priv *priv = netdev_priv(dev); > + struct hwtstamp_config cfg; > + > + if (!priv->timestamp_possible) > + return -EOPNOTSUPP; > + > + cfg.flags = 0; > + cfg.tx_type = priv->timestamp_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; > + cfg.rx_filter = priv->timestamp_enabled ? HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE; > + > + return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; nitpick: please don't use the '?' operator. > +} > + > +static int ctucan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) > +{ > + switch (cmd) { > + case SIOCSHWTSTAMP: > + return ctucan_hwtstamp_set(dev, ifr); > + case SIOCGHWTSTAMP: > + return ctucan_hwtstamp_get(dev, ifr); > + default: > + return -EOPNOTSUPP; > + } > +} > + > +static int ctucan_ethtool_get_ts_info(struct net_device *ndev, > + struct ethtool_ts_info *info) > +{ > + struct ctucan_priv *priv = netdev_priv(ndev); > + > + if (!priv->timestamp_possible) > + return ethtool_op_get_ts_info(ndev, info); > + > + can_ethtool_op_get_ts_info_hwts(ndev, info); > + info->rx_filters |= BIT(HWTSTAMP_FILTER_NONE); > + info->tx_types |= BIT(HWTSTAMP_TX_OFF); > + > + return 0; > +} > + > static const struct net_device_ops ctucan_netdev_ops = { > .ndo_open = ctucan_open, > .ndo_stop = ctucan_close, > .ndo_start_xmit = ctucan_start_xmit, > .ndo_change_mtu = can_change_mtu, > + .ndo_eth_ioctl = ctucan_ioctl, > }; > > static const struct ethtool_ops ctucan_ethtool_ops = { > - .get_ts_info = ethtool_op_get_ts_info, > + .get_ts_info = ctucan_ethtool_get_ts_info, > }; > > int ctucan_suspend(struct device *dev) > @@ -1338,12 +1452,42 @@ int ctucan_resume(struct device *dev) > } > EXPORT_SYMBOL(ctucan_resume); > > +int ctucan_runtime_suspend(struct device *dev) > +{ > + struct net_device *ndev = dev_get_drvdata(dev); > + struct ctucan_priv *priv = netdev_priv(ndev); > + > + if (!IS_ERR_OR_NULL(priv->timestamp_clk)) > + clk_disable_unprepare(priv->timestamp_clk); The common clock framework handles NULL pointers, so remove the IS_ERR_OR_NULL(). > + return 0; > +} > +EXPORT_SYMBOL(ctucan_runtime_suspend); > + > +int ctucan_runtime_resume(struct device *dev) > +{ > + struct net_device *ndev = dev_get_drvdata(dev); > + struct ctucan_priv *priv = netdev_priv(ndev); > + int ret; > + > + if (!IS_ERR_OR_NULL(priv->timestamp_clk)) { ...same here > + ret = clk_prepare_enable(priv->timestamp_clk); > + if (ret) { > + dev_err(dev, "Cannot enable timestamping clock: %d\n", ret); > + return ret; > + } > + } > + return 0; > +} > +EXPORT_SYMBOL(ctucan_runtime_resume); > + > int ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigned int ntxbufs, > unsigned long can_clk_rate, int pm_enable_call, > void (*set_drvdata_fnc)(struct device *dev, struct net_device *ndev)) > { > struct ctucan_priv *priv; > struct net_device *ndev; > + u32 timestamp_freq = 0; nitpick: name this _rate, similar to can_clk_rate. > + u32 timestamp_bit_size = 0; > int ret; > > /* Create a CAN device instance */ > @@ -1373,6 +1517,7 @@ int ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigne > | CAN_CTRLMODE_FD_NON_ISO > | CAN_CTRLMODE_ONE_SHOT; > priv->mem_base = addr; > + priv->timestamp_possible = true; > > /* Get IRQ for the device */ > ndev->irq = irq; > @@ -1386,27 +1531,39 @@ int ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigne > > /* Getting the can_clk info */ > if (!can_clk_rate) { > - priv->can_clk = devm_clk_get(dev, NULL); > + priv->can_clk = devm_clk_get_optional(dev, "core-clk"); > + if (!priv->can_clk) > + /* For compatibility with (older) device trees without clock-names */ > + priv->can_clk = devm_clk_get(dev, NULL); > if (IS_ERR(priv->can_clk)) { > - dev_err(dev, "Device clock not found.\n"); > + dev_err(dev, "Device clock not found: %pe.\n", priv->can_clk); > ret = PTR_ERR(priv->can_clk); > goto err_free; > } > can_clk_rate = clk_get_rate(priv->can_clk); > } > > + /* If it's a platform device - get the timestamping clock */ > + if (pm_enable_call) { The variable pm_enable_call feels wrong. As PCI device automatically do an automatic pm_runtime_enable(), better move the pm_runtime_enable() to the platform device driver. But that's a separate patch. Please don't wire more functionality to pm_enable_call. What about: - if ctucan_probe_common() is called with can_clk_rate set, use it as the timestamp_freq, too. - otherwise get the rate from the ts-clk, or core clock > + priv->timestamp_clk = devm_clk_get(dev, "ts-clk"); > + if (IS_ERR(priv->timestamp_clk)) { > + /* Take the core clock instead */ > + dev_info(dev, "Failed to get ts clk\nl"); ^^^ trailing 'l' > + priv->timestamp_clk = priv->can_clk; > + } > + clk_prepare_enable(priv->timestamp_clk); Later in this function there is a call to pm_runtime_put(dev). Without runtime PM the clock will not turned off. So you rely on runtime PM, please adjust your Kconfig accordingly. > + timestamp_freq = clk_get_rate(priv->timestamp_clk); > + } else { > + /* PCI device: assume tstamp freq is equal to bus clk rate */ > + timestamp_freq = can_clk_rate; > + } > + > priv->write_reg = ctucan_write32_le; > priv->read_reg = ctucan_read32_le; > > + pm_runtime_get_noresume(dev); I think you're missing a pm_runtime_set_active() call here. > if (pm_enable_call) > pm_runtime_enable(dev); > - ret = pm_runtime_get_sync(dev); > - if (ret < 0) { > - netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", > - __func__, ret); > - pm_runtime_put_noidle(priv->dev); > - goto err_pmdisable; > - } > > /* Check for big-endianity and set according IO-accessors */ > if ((ctucan_read32(priv, CTUCANFD_DEVICE_ID) & 0xFFFF) != CTUCANFD_ID) { > @@ -1425,6 +1582,49 @@ int ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigne > > priv->can.clock.freq = can_clk_rate; > > + /* Obtain timestamping counter bit size */ > + timestamp_bit_size = FIELD_GET(REG_ERR_CAPT_TS_BITS, > + ctucan_read32(priv, CTUCANFD_ERR_CAPT)); > + > + /* The register value is actually bit_size - 1 */ > + if (timestamp_bit_size) { > + timestamp_bit_size += 1; > + } else { > + /* For 2.x versions of the IP core, we will assume 64-bit counter > + * if there was a 0 in the register. > + */ > + u32 version_reg = ctucan_read32(priv, CTUCANFD_DEVICE_ID); > + u32 major = FIELD_GET(REG_DEVICE_ID_VER_MAJOR, version_reg); > + > + if (major == 2) > + timestamp_bit_size = 64; > + else > + priv->timestamp_possible = false; > + } > + > + /* Setup conversion constants and work delay */ > + priv->cc.mask = CYCLECOUNTER_MASK(timestamp_bit_size); > + if (priv->timestamp_possible) { > + u64 max_cycles; > + u64 work_delay_ns; > + u32 maxsec = min_t(u32, CTUCANFD_MAX_WORK_DELAY_SEC, > + div_u64(priv->cc.mask, timestamp_freq)); > + > + priv->cc.read = ctucan_read_timestamp_cc_wrapper; > + clocks_calc_mult_shift(&priv->cc.mult, &priv->cc.shift, > + timestamp_freq, NSEC_PER_SEC, maxsec); > + > + /* shortened copy of clocks_calc_max_nsecs() */ > + max_cycles = div_u64(ULLONG_MAX, priv->cc.mult); > + max_cycles = min(max_cycles, priv->cc.mask); > + work_delay_ns = clocksource_cyc2ns(max_cycles, priv->cc.mult, > + priv->cc.shift) >> 1; > + priv->work_delay_jiffies = nsecs_to_jiffies(work_delay_ns); > + > + if (priv->work_delay_jiffies == 0) > + priv->timestamp_possible = false; > + } > + > netif_napi_add(ndev, &priv->napi, ctucan_rx_poll, NAPI_POLL_WEIGHT); > > ret = register_candev(ndev); > @@ -1442,7 +1642,6 @@ int ctucan_probe_common(struct device *dev, void __iomem *addr, int irq, unsigne > > err_deviceoff: > pm_runtime_put(priv->dev); > -err_pmdisable: > if (pm_enable_call) > pm_runtime_disable(dev); > err_free: > diff --git a/drivers/net/can/ctucanfd/ctucanfd_pci.c b/drivers/net/can/ctucanfd/ctucanfd_pci.c > index 8f2956a8ae43..bdb7cf789776 100644 > --- a/drivers/net/can/ctucanfd/ctucanfd_pci.c > +++ b/drivers/net/can/ctucanfd/ctucanfd_pci.c > @@ -271,7 +271,10 @@ static void ctucan_pci_remove(struct pci_dev *pdev) > kfree(bdata); > } > > -static SIMPLE_DEV_PM_OPS(ctucan_pci_pm_ops, ctucan_suspend, ctucan_resume); > +static const struct dev_pm_ops ctucan_pci_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(ctucan_suspend, ctucan_resume) > + SET_RUNTIME_PM_OPS(ctucan_runtime_suspend, ctucan_runtime_resume, NULL) > +}; > > static const struct pci_device_id ctucan_pci_tbl[] = { > {PCI_DEVICE_DATA(TEDIA, CTUCAN_VER21, > diff --git a/drivers/net/can/ctucanfd/ctucanfd_platform.c b/drivers/net/can/ctucanfd/ctucanfd_platform.c > index 89d54c2151e1..1b2052aec2ab 100644 > --- a/drivers/net/can/ctucanfd/ctucanfd_platform.c > +++ b/drivers/net/can/ctucanfd/ctucanfd_platform.c > @@ -104,7 +104,10 @@ static int ctucan_platform_remove(struct platform_device *pdev) > return 0; > } > > -static SIMPLE_DEV_PM_OPS(ctucan_platform_pm_ops, ctucan_suspend, ctucan_resume); > +static const struct dev_pm_ops ctucan_platform_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(ctucan_suspend, ctucan_resume) > + SET_RUNTIME_PM_OPS(ctucan_runtime_suspend, ctucan_runtime_resume, NULL) > +}; > > /* Match table for OF platform binding */ > static const struct of_device_id ctucan_of_match[] = { > diff --git a/drivers/net/can/ctucanfd/ctucanfd_timestamp.c b/drivers/net/can/ctucanfd/ctucanfd_timestamp.c > new file mode 100644 > index 000000000000..77e461d1962d > --- /dev/null > +++ b/drivers/net/can/ctucanfd/ctucanfd_timestamp.c > @@ -0,0 +1,70 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/******************************************************************************* > + * > + * CTU CAN FD IP Core > + * > + * Copyright (C) 2022 Matej Vasilevski <matej.vasilevski@xxxxxxxxx> FEE CTU > + * > + * Project advisors: > + * Jiri Novak <jnovak@xxxxxxxxxxx> > + * Pavel Pisa <pisa@xxxxxxxxxxxxxxxx> > + * > + * Department of Measurement (http://meas.fel.cvut.cz/) > + * Faculty of Electrical Engineering (http://www.fel.cvut.cz) > + * Czech Technical University (http://www.cvut.cz/) > + ******************************************************************************/ > + > +#include "linux/spinlock.h" > +#include <linux/bitops.h> > +#include <linux/clocksource.h> > +#include <linux/math64.h> > +#include <linux/timecounter.h> > +#include <linux/workqueue.h> > + > +#include "ctucanfd.h" > +#include "ctucanfd_kregs.h" > + > +u64 ctucan_read_timestamp_cc_wrapper(const struct cyclecounter *cc) > +{ > + struct ctucan_priv *priv; Please add a "lockdep_assert_held(&priv->tc_lock);" and compile your code with lockdep enabled. > + > + priv = container_of(cc, struct ctucan_priv, cc); > + return ctucan_read_timestamp_counter(priv); > +} > + > +static void ctucan_timestamp_work(struct work_struct *work) > +{ > + struct delayed_work *delayed_work = to_delayed_work(work); > + struct ctucan_priv *priv = container_of(delayed_work, struct ctucan_priv, timestamp); > + unsigned long flags; > + > + spin_lock_irqsave(&priv->tc_lock, flags); > + timecounter_read(&priv->tc); > + spin_unlock_irqrestore(&priv->tc_lock, flags); > + schedule_delayed_work(&priv->timestamp, priv->work_delay_jiffies); > +} > + > +void ctucan_skb_set_timestamp(struct ctucan_priv *priv, struct sk_buff *skb, u64 timestamp) > +{ > + struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); > + u64 ns; > + unsigned long flags; > + > + spin_lock_irqsave(&priv->tc_lock, flags); > + ns = timecounter_cyc2time(&priv->tc, timestamp); > + spin_unlock_irqrestore(&priv->tc_lock, flags); > + hwtstamps->hwtstamp = ns_to_ktime(ns); > +} > + > +void ctucan_timestamp_init(struct ctucan_priv *priv) > +{ > + spin_lock_init(&priv->tc_lock); please lock ->tc_lock during timecounter_init(), too. This will keep the lockdep_assert_held() happy. > + timecounter_init(&priv->tc, &priv->cc, ktime_get_real_ns()); > + INIT_DELAYED_WORK(&priv->timestamp, ctucan_timestamp_work); > + schedule_delayed_work(&priv->timestamp, priv->work_delay_jiffies); > +} > + > +void ctucan_timestamp_stop(struct ctucan_priv *priv) > +{ > + cancel_delayed_work_sync(&priv->timestamp); > +} > -- > 2.25.1 > > -- Pengutronix e.K. | Marc Kleine-Budde | Embedded Linux | https://www.pengutronix.de | Vertretung West/Dortmund | Phone: +49-231-2826-924 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
Attachment:
signature.asc
Description: PGP signature