linux/drivers/spi/spi-slave-time.c
<<
>>
Prefs
   1/*
   2 * SPI slave handler reporting uptime at reception of previous SPI message
   3 *
   4 * This SPI slave handler sends the time of reception of the last SPI message
   5 * as two 32-bit unsigned integers in binary format and in network byte order,
   6 * representing the number of seconds and fractional seconds (in microseconds)
   7 * since boot up.
   8 *
   9 * Copyright (C) 2016-2017 Glider bvba
  10 *
  11 * This file is subject to the terms and conditions of the GNU General Public
  12 * License.  See the file "COPYING" in the main directory of this archive
  13 * for more details.
  14 *
  15 * Usage (assuming /dev/spidev2.0 corresponds to the SPI master on the remote
  16 * system):
  17 *
  18 *   # spidev_test -D /dev/spidev2.0 -p dummy-8B
  19 *   spi mode: 0x0
  20 *   bits per word: 8
  21 *   max speed: 500000 Hz (500 KHz)
  22 *   RX | 00 00 04 6D 00 09 5B BB ...
  23 *              ^^^^^    ^^^^^^^^
  24 *              seconds  microseconds
  25 */
  26
  27#include <linux/completion.h>
  28#include <linux/module.h>
  29#include <linux/sched/clock.h>
  30#include <linux/spi/spi.h>
  31
  32
  33struct spi_slave_time_priv {
  34        struct spi_device *spi;
  35        struct completion finished;
  36        struct spi_transfer xfer;
  37        struct spi_message msg;
  38        __be32 buf[2];
  39};
  40
  41static int spi_slave_time_submit(struct spi_slave_time_priv *priv);
  42
  43static void spi_slave_time_complete(void *arg)
  44{
  45        struct spi_slave_time_priv *priv = arg;
  46        int ret;
  47
  48        ret = priv->msg.status;
  49        if (ret)
  50                goto terminate;
  51
  52        ret = spi_slave_time_submit(priv);
  53        if (ret)
  54                goto terminate;
  55
  56        return;
  57
  58terminate:
  59        dev_info(&priv->spi->dev, "Terminating\n");
  60        complete(&priv->finished);
  61}
  62
  63static int spi_slave_time_submit(struct spi_slave_time_priv *priv)
  64{
  65        u32 rem_us;
  66        int ret;
  67        u64 ts;
  68
  69        ts = local_clock();
  70        rem_us = do_div(ts, 1000000000) / 1000;
  71
  72        priv->buf[0] = cpu_to_be32(ts);
  73        priv->buf[1] = cpu_to_be32(rem_us);
  74
  75        spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
  76
  77        priv->msg.complete = spi_slave_time_complete;
  78        priv->msg.context = priv;
  79
  80        ret = spi_async(priv->spi, &priv->msg);
  81        if (ret)
  82                dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
  83
  84        return ret;
  85}
  86
  87static int spi_slave_time_probe(struct spi_device *spi)
  88{
  89        struct spi_slave_time_priv *priv;
  90        int ret;
  91
  92        priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
  93        if (!priv)
  94                return -ENOMEM;
  95
  96        priv->spi = spi;
  97        init_completion(&priv->finished);
  98        priv->xfer.tx_buf = priv->buf;
  99        priv->xfer.len = sizeof(priv->buf);
 100
 101        ret = spi_slave_time_submit(priv);
 102        if (ret)
 103                return ret;
 104
 105        spi_set_drvdata(spi, priv);
 106        return 0;
 107}
 108
 109static int spi_slave_time_remove(struct spi_device *spi)
 110{
 111        struct spi_slave_time_priv *priv = spi_get_drvdata(spi);
 112
 113        spi_slave_abort(spi);
 114        wait_for_completion(&priv->finished);
 115        return 0;
 116}
 117
 118static struct spi_driver spi_slave_time_driver = {
 119        .driver = {
 120                .name   = "spi-slave-time",
 121        },
 122        .probe          = spi_slave_time_probe,
 123        .remove         = spi_slave_time_remove,
 124};
 125module_spi_driver(spi_slave_time_driver);
 126
 127MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
 128MODULE_DESCRIPTION("SPI slave reporting uptime at previous SPI message");
 129MODULE_LICENSE("GPL v2");
 130