|
@@ -178,6 +178,86 @@ ssize_t iio_read_const_attr(struct device *dev,
|
|
|
}
|
|
|
EXPORT_SYMBOL(iio_read_const_attr);
|
|
|
|
|
|
+static int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ const struct iio_event_interface *ev_int = indio_dev->event_interface;
|
|
|
+
|
|
|
+ ret = mutex_lock_interruptible(&indio_dev->mlock);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ if ((ev_int && iio_event_enabled(ev_int)) ||
|
|
|
+ iio_buffer_enabled(indio_dev)) {
|
|
|
+ mutex_unlock(&indio_dev->mlock);
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+ indio_dev->clock_id = clock_id;
|
|
|
+ mutex_unlock(&indio_dev->mlock);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * iio_get_time_ns() - utility function to get a time stamp for events etc
|
|
|
+ * @indio_dev: device
|
|
|
+ */
|
|
|
+s64 iio_get_time_ns(const struct iio_dev *indio_dev)
|
|
|
+{
|
|
|
+ struct timespec tp;
|
|
|
+
|
|
|
+ switch (iio_device_get_clock(indio_dev)) {
|
|
|
+ case CLOCK_REALTIME:
|
|
|
+ ktime_get_real_ts(&tp);
|
|
|
+ break;
|
|
|
+ case CLOCK_MONOTONIC:
|
|
|
+ ktime_get_ts(&tp);
|
|
|
+ break;
|
|
|
+ case CLOCK_MONOTONIC_RAW:
|
|
|
+ getrawmonotonic(&tp);
|
|
|
+ break;
|
|
|
+ case CLOCK_REALTIME_COARSE:
|
|
|
+ tp = current_kernel_time();
|
|
|
+ break;
|
|
|
+ case CLOCK_MONOTONIC_COARSE:
|
|
|
+ tp = get_monotonic_coarse();
|
|
|
+ break;
|
|
|
+ case CLOCK_BOOTTIME:
|
|
|
+ get_monotonic_boottime(&tp);
|
|
|
+ break;
|
|
|
+ case CLOCK_TAI:
|
|
|
+ timekeeping_clocktai(&tp);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ BUG();
|
|
|
+ }
|
|
|
+
|
|
|
+ return timespec_to_ns(&tp);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(iio_get_time_ns);
|
|
|
+
|
|
|
+/**
|
|
|
+ * iio_get_time_res() - utility function to get time stamp clock resolution in
|
|
|
+ * nano seconds.
|
|
|
+ * @indio_dev: device
|
|
|
+ */
|
|
|
+unsigned int iio_get_time_res(const struct iio_dev *indio_dev)
|
|
|
+{
|
|
|
+ switch (iio_device_get_clock(indio_dev)) {
|
|
|
+ case CLOCK_REALTIME:
|
|
|
+ case CLOCK_MONOTONIC:
|
|
|
+ case CLOCK_MONOTONIC_RAW:
|
|
|
+ case CLOCK_BOOTTIME:
|
|
|
+ case CLOCK_TAI:
|
|
|
+ return hrtimer_resolution;
|
|
|
+ case CLOCK_REALTIME_COARSE:
|
|
|
+ case CLOCK_MONOTONIC_COARSE:
|
|
|
+ return LOW_RES_NSEC;
|
|
|
+ default:
|
|
|
+ BUG();
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(iio_get_time_res);
|
|
|
+
|
|
|
static int __init iio_init(void)
|
|
|
{
|
|
|
int ret;
|
|
@@ -990,11 +1070,91 @@ static ssize_t iio_show_dev_name(struct device *dev,
|
|
|
|
|
|
static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
|
|
|
|
|
|
+static ssize_t iio_show_timestamp_clock(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ const struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
|
+ const clockid_t clk = iio_device_get_clock(indio_dev);
|
|
|
+ const char *name;
|
|
|
+ ssize_t sz;
|
|
|
+
|
|
|
+ switch (clk) {
|
|
|
+ case CLOCK_REALTIME:
|
|
|
+ name = "realtime\n";
|
|
|
+ sz = sizeof("realtime\n");
|
|
|
+ break;
|
|
|
+ case CLOCK_MONOTONIC:
|
|
|
+ name = "monotonic\n";
|
|
|
+ sz = sizeof("monotonic\n");
|
|
|
+ break;
|
|
|
+ case CLOCK_MONOTONIC_RAW:
|
|
|
+ name = "monotonic_raw\n";
|
|
|
+ sz = sizeof("monotonic_raw\n");
|
|
|
+ break;
|
|
|
+ case CLOCK_REALTIME_COARSE:
|
|
|
+ name = "realtime_coarse\n";
|
|
|
+ sz = sizeof("realtime_coarse\n");
|
|
|
+ break;
|
|
|
+ case CLOCK_MONOTONIC_COARSE:
|
|
|
+ name = "monotonic_coarse\n";
|
|
|
+ sz = sizeof("monotonic_coarse\n");
|
|
|
+ break;
|
|
|
+ case CLOCK_BOOTTIME:
|
|
|
+ name = "boottime\n";
|
|
|
+ sz = sizeof("boottime\n");
|
|
|
+ break;
|
|
|
+ case CLOCK_TAI:
|
|
|
+ name = "tai\n";
|
|
|
+ sz = sizeof("tai\n");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ BUG();
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(buf, name, sz);
|
|
|
+ return sz;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t iio_store_timestamp_clock(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buf, size_t len)
|
|
|
+{
|
|
|
+ clockid_t clk;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (sysfs_streq(buf, "realtime"))
|
|
|
+ clk = CLOCK_REALTIME;
|
|
|
+ else if (sysfs_streq(buf, "monotonic"))
|
|
|
+ clk = CLOCK_MONOTONIC;
|
|
|
+ else if (sysfs_streq(buf, "monotonic_raw"))
|
|
|
+ clk = CLOCK_MONOTONIC_RAW;
|
|
|
+ else if (sysfs_streq(buf, "realtime_coarse"))
|
|
|
+ clk = CLOCK_REALTIME_COARSE;
|
|
|
+ else if (sysfs_streq(buf, "monotonic_coarse"))
|
|
|
+ clk = CLOCK_MONOTONIC_COARSE;
|
|
|
+ else if (sysfs_streq(buf, "boottime"))
|
|
|
+ clk = CLOCK_BOOTTIME;
|
|
|
+ else if (sysfs_streq(buf, "tai"))
|
|
|
+ clk = CLOCK_TAI;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ ret = iio_device_set_clock(dev_to_iio_dev(dev), clk);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+static DEVICE_ATTR(current_timestamp_clock, S_IRUGO | S_IWUSR,
|
|
|
+ iio_show_timestamp_clock, iio_store_timestamp_clock);
|
|
|
+
|
|
|
static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
|
|
{
|
|
|
int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
|
|
|
struct iio_dev_attr *p;
|
|
|
- struct attribute **attr;
|
|
|
+ struct attribute **attr, *clk = NULL;
|
|
|
|
|
|
/* First count elements in any existing group */
|
|
|
if (indio_dev->info->attrs) {
|
|
@@ -1009,16 +1169,25 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
|
|
*/
|
|
|
if (indio_dev->channels)
|
|
|
for (i = 0; i < indio_dev->num_channels; i++) {
|
|
|
- ret = iio_device_add_channel_sysfs(indio_dev,
|
|
|
- &indio_dev
|
|
|
- ->channels[i]);
|
|
|
+ const struct iio_chan_spec *chan =
|
|
|
+ &indio_dev->channels[i];
|
|
|
+
|
|
|
+ if (chan->type == IIO_TIMESTAMP)
|
|
|
+ clk = &dev_attr_current_timestamp_clock.attr;
|
|
|
+
|
|
|
+ ret = iio_device_add_channel_sysfs(indio_dev, chan);
|
|
|
if (ret < 0)
|
|
|
goto error_clear_attrs;
|
|
|
attrcount += ret;
|
|
|
}
|
|
|
|
|
|
+ if (indio_dev->event_interface)
|
|
|
+ clk = &dev_attr_current_timestamp_clock.attr;
|
|
|
+
|
|
|
if (indio_dev->name)
|
|
|
attrcount++;
|
|
|
+ if (clk)
|
|
|
+ attrcount++;
|
|
|
|
|
|
indio_dev->chan_attr_group.attrs = kcalloc(attrcount + 1,
|
|
|
sizeof(indio_dev->chan_attr_group.attrs[0]),
|
|
@@ -1039,6 +1208,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
|
|
indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr;
|
|
|
if (indio_dev->name)
|
|
|
indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr;
|
|
|
+ if (clk)
|
|
|
+ indio_dev->chan_attr_group.attrs[attrn++] = clk;
|
|
|
|
|
|
indio_dev->groups[indio_dev->groupcounter++] =
|
|
|
&indio_dev->chan_attr_group;
|