mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-26 12:02:25 +00:00
The mcp251xfd chip is configured to provide a timestamp with each received and transmitted CAN frame. The timestamp is derived from the internal free-running timer, which can also be read from the TBC register via SPI. The timer is 32 bits wide and is clocked by the external oscillator (typically 20 or 40 MHz). To avoid confusion, we call this timestamp "timestamp_raw" or "ts_raw" for short. Using the timecounter framework, the "ts_raw" is converted to 64 bit nanoseconds since the epoch. This is what we call "timestamp". This is a preparation for the next patches which use the "timestamp" to work around a bug where so far only the "ts_raw" is used. Tested-by: Stefan Althöfer <Stefan.Althoefer@janztec.com> Tested-by: Thomas Kopp <thomas.kopp@microchip.com> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
65 lines
1.6 KiB
C
65 lines
1.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// mcp251xfd - Microchip MCP251xFD Family CAN controller driver
|
|
//
|
|
// Copyright (c) 2021, 2023 Pengutronix,
|
|
// Marc Kleine-Budde <kernel@pengutronix.de>
|
|
//
|
|
|
|
#include <linux/clocksource.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include "mcp251xfd.h"
|
|
|
|
static u64 mcp251xfd_timestamp_raw_read(const struct cyclecounter *cc)
|
|
{
|
|
const struct mcp251xfd_priv *priv;
|
|
u32 ts_raw = 0;
|
|
int err;
|
|
|
|
priv = container_of(cc, struct mcp251xfd_priv, cc);
|
|
err = mcp251xfd_get_timestamp_raw(priv, &ts_raw);
|
|
if (err)
|
|
netdev_err(priv->ndev,
|
|
"Error %d while reading timestamp. HW timestamps may be inaccurate.",
|
|
err);
|
|
|
|
return ts_raw;
|
|
}
|
|
|
|
static void mcp251xfd_timestamp_work(struct work_struct *work)
|
|
{
|
|
struct delayed_work *delayed_work = to_delayed_work(work);
|
|
struct mcp251xfd_priv *priv;
|
|
|
|
priv = container_of(delayed_work, struct mcp251xfd_priv, timestamp);
|
|
timecounter_read(&priv->tc);
|
|
|
|
schedule_delayed_work(&priv->timestamp,
|
|
MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ);
|
|
}
|
|
|
|
void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv)
|
|
{
|
|
struct cyclecounter *cc = &priv->cc;
|
|
|
|
cc->read = mcp251xfd_timestamp_raw_read;
|
|
cc->mask = CYCLECOUNTER_MASK(32);
|
|
cc->shift = 1;
|
|
cc->mult = clocksource_hz2mult(priv->can.clock.freq, cc->shift);
|
|
|
|
INIT_DELAYED_WORK(&priv->timestamp, mcp251xfd_timestamp_work);
|
|
}
|
|
|
|
void mcp251xfd_timestamp_start(struct mcp251xfd_priv *priv)
|
|
{
|
|
timecounter_init(&priv->tc, &priv->cc, ktime_get_real_ns());
|
|
schedule_delayed_work(&priv->timestamp,
|
|
MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ);
|
|
}
|
|
|
|
void mcp251xfd_timestamp_stop(struct mcp251xfd_priv *priv)
|
|
{
|
|
cancel_delayed_work_sync(&priv->timestamp);
|
|
}
|