Commit 31d7c17e authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files


Pull in a dependency for Vladimir's work on more precise
packet time stamping.

Mark Brown says:

====================
spi: Add a PTP API

For detailed timestamping of operations.
====================

Signed-off-by: default avatarJakub Kicinski <jakub.kicinski@netronome.com>
parents bacb7e18 79591b7d
Loading
Loading
Loading
Loading
+127 −0
Original line number Diff line number Diff line
@@ -1171,6 +1171,11 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
		spi_statistics_add_transfer_stats(statm, xfer, ctlr);
		spi_statistics_add_transfer_stats(stats, xfer, ctlr);

		if (!ctlr->ptp_sts_supported) {
			xfer->ptp_sts_word_pre = 0;
			ptp_read_system_prets(xfer->ptp_sts);
		}

		if (xfer->tx_buf || xfer->rx_buf) {
			reinit_completion(&ctlr->xfer_completion);

@@ -1197,6 +1202,11 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
					xfer->len);
		}

		if (!ctlr->ptp_sts_supported) {
			ptp_read_system_postts(xfer->ptp_sts);
			xfer->ptp_sts_word_post = xfer->len;
		}

		trace_spi_transfer_stop(msg, xfer);

		if (msg->status != -EINPROGRESS)
@@ -1265,6 +1275,7 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
 */
static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
{
	struct spi_transfer *xfer;
	struct spi_message *msg;
	bool was_busy = false;
	unsigned long flags;
@@ -1391,6 +1402,13 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
		goto out;
	}

	if (!ctlr->ptp_sts_supported && !ctlr->transfer_one) {
		list_for_each_entry(xfer, &msg->transfers, transfer_list) {
			xfer->ptp_sts_word_pre = 0;
			ptp_read_system_prets(xfer->ptp_sts);
		}
	}

	ret = ctlr->transfer_one_message(ctlr, msg);
	if (ret) {
		dev_err(&ctlr->dev,
@@ -1418,6 +1436,99 @@ static void spi_pump_messages(struct kthread_work *work)
	__spi_pump_messages(ctlr, true);
}

/**
 * spi_take_timestamp_pre - helper for drivers to collect the beginning of the
 *			    TX timestamp for the requested byte from the SPI
 *			    transfer. The frequency with which this function
 *			    must be called (once per word, once for the whole
 *			    transfer, once per batch of words etc) is arbitrary
 *			    as long as the @tx buffer offset is greater than or
 *			    equal to the requested byte at the time of the
 *			    call. The timestamp is only taken once, at the
 *			    first such call. It is assumed that the driver
 *			    advances its @tx buffer pointer monotonically.
 * @ctlr: Pointer to the spi_controller structure of the driver
 * @xfer: Pointer to the transfer being timestamped
 * @tx: Pointer to the current word within the xfer->tx_buf that the driver is
 *	preparing to transmit right now.
 * @irqs_off: If true, will disable IRQs and preemption for the duration of the
 *	      transfer, for less jitter in time measurement. Only compatible
 *	      with PIO drivers. If true, must follow up with
 *	      spi_take_timestamp_post or otherwise system will crash.
 *	      WARNING: for fully predictable results, the CPU frequency must
 *	      also be under control (governor).
 */
void spi_take_timestamp_pre(struct spi_controller *ctlr,
			    struct spi_transfer *xfer,
			    const void *tx, bool irqs_off)
{
	u8 bytes_per_word = DIV_ROUND_UP(xfer->bits_per_word, 8);

	if (!xfer->ptp_sts)
		return;

	if (xfer->timestamped_pre)
		return;

	if (tx < (xfer->tx_buf + xfer->ptp_sts_word_pre * bytes_per_word))
		return;

	/* Capture the resolution of the timestamp */
	xfer->ptp_sts_word_pre = (tx - xfer->tx_buf) / bytes_per_word;

	xfer->timestamped_pre = true;

	if (irqs_off) {
		local_irq_save(ctlr->irq_flags);
		preempt_disable();
	}

	ptp_read_system_prets(xfer->ptp_sts);
}
EXPORT_SYMBOL_GPL(spi_take_timestamp_pre);

/**
 * spi_take_timestamp_post - helper for drivers to collect the end of the
 *			     TX timestamp for the requested byte from the SPI
 *			     transfer. Can be called with an arbitrary
 *			     frequency: only the first call where @tx exceeds
 *			     or is equal to the requested word will be
 *			     timestamped.
 * @ctlr: Pointer to the spi_controller structure of the driver
 * @xfer: Pointer to the transfer being timestamped
 * @tx: Pointer to the current word within the xfer->tx_buf that the driver has
 *	just transmitted.
 * @irqs_off: If true, will re-enable IRQs and preemption for the local CPU.
 */
void spi_take_timestamp_post(struct spi_controller *ctlr,
			     struct spi_transfer *xfer,
			     const void *tx, bool irqs_off)
{
	u8 bytes_per_word = DIV_ROUND_UP(xfer->bits_per_word, 8);

	if (!xfer->ptp_sts)
		return;

	if (xfer->timestamped_post)
		return;

	if (tx < (xfer->tx_buf + xfer->ptp_sts_word_post * bytes_per_word))
		return;

	ptp_read_system_postts(xfer->ptp_sts);

	if (irqs_off) {
		local_irq_restore(ctlr->irq_flags);
		preempt_enable();
	}

	/* Capture the resolution of the timestamp */
	xfer->ptp_sts_word_post = (tx - xfer->tx_buf) / bytes_per_word;

	xfer->timestamped_post = true;
}
EXPORT_SYMBOL_GPL(spi_take_timestamp_post);

/**
 * spi_set_thread_rt - set the controller to pump at realtime priority
 * @ctlr: controller to boost priority of
@@ -1503,6 +1614,7 @@ EXPORT_SYMBOL_GPL(spi_get_next_queued_message);
 */
void spi_finalize_current_message(struct spi_controller *ctlr)
{
	struct spi_transfer *xfer;
	struct spi_message *mesg;
	unsigned long flags;
	int ret;
@@ -1511,6 +1623,13 @@ void spi_finalize_current_message(struct spi_controller *ctlr)
	mesg = ctlr->cur_msg;
	spin_unlock_irqrestore(&ctlr->queue_lock, flags);

	if (!ctlr->ptp_sts_supported && !ctlr->transfer_one) {
		list_for_each_entry(xfer, &mesg->transfers, transfer_list) {
			ptp_read_system_postts(xfer->ptp_sts);
			xfer->ptp_sts_word_post = xfer->len;
		}
	}

	spi_unmap_msg(ctlr, mesg);

	if (ctlr->cur_msg_prepared && ctlr->unprepare_message) {
@@ -3273,6 +3392,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
	struct spi_controller *ctlr = spi->controller;
	struct spi_transfer *xfer;

	/*
	 * Some controllers do not support doing regular SPI transfers. Return
@@ -3288,6 +3408,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)

	trace_spi_message_submit(message);

	if (!ctlr->ptp_sts_supported) {
		list_for_each_entry(xfer, &message->transfers, transfer_list) {
			xfer->ptp_sts_word_pre = 0;
			ptp_read_system_prets(xfer->ptp_sts);
		}
	}

	return ctlr->transfer(spi, message);
}

+61 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/completion.h>
#include <linux/scatterlist.h>
#include <linux/gpio/consumer.h>
#include <linux/ptp_clock_kernel.h>

struct dma_chan;
struct property_entry;
@@ -409,6 +410,12 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
 * @fw_translate_cs: If the boot firmware uses different numbering scheme
 *	what Linux expects, this optional hook can be used to translate
 *	between the two.
 * @ptp_sts_supported: If the driver sets this to true, it must provide a
 *	time snapshot in @spi_transfer->ptp_sts as close as possible to the
 *	moment in time when @spi_transfer->ptp_sts_word_pre and
 *	@spi_transfer->ptp_sts_word_post were transmitted.
 *	If the driver does not set this, the SPI core takes the snapshot as
 *	close to the driver hand-over as possible.
 *
 * Each SPI controller can communicate with one or more @spi_device
 * children.  These make a small bus, sharing MOSI, MISO and SCK signals
@@ -604,6 +611,15 @@ struct spi_controller {
	void			*dummy_tx;

	int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs);

	/*
	 * Driver sets this field to indicate it is able to snapshot SPI
	 * transfers (needed e.g. for reading the time of POSIX clocks)
	 */
	bool			ptp_sts_supported;

	/* Interrupt enable state during PTP system timestamping */
	unsigned long		irq_flags;
};

static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
@@ -644,6 +660,14 @@ extern struct spi_message *spi_get_next_queued_message(struct spi_controller *ct
extern void spi_finalize_current_message(struct spi_controller *ctlr);
extern void spi_finalize_current_transfer(struct spi_controller *ctlr);

/* Helper calls for driver to timestamp transfer */
void spi_take_timestamp_pre(struct spi_controller *ctlr,
			    struct spi_transfer *xfer,
			    const void *tx, bool irqs_off);
void spi_take_timestamp_post(struct spi_controller *ctlr,
			     struct spi_transfer *xfer,
			     const void *tx, bool irqs_off);

/* the spi driver core manages memory for the spi_controller classdev */
extern struct spi_controller *__spi_alloc_controller(struct device *host,
						unsigned int size, bool slave);
@@ -753,6 +777,35 @@ extern void spi_res_release(struct spi_controller *ctlr,
 * @transfer_list: transfers are sequenced through @spi_message.transfers
 * @tx_sg: Scatterlist for transmit, currently not for client use
 * @rx_sg: Scatterlist for receive, currently not for client use
 * @ptp_sts_word_pre: The word (subject to bits_per_word semantics) offset
 *	within @tx_buf for which the SPI device is requesting that the time
 *	snapshot for this transfer begins. Upon completing the SPI transfer,
 *	this value may have changed compared to what was requested, depending
 *	on the available snapshotting resolution (DMA transfer,
 *	@ptp_sts_supported is false, etc).
 * @ptp_sts_word_post: See @ptp_sts_word_post. The two can be equal (meaning
 *	that a single byte should be snapshotted).
 *	If the core takes care of the timestamp (if @ptp_sts_supported is false
 *	for this controller), it will set @ptp_sts_word_pre to 0, and
 *	@ptp_sts_word_post to the length of the transfer. This is done
 *	purposefully (instead of setting to spi_transfer->len - 1) to denote
 *	that a transfer-level snapshot taken from within the driver may still
 *	be of higher quality.
 * @ptp_sts: Pointer to a memory location held by the SPI slave device where a
 *	PTP system timestamp structure may lie. If drivers use PIO or their
 *	hardware has some sort of assist for retrieving exact transfer timing,
 *	they can (and should) assert @ptp_sts_supported and populate this
 *	structure using the ptp_read_system_*ts helper functions.
 *	The timestamp must represent the time at which the SPI slave device has
 *	processed the word, i.e. the "pre" timestamp should be taken before
 *	transmitting the "pre" word, and the "post" timestamp after receiving
 *	transmit confirmation from the controller for the "post" word.
 * @timestamped_pre: Set by the SPI controller driver to denote it has acted
 *	upon the @ptp_sts request. Not set when the SPI core has taken care of
 *	the task. SPI device drivers are free to print a warning if this comes
 *	back unset and they need the better resolution.
 * @timestamped_post: See above. The reason why both exist is that these
 *	booleans are also used to keep state in the core SPI logic.
 *
 * SPI transfers always write the same number of bytes as they read.
 * Protocol drivers should always provide @rx_buf and/or @tx_buf.
@@ -842,6 +895,14 @@ struct spi_transfer {

	u32		effective_speed_hz;

	unsigned int	ptp_sts_word_pre;
	unsigned int	ptp_sts_word_post;

	struct ptp_system_timestamp *ptp_sts;

	bool		timestamped_pre;
	bool		timestamped_post;

	struct list_head transfer_list;
};