]>
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 | ||
612a462a | 33 | static const struct config_item_type iio_hrtimer_type = { |
ac5006a2 DB |
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 = { | |
ac5006a2 DB |
117 | .set_trigger_state = iio_trig_hrtimer_set_state, |
118 | }; | |
119 | ||
120 | static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) | |
121 | { | |
122 | struct iio_hrtimer_info *trig_info; | |
123 | int ret; | |
124 | ||
125 | trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); | |
126 | if (!trig_info) | |
127 | return ERR_PTR(-ENOMEM); | |
128 | ||
129 | trig_info->swt.trigger = iio_trigger_alloc("%s", name); | |
130 | if (!trig_info->swt.trigger) { | |
131 | ret = -ENOMEM; | |
132 | goto err_free_trig_info; | |
133 | } | |
134 | ||
135 | iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info); | |
136 | trig_info->swt.trigger->ops = &iio_hrtimer_trigger_ops; | |
137 | trig_info->swt.trigger->dev.groups = iio_hrtimer_attr_groups; | |
138 | ||
139 | hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | |
140 | trig_info->timer.function = iio_hrtimer_trig_handler; | |
141 | ||
142 | trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; | |
8b0e1953 | 143 | trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency; |
ac5006a2 DB |
144 | |
145 | ret = iio_trigger_register(trig_info->swt.trigger); | |
146 | if (ret) | |
147 | goto err_free_trigger; | |
148 | ||
149 | iio_swt_group_init_type_name(&trig_info->swt, name, &iio_hrtimer_type); | |
150 | return &trig_info->swt; | |
151 | err_free_trigger: | |
152 | iio_trigger_free(trig_info->swt.trigger); | |
153 | err_free_trig_info: | |
154 | kfree(trig_info); | |
155 | ||
156 | return ERR_PTR(ret); | |
157 | } | |
158 | ||
159 | static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt) | |
160 | { | |
161 | struct iio_hrtimer_info *trig_info; | |
162 | ||
163 | trig_info = iio_trigger_get_drvdata(swt->trigger); | |
164 | ||
165 | iio_trigger_unregister(swt->trigger); | |
166 | ||
167 | /* cancel the timer after unreg to make sure no one rearms it */ | |
168 | hrtimer_cancel(&trig_info->timer); | |
169 | iio_trigger_free(swt->trigger); | |
170 | kfree(trig_info); | |
171 | ||
172 | return 0; | |
173 | } | |
174 | ||
175 | static const struct iio_sw_trigger_ops iio_trig_hrtimer_ops = { | |
176 | .probe = iio_trig_hrtimer_probe, | |
177 | .remove = iio_trig_hrtimer_remove, | |
178 | }; | |
179 | ||
180 | static struct iio_sw_trigger_type iio_trig_hrtimer = { | |
181 | .name = "hrtimer", | |
182 | .owner = THIS_MODULE, | |
183 | .ops = &iio_trig_hrtimer_ops, | |
184 | }; | |
185 | ||
186 | module_iio_sw_trigger_driver(iio_trig_hrtimer); | |
187 | ||
188 | MODULE_AUTHOR("Marten Svanfeldt <marten@intuitiveaerial.com>"); | |
189 | MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); | |
190 | MODULE_DESCRIPTION("Periodic hrtimer trigger for the IIO subsystem"); | |
191 | MODULE_LICENSE("GPL v2"); |