1efd8d98dSMarc Kleine-Budde // SPDX-License-Identifier: GPL-2.0 2efd8d98dSMarc Kleine-Budde // 3efd8d98dSMarc Kleine-Budde // mcp251xfd - Microchip MCP251xFD Family CAN controller driver 4efd8d98dSMarc Kleine-Budde // 5efd8d98dSMarc Kleine-Budde // Copyright (c) 2021 Pengutronix, 6efd8d98dSMarc Kleine-Budde // Marc Kleine-Budde <kernel@pengutronix.de> 7efd8d98dSMarc Kleine-Budde // 8efd8d98dSMarc Kleine-Budde 9efd8d98dSMarc Kleine-Budde #include <linux/clocksource.h> 10efd8d98dSMarc Kleine-Budde #include <linux/workqueue.h> 11efd8d98dSMarc Kleine-Budde 12efd8d98dSMarc Kleine-Budde #include "mcp251xfd.h" 13efd8d98dSMarc Kleine-Budde 14efd8d98dSMarc Kleine-Budde static u64 mcp251xfd_timestamp_read(const struct cyclecounter *cc) 15efd8d98dSMarc Kleine-Budde { 16b2fcc707SMarc Kleine-Budde const struct mcp251xfd_priv *priv; 17efd8d98dSMarc Kleine-Budde u32 timestamp = 0; 18efd8d98dSMarc Kleine-Budde int err; 19efd8d98dSMarc Kleine-Budde 20efd8d98dSMarc Kleine-Budde priv = container_of(cc, struct mcp251xfd_priv, cc); 21efd8d98dSMarc Kleine-Budde err = mcp251xfd_get_timestamp(priv, ×tamp); 22efd8d98dSMarc Kleine-Budde if (err) 23efd8d98dSMarc Kleine-Budde netdev_err(priv->ndev, 24efd8d98dSMarc Kleine-Budde "Error %d while reading timestamp. HW timestamps may be inaccurate.", 25efd8d98dSMarc Kleine-Budde err); 26efd8d98dSMarc Kleine-Budde 27efd8d98dSMarc Kleine-Budde return timestamp; 28efd8d98dSMarc Kleine-Budde } 29efd8d98dSMarc Kleine-Budde 30efd8d98dSMarc Kleine-Budde static void mcp251xfd_timestamp_work(struct work_struct *work) 31efd8d98dSMarc Kleine-Budde { 32efd8d98dSMarc Kleine-Budde struct delayed_work *delayed_work = to_delayed_work(work); 33efd8d98dSMarc Kleine-Budde struct mcp251xfd_priv *priv; 34efd8d98dSMarc Kleine-Budde 35efd8d98dSMarc Kleine-Budde priv = container_of(delayed_work, struct mcp251xfd_priv, timestamp); 36efd8d98dSMarc Kleine-Budde timecounter_read(&priv->tc); 37efd8d98dSMarc Kleine-Budde 38efd8d98dSMarc Kleine-Budde schedule_delayed_work(&priv->timestamp, 39efd8d98dSMarc Kleine-Budde MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ); 40efd8d98dSMarc Kleine-Budde } 41efd8d98dSMarc Kleine-Budde 42b2fcc707SMarc Kleine-Budde void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv, 43efd8d98dSMarc Kleine-Budde struct sk_buff *skb, u32 timestamp) 44efd8d98dSMarc Kleine-Budde { 45efd8d98dSMarc Kleine-Budde struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); 46efd8d98dSMarc Kleine-Budde u64 ns; 47efd8d98dSMarc Kleine-Budde 48efd8d98dSMarc Kleine-Budde ns = timecounter_cyc2time(&priv->tc, timestamp); 49efd8d98dSMarc Kleine-Budde hwtstamps->hwtstamp = ns_to_ktime(ns); 50efd8d98dSMarc Kleine-Budde } 51efd8d98dSMarc Kleine-Budde 52efd8d98dSMarc Kleine-Budde void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv) 53efd8d98dSMarc Kleine-Budde { 54efd8d98dSMarc Kleine-Budde struct cyclecounter *cc = &priv->cc; 55efd8d98dSMarc Kleine-Budde 56efd8d98dSMarc Kleine-Budde cc->read = mcp251xfd_timestamp_read; 57efd8d98dSMarc Kleine-Budde cc->mask = CYCLECOUNTER_MASK(32); 58efd8d98dSMarc Kleine-Budde cc->shift = 1; 59efd8d98dSMarc Kleine-Budde cc->mult = clocksource_hz2mult(priv->can.clock.freq, cc->shift); 60efd8d98dSMarc Kleine-Budde 61efd8d98dSMarc Kleine-Budde timecounter_init(&priv->tc, &priv->cc, ktime_get_real_ns()); 62efd8d98dSMarc Kleine-Budde 63efd8d98dSMarc Kleine-Budde INIT_DELAYED_WORK(&priv->timestamp, mcp251xfd_timestamp_work); 64efd8d98dSMarc Kleine-Budde schedule_delayed_work(&priv->timestamp, 65efd8d98dSMarc Kleine-Budde MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ); 66efd8d98dSMarc Kleine-Budde } 67efd8d98dSMarc Kleine-Budde 68efd8d98dSMarc Kleine-Budde void mcp251xfd_timestamp_stop(struct mcp251xfd_priv *priv) 69efd8d98dSMarc Kleine-Budde { 70efd8d98dSMarc Kleine-Budde cancel_delayed_work_sync(&priv->timestamp); 71efd8d98dSMarc Kleine-Budde } 72