Commit | Line | Data |
---|---|---|
ac4029fb | 1 | /* |
4efe6065 | 2 | * Greybus Vibrator protocol driver. |
ac4029fb GKH |
3 | * |
4 | * Copyright 2014 Google Inc. | |
a46e9671 | 5 | * Copyright 2014 Linaro Ltd. |
ac4029fb GKH |
6 | * |
7 | * Released under the GPLv2 only. | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/kdev_t.h> | |
396671b1 | 15 | #include <linux/idr.h> |
633e45ea AC |
16 | #include <linux/pm_runtime.h> |
17 | ||
ac4029fb GKH |
18 | #include "greybus.h" |
19 | ||
20 | struct gb_vibrator_device { | |
21 | struct gb_connection *connection; | |
22 | struct device *dev; | |
396671b1 | 23 | int minor; /* vibrator minor number */ |
0900845a | 24 | struct delayed_work delayed_work; |
ac4029fb GKH |
25 | }; |
26 | ||
6d653370 | 27 | /* Greybus Vibrator operation types */ |
ac4029fb GKH |
28 | #define GB_VIBRATOR_TYPE_ON 0x02 |
29 | #define GB_VIBRATOR_TYPE_OFF 0x03 | |
ac4029fb | 30 | |
0900845a | 31 | static int turn_off(struct gb_vibrator_device *vib) |
ac4029fb | 32 | { |
633e45ea AC |
33 | struct gb_bundle *bundle = vib->connection->bundle; |
34 | int ret; | |
35 | ||
0900845a AC |
36 | ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, |
37 | NULL, 0, NULL, 0); | |
633e45ea AC |
38 | |
39 | gb_pm_runtime_put_autosuspend(bundle); | |
40 | ||
41 | return ret; | |
ac4029fb GKH |
42 | } |
43 | ||
0900845a | 44 | static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) |
ac4029fb | 45 | { |
633e45ea AC |
46 | struct gb_bundle *bundle = vib->connection->bundle; |
47 | int ret; | |
48 | ||
49 | ret = gb_pm_runtime_get_sync(bundle); | |
50 | if (ret) | |
51 | return ret; | |
52 | ||
0900845a AC |
53 | /* Vibrator was switched ON earlier */ |
54 | if (cancel_delayed_work_sync(&vib->delayed_work)) | |
55 | turn_off(vib); | |
56 | ||
57 | ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, | |
633e45ea | 58 | NULL, 0, NULL, 0); |
0900845a AC |
59 | if (ret) { |
60 | gb_pm_runtime_put_autosuspend(bundle); | |
61 | return ret; | |
62 | } | |
633e45ea | 63 | |
0900845a | 64 | schedule_delayed_work(&vib->delayed_work, msecs_to_jiffies(timeout_ms)); |
633e45ea | 65 | |
0900845a AC |
66 | return 0; |
67 | } | |
68 | ||
69 | static void gb_vibrator_worker(struct work_struct *work) | |
70 | { | |
71 | struct delayed_work *delayed_work = to_delayed_work(work); | |
72 | struct gb_vibrator_device *vib = | |
73 | container_of(delayed_work, struct gb_vibrator_device, delayed_work); | |
74 | ||
75 | turn_off(vib); | |
ac4029fb GKH |
76 | } |
77 | ||
78 | static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, | |
79 | const char *buf, size_t count) | |
80 | { | |
81 | struct gb_vibrator_device *vib = dev_get_drvdata(dev); | |
82 | unsigned long val; | |
83 | int retval; | |
84 | ||
85 | retval = kstrtoul(buf, 10, &val); | |
86 | if (retval < 0) { | |
87 | dev_err(dev, "could not parse timeout value %d\n", retval); | |
88 | return retval; | |
89 | } | |
90 | ||
ac4029fb GKH |
91 | if (val) |
92 | retval = turn_on(vib, (u16)val); | |
93 | else | |
94 | retval = turn_off(vib); | |
95 | if (retval) | |
96 | return retval; | |
97 | ||
98 | return count; | |
99 | } | |
100 | static DEVICE_ATTR_WO(timeout); | |
101 | ||
102 | static struct attribute *vibrator_attrs[] = { | |
103 | &dev_attr_timeout.attr, | |
104 | NULL, | |
105 | }; | |
106 | ATTRIBUTE_GROUPS(vibrator); | |
107 | ||
108 | static struct class vibrator_class = { | |
109 | .name = "vibrator", | |
110 | .owner = THIS_MODULE, | |
ac4029fb | 111 | .dev_groups = vibrator_groups, |
ac4029fb GKH |
112 | }; |
113 | ||
6b17492e | 114 | static DEFINE_IDA(minors); |
ac4029fb | 115 | |
e0deb079 JH |
116 | static int gb_vibrator_probe(struct gb_bundle *bundle, |
117 | const struct greybus_bundle_id *id) | |
ac4029fb | 118 | { |
e0deb079 JH |
119 | struct greybus_descriptor_cport *cport_desc; |
120 | struct gb_connection *connection; | |
ac4029fb GKH |
121 | struct gb_vibrator_device *vib; |
122 | struct device *dev; | |
123 | int retval; | |
124 | ||
e0deb079 JH |
125 | if (bundle->num_cports != 1) |
126 | return -ENODEV; | |
127 | ||
128 | cport_desc = &bundle->cport_desc[0]; | |
129 | if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR) | |
130 | return -ENODEV; | |
131 | ||
ac4029fb GKH |
132 | vib = kzalloc(sizeof(*vib), GFP_KERNEL); |
133 | if (!vib) | |
134 | return -ENOMEM; | |
135 | ||
e0deb079 JH |
136 | connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), |
137 | NULL); | |
138 | if (IS_ERR(connection)) { | |
139 | retval = PTR_ERR(connection); | |
140 | goto err_free_vib; | |
141 | } | |
0ec30632 | 142 | gb_connection_set_data(connection, vib); |
ac4029fb | 143 | |
e0deb079 JH |
144 | vib->connection = connection; |
145 | ||
146 | greybus_set_drvdata(bundle, vib); | |
147 | ||
148 | retval = gb_connection_enable(connection); | |
149 | if (retval) | |
150 | goto err_connection_destroy; | |
151 | ||
ac4029fb | 152 | /* |
396671b1 GKH |
153 | * For now we create a device in sysfs for the vibrator, but odds are |
154 | * there is a "real" device somewhere in the kernel for this, but I | |
ac4029fb GKH |
155 | * can't find it at the moment... |
156 | */ | |
6b17492e | 157 | vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); |
396671b1 GKH |
158 | if (vib->minor < 0) { |
159 | retval = vib->minor; | |
e0deb079 | 160 | goto err_connection_disable; |
396671b1 | 161 | } |
e0deb079 | 162 | dev = device_create(&vibrator_class, &bundle->dev, |
5ae2f55b | 163 | MKDEV(0, 0), vib, "vibrator%d", vib->minor); |
ac4029fb GKH |
164 | if (IS_ERR(dev)) { |
165 | retval = -EINVAL; | |
6b17492e | 166 | goto err_ida_remove; |
ac4029fb | 167 | } |
ac4029fb GKH |
168 | vib->dev = dev; |
169 | ||
0900845a AC |
170 | INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker); |
171 | ||
633e45ea AC |
172 | gb_pm_runtime_put_autosuspend(bundle); |
173 | ||
ac4029fb GKH |
174 | return 0; |
175 | ||
6b17492e GKH |
176 | err_ida_remove: |
177 | ida_simple_remove(&minors, vib->minor); | |
e0deb079 JH |
178 | err_connection_disable: |
179 | gb_connection_disable(connection); | |
180 | err_connection_destroy: | |
181 | gb_connection_destroy(connection); | |
182 | err_free_vib: | |
ac4029fb | 183 | kfree(vib); |
e0deb079 | 184 | |
ac4029fb GKH |
185 | return retval; |
186 | } | |
187 | ||
e0deb079 | 188 | static void gb_vibrator_disconnect(struct gb_bundle *bundle) |
ac4029fb | 189 | { |
e0deb079 | 190 | struct gb_vibrator_device *vib = greybus_get_drvdata(bundle); |
633e45ea AC |
191 | int ret; |
192 | ||
193 | ret = gb_pm_runtime_get_sync(bundle); | |
194 | if (ret) | |
195 | gb_pm_runtime_get_noresume(bundle); | |
ac4029fb | 196 | |
0900845a AC |
197 | if (cancel_delayed_work_sync(&vib->delayed_work)) |
198 | turn_off(vib); | |
199 | ||
ac4029fb | 200 | device_unregister(vib->dev); |
d7849bff | 201 | ida_simple_remove(&minors, vib->minor); |
e0deb079 JH |
202 | gb_connection_disable(vib->connection); |
203 | gb_connection_destroy(vib->connection); | |
ac4029fb GKH |
204 | kfree(vib); |
205 | } | |
206 | ||
e0deb079 JH |
207 | static const struct greybus_bundle_id gb_vibrator_id_table[] = { |
208 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) }, | |
209 | { } | |
210 | }; | |
211 | MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table); | |
212 | ||
213 | static struct greybus_driver gb_vibrator_driver = { | |
214 | .name = "vibrator", | |
215 | .probe = gb_vibrator_probe, | |
216 | .disconnect = gb_vibrator_disconnect, | |
217 | .id_table = gb_vibrator_id_table, | |
ac4029fb GKH |
218 | }; |
219 | ||
e0deb079 | 220 | static __init int gb_vibrator_init(void) |
ac4029fb GKH |
221 | { |
222 | int retval; | |
223 | ||
224 | retval = class_register(&vibrator_class); | |
225 | if (retval) | |
226 | return retval; | |
227 | ||
e0deb079 | 228 | retval = greybus_register(&gb_vibrator_driver); |
d4efa688 JH |
229 | if (retval) |
230 | goto err_class_unregister; | |
231 | ||
232 | return 0; | |
233 | ||
234 | err_class_unregister: | |
235 | class_unregister(&vibrator_class); | |
236 | ||
237 | return retval; | |
ac4029fb | 238 | } |
e0deb079 | 239 | module_init(gb_vibrator_init); |
ac4029fb | 240 | |
e0deb079 | 241 | static __exit void gb_vibrator_exit(void) |
ac4029fb | 242 | { |
e0deb079 | 243 | greybus_deregister(&gb_vibrator_driver); |
ac4029fb | 244 | class_unregister(&vibrator_class); |
5c1ac694 | 245 | ida_destroy(&minors); |
ac4029fb | 246 | } |
e0deb079 | 247 | module_exit(gb_vibrator_exit); |
66b676fd GKH |
248 | |
249 | MODULE_LICENSE("GPL v2"); |