]>
Commit | Line | Data |
---|---|---|
ac5006a2 DB |
1 | /** |
2 | * The industrial I/O periodic hrtimer trigger driver | |
3 | * | |
4 | * Copyright (C) Intuitive Aerial AB | |
5 | * Written by Marten Svanfeldt, marten@intuitiveaerial.com | |
6 | * Copyright (C) 2012, Analog Device Inc. | |
7 | * Author: Lars-Peter Clausen <lars@metafoo.de> | |
8 | * Copyright (C) 2015, Intel Corporation | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License version 2 as published by | |
12 | * the Free Software Foundation. | |
13 | * | |
14 | */ | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/hrtimer.h> | |
18 | ||
19 | #include <linux/iio/iio.h> | |
20 | #include <linux/iio/trigger.h> | |
21 | #include <linux/iio/sw_trigger.h> | |
22 | ||
23 | /* default sampling frequency - 100Hz */ | |
24 | #define HRTIMER_DEFAULT_SAMPLING_FREQUENCY 100 | |
25 | ||
26 | struct iio_hrtimer_info { | |
27 | struct iio_sw_trigger swt; | |
28 | struct hrtimer timer; | |
29 | unsigned long sampling_frequency; | |
30 | ktime_t period; | |
31 | }; | |
32 | ||
33 | static struct config_item_type iio_hrtimer_type = { | |
34 | .ct_owner = THIS_MODULE, | |
35 | }; | |
36 | ||
37 | static | |
38 | ssize_t iio_hrtimer_show_sampling_frequency(struct device *dev, | |
39 | struct device_attribute *attr, | |
40 | char *buf) | |
41 | { | |
42 | struct iio_trigger *trig = to_iio_trigger(dev); | |
43 | struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); | |
44 | ||
45 | return snprintf(buf, PAGE_SIZE, "%lu\n", info->sampling_frequency); | |
46 | } | |
47 | ||
48 | static | |
49 | ssize_t iio_hrtimer_store_sampling_frequency(struct device *dev, | |
50 | struct device_attribute *attr, | |
51 | const char *buf, size_t len) | |
52 | { | |
53 | struct iio_trigger *trig = to_iio_trigger(dev); | |
54 | struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); | |
55 | unsigned long val; | |
56 | int ret; | |
57 | ||
58 | ret = kstrtoul(buf, 10, &val); | |
59 | if (ret) | |
60 | return ret; | |
61 | ||
62 | if (!val || val > NSEC_PER_SEC) | |
63 | return -EINVAL; | |
64 | ||
65 | info->sampling_frequency = val; | |
8b0e1953 | 66 | info->period = NSEC_PER_SEC / val; |
ac5006a2 DB |
67 | |
68 | return len; | |
69 | } | |
70 | ||
71 | static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR, | |
72 | iio_hrtimer_show_sampling_frequency, | |
73 | iio_hrtimer_store_sampling_frequency); | |
74 | ||
75 | static struct attribute *iio_hrtimer_attrs[] = { | |
76 | &dev_attr_sampling_frequency.attr, | |
77 | NULL | |
78 | }; | |
79 | ||
80 | static const struct attribute_group iio_hrtimer_attr_group = { | |
81 | .attrs = iio_hrtimer_attrs, | |
82 | }; | |
83 | ||
84 | static const struct attribute_group *iio_hrtimer_attr_groups[] = { | |
85 | &iio_hrtimer_attr_group, | |
86 | NULL | |
87 | }; | |
88 | ||
89 | static enum hrtimer_restart iio_hrtimer_trig_handler(struct hrtimer *timer) | |
90 | { | |
91 | struct iio_hrtimer_info *info; | |
92 | ||
93 | info = container_of(timer, struct iio_hrtimer_info, timer); | |
94 | ||
95 | hrtimer_forward_now(timer, info->period); | |
96 | iio_trigger_poll(info->swt.trigger); | |
97 | ||
98 | return HRTIMER_RESTART; | |
99 | } | |
100 | ||
101 | static int iio_trig_hrtimer_set_state(struct iio_trigger *trig, bool state) | |
102 | { | |
103 | struct iio_hrtimer_info *trig_info; | |
104 | ||
105 | trig_info = iio_trigger_get_drvdata(trig); | |
106 | ||
107 | if (state) | |
108 | hrtimer_start(&trig_info->timer, trig_info->period, | |
109 | HRTIMER_MODE_REL); | |
110 | else | |
111 | hrtimer_cancel(&trig_info->timer); | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | static const struct iio_trigger_ops iio_hrtimer_trigger_ops = { | |
117 | .owner = THIS_MODULE, | |
118 | .set_trigger_state = iio_trig_hrtimer_set_state, | |
119 | }; | |
120 | ||
121 | static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) | |
122 | { | |
123 | struct iio_hrtimer_info *trig_info; | |
124 | int ret; | |
125 | ||
126 | trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); | |
127 | if (!trig_info) | |
128 | return ERR_PTR(-ENOMEM); | |
129 | ||
130 | trig_info->swt.trigger = iio_trigger_alloc("%s", name); | |
131 | if (!trig_info->swt.trigger) { | |
132 | ret = -ENOMEM; | |
133 | goto err_free_trig_info; | |
134 | } | |
135 | ||
136 | iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info); | |
137 | trig_info->swt.trigger->ops = &iio_hrtimer_trigger_ops; | |
138 | trig_info->swt.trigger->dev.groups = iio_hrtimer_attr_groups; | |
139 | ||
140 | hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | |
141 | trig_info->timer.function = iio_hrtimer_trig_handler; | |
142 | ||
143 | trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; | |
8b0e1953 | 144 | trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency; |
ac5006a2 DB |
145 | |
146 | ret = iio_trigger_register(trig_info->swt.trigger); | |
147 | if (ret) | |
148 | goto err_free_trigger; | |
149 | ||
150 | iio_swt_group_init_type_name(&trig_info->swt, name, &iio_hrtimer_type); | |
151 | return &trig_info->swt; | |
152 | err_free_trigger: | |
153 | iio_trigger_free(trig_info->swt.trigger); | |
154 | err_free_trig_info: | |
155 | kfree(trig_info); | |
156 | ||
157 | return ERR_PTR(ret); | |
158 | } | |
159 | ||
160 | static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt) | |
161 | { | |
162 | struct iio_hrtimer_info *trig_info; | |
163 | ||
164 | trig_info = iio_trigger_get_drvdata(swt->trigger); | |
165 | ||
166 | iio_trigger_unregister(swt->trigger); | |
167 | ||
168 | /* cancel the timer after unreg to make sure no one rearms it */ | |
169 | hrtimer_cancel(&trig_info->timer); | |
170 | iio_trigger_free(swt->trigger); | |
171 | kfree(trig_info); | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | static const struct iio_sw_trigger_ops iio_trig_hrtimer_ops = { | |
177 | .probe = iio_trig_hrtimer_probe, | |
178 | .remove = iio_trig_hrtimer_remove, | |
179 | }; | |
180 | ||
181 | static struct iio_sw_trigger_type iio_trig_hrtimer = { | |
182 | .name = "hrtimer", | |
183 | .owner = THIS_MODULE, | |
184 | .ops = &iio_trig_hrtimer_ops, | |
185 | }; | |
186 | ||
187 | module_iio_sw_trigger_driver(iio_trig_hrtimer); | |
188 | ||
189 | MODULE_AUTHOR("Marten Svanfeldt <marten@intuitiveaerial.com>"); | |
190 | MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); | |
191 | MODULE_DESCRIPTION("Periodic hrtimer trigger for the IIO subsystem"); | |
192 | MODULE_LICENSE("GPL v2"); |