Commit 4e1a18ba authored by Marc Kleine-Budde's avatar Marc Kleine-Budde
Browse files

can: rockchip_canfd: add hardware timestamping support

parent a5605d61
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -292,6 +292,8 @@ static void rkcanfd_chip_start(struct rkcanfd_priv *priv)

	rkcanfd_chip_fifo_setup(priv);
	rkcanfd_timestamp_init(priv);
	rkcanfd_timestamp_start(priv);

	rkcanfd_set_bittiming(priv);

	rkcanfd_chip_interrupts_disable(priv);
@@ -315,6 +317,7 @@ static void rkcanfd_chip_stop(struct rkcanfd_priv *priv, const enum can_state st
{
	priv->can.state = state;

	rkcanfd_timestamp_stop(priv);
	__rkcanfd_chip_stop(priv, state);
}

@@ -322,6 +325,7 @@ static void rkcanfd_chip_stop_sync(struct rkcanfd_priv *priv, const enum can_sta
{
	priv->can.state = state;

	rkcanfd_timestamp_stop_sync(priv);
	__rkcanfd_chip_stop(priv, state);
}

@@ -353,6 +357,8 @@ rkcanfd_alloc_can_err_skb(struct rkcanfd_priv *priv,
	*timestamp = rkcanfd_get_timestamp(priv);

	skb = alloc_can_err_skb(priv->ndev, cf);
	if (skb)
		rkcanfd_skb_set_timestamp(priv, skb, *timestamp);

	return skb;
}
+1 −1
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ rkcanfd_ethtool_get_ethtool_stats(struct net_device *ndev,
}

static const struct ethtool_ops rkcanfd_ethtool_ops = {
	.get_ts_info = ethtool_op_get_ts_info,
	.get_ts_info = can_ethtool_op_get_ts_info_hwts,
	.get_strings = rkcanfd_ethtool_get_strings,
	.get_sset_count = rkcanfd_ethtool_get_sset_count,
	.get_ethtool_stats = rkcanfd_ethtool_get_ethtool_stats,
+1 −0
Original line number Diff line number Diff line
@@ -267,6 +267,7 @@ static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
	}

	memcpy(skb_cfd, cfd, len);
	rkcanfd_skb_set_timestamp(priv, skb, header->ts);

	err = can_rx_offload_queue_timestamp(&priv->offload, skb, header->ts);
	if (err)
+92 −2
Original line number Diff line number Diff line
@@ -4,12 +4,102 @@
//               Marc Kleine-Budde <kernel@pengutronix.de>
//

#include <linux/clocksource.h>

#include "rockchip_canfd.h"

static u64 rkcanfd_timestamp_read(const struct cyclecounter *cc)
{
	const struct rkcanfd_priv *priv = container_of(cc, struct rkcanfd_priv, cc);

	return rkcanfd_get_timestamp(priv);
}

void rkcanfd_skb_set_timestamp(const struct rkcanfd_priv *priv,
			       struct sk_buff *skb, const u32 timestamp)
{
	struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
	u64 ns;

	ns = timecounter_cyc2time(&priv->tc, timestamp);

	hwtstamps->hwtstamp = ns_to_ktime(ns);
}

static void rkcanfd_timestamp_work(struct work_struct *work)
{
	const struct delayed_work *delayed_work = to_delayed_work(work);
	struct rkcanfd_priv *priv;

	priv = container_of(delayed_work, struct rkcanfd_priv, timestamp);
	timecounter_read(&priv->tc);

	schedule_delayed_work(&priv->timestamp, priv->work_delay_jiffies);
}

void rkcanfd_timestamp_init(struct rkcanfd_priv *priv)
{
	u32 reg;
	const struct can_bittiming *dbt = &priv->can.data_bittiming;
	const struct can_bittiming *bt = &priv->can.bittiming;
	struct cyclecounter *cc = &priv->cc;
	u32 bitrate, div, reg, rate;
	u64 work_delay_ns;
	u64 max_cycles;

	reg = RKCANFD_REG_TIMESTAMP_CTRL_TIME_BASE_COUNTER_ENABLE;
	/* At the standard clock rate of 300Mhz on the rk3658, the 32
	 * bit timer overflows every 14s. This means that we have to
	 * poll it quite often to avoid missing a wrap around.
	 *
	 * Divide it down to a reasonable rate, at least twice the bit
	 * rate.
	 */
	bitrate = max(bt->bitrate, dbt->bitrate);
	div = min(DIV_ROUND_UP(priv->can.clock.freq, bitrate * 2),
		  FIELD_MAX(RKCANFD_REG_TIMESTAMP_CTRL_TIME_BASE_COUNTER_PRESCALE) + 1);

	reg = FIELD_PREP(RKCANFD_REG_TIMESTAMP_CTRL_TIME_BASE_COUNTER_PRESCALE,
			 div - 1) |
		RKCANFD_REG_TIMESTAMP_CTRL_TIME_BASE_COUNTER_ENABLE;
	rkcanfd_write(priv, RKCANFD_REG_TIMESTAMP_CTRL, reg);

	cc->read = rkcanfd_timestamp_read;
	cc->mask = CYCLECOUNTER_MASK(32);

	rate = priv->can.clock.freq / div;
	clocks_calc_mult_shift(&cc->mult, &cc->shift, rate, NSEC_PER_SEC,
			       RKCANFD_TIMESTAMP_WORK_MAX_DELAY_SEC);

	max_cycles = div_u64(ULLONG_MAX, cc->mult);
	max_cycles = min(max_cycles, cc->mask);
	work_delay_ns = clocksource_cyc2ns(max_cycles, cc->mult, cc->shift) / 3;
	priv->work_delay_jiffies = nsecs_to_jiffies(work_delay_ns);
	INIT_DELAYED_WORK(&priv->timestamp, rkcanfd_timestamp_work);

	netdev_dbg(priv->ndev, "clock=%lu.%02luMHz bitrate=%lu.%02luMBit/s div=%u rate=%lu.%02luMHz mult=%u shift=%u delay=%lus\n",
		   priv->can.clock.freq / MEGA,
		   priv->can.clock.freq % MEGA / KILO / 10,
		   bitrate / MEGA,
		   bitrate % MEGA / KILO / 100,
		   div,
		   rate / MEGA,
		   rate % MEGA / KILO / 10,
		   cc->mult, cc->shift,
		   priv->work_delay_jiffies / HZ);
}

void rkcanfd_timestamp_start(struct rkcanfd_priv *priv)
{
	timecounter_init(&priv->tc, &priv->cc, ktime_get_real_ns());

	schedule_delayed_work(&priv->timestamp, priv->work_delay_jiffies);
}

void rkcanfd_timestamp_stop(struct rkcanfd_priv *priv)
{
	cancel_delayed_work(&priv->timestamp);
}

void rkcanfd_timestamp_stop_sync(struct rkcanfd_priv *priv)
{
	cancel_delayed_work_sync(&priv->timestamp);
}
+4 −0
Original line number Diff line number Diff line
@@ -145,8 +145,10 @@ void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts,
{
	struct net_device_stats *stats = &priv->ndev->stats;
	unsigned int tx_tail;
	struct sk_buff *skb;

	tx_tail = rkcanfd_get_tx_tail(priv);
	skb = priv->can.echo_skb[tx_tail];

	/* Manual handling of CAN Bus Error counters. See
	 * rkcanfd_get_corrected_berr_counter() for detailed
@@ -155,6 +157,8 @@ void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts,
	if (priv->bec.txerr)
		priv->bec.txerr--;

	if (skb)
		rkcanfd_skb_set_timestamp(priv, skb, ts);
	stats->tx_bytes +=
		can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
							    tx_tail, ts,
Loading