blob: 7296a4dd0a0cbb10f07f560dece70f1f118275ae [file] [log] [blame]
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -08001/*
Greg Kroah-Hartman4efe6062014-11-17 16:55:54 -08002 * Greybus Vibrator protocol driver.
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -08003 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 Linaro Ltd.
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -08006 *
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>
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -080015#include <linux/idr.h>
Ann Chen633e45e2016-07-22 15:33:55 +080016#include <linux/pm_runtime.h>
17
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080018#include "greybus.h"
19
20struct gb_vibrator_device {
21 struct gb_connection *connection;
22 struct device *dev;
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -080023 int minor; /* vibrator minor number */
Ann Chen0900845a2016-08-17 16:38:56 +080024 struct delayed_work delayed_work;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080025};
26
Alex Elder6d653372015-05-07 13:03:52 -050027/* Greybus Vibrator operation types */
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080028#define GB_VIBRATOR_TYPE_ON 0x02
29#define GB_VIBRATOR_TYPE_OFF 0x03
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080030
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080031static int turn_off(struct gb_vibrator_device *vib)
32{
Ann Chen633e45e2016-07-22 15:33:55 +080033 struct gb_bundle *bundle = vib->connection->bundle;
34 int ret;
35
Ann Chen633e45e2016-07-22 15:33:55 +080036 ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF,
37 NULL, 0, NULL, 0);
38
39 gb_pm_runtime_put_autosuspend(bundle);
40
41 return ret;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080042}
43
Ann Chen0900845a2016-08-17 16:38:56 +080044static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
45{
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
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,
58 NULL, 0, NULL, 0);
59 if (ret) {
60 gb_pm_runtime_put_autosuspend(bundle);
61 return ret;
62 }
63
64 schedule_delayed_work(&vib->delayed_work, msecs_to_jiffies(timeout_ms));
65
66 return 0;
67}
68
69static 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);
76}
77
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080078static 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
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -080091 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}
100static DEVICE_ATTR_WO(timeout);
101
102static struct attribute *vibrator_attrs[] = {
103 &dev_attr_timeout.attr,
104 NULL,
105};
106ATTRIBUTE_GROUPS(vibrator);
107
108static struct class vibrator_class = {
109 .name = "vibrator",
110 .owner = THIS_MODULE,
111#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
112 .dev_groups = vibrator_groups,
113#endif
114};
115
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700116static DEFINE_IDA(minors);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800117
Johan Hovolde0deb072016-01-21 17:34:24 +0100118static int gb_vibrator_probe(struct gb_bundle *bundle,
119 const struct greybus_bundle_id *id)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800120{
Johan Hovolde0deb072016-01-21 17:34:24 +0100121 struct greybus_descriptor_cport *cport_desc;
122 struct gb_connection *connection;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800123 struct gb_vibrator_device *vib;
124 struct device *dev;
125 int retval;
126
Johan Hovolde0deb072016-01-21 17:34:24 +0100127 if (bundle->num_cports != 1)
128 return -ENODEV;
129
130 cport_desc = &bundle->cport_desc[0];
131 if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR)
132 return -ENODEV;
133
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800134 vib = kzalloc(sizeof(*vib), GFP_KERNEL);
135 if (!vib)
136 return -ENOMEM;
137
Johan Hovolde0deb072016-01-21 17:34:24 +0100138 connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
139 NULL);
140 if (IS_ERR(connection)) {
141 retval = PTR_ERR(connection);
142 goto err_free_vib;
143 }
Greg Kroah-Hartman0ec30632016-03-22 14:30:35 -0400144 gb_connection_set_data(connection, vib);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800145
Johan Hovolde0deb072016-01-21 17:34:24 +0100146 vib->connection = connection;
147
148 greybus_set_drvdata(bundle, vib);
149
150 retval = gb_connection_enable(connection);
151 if (retval)
152 goto err_connection_destroy;
153
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800154 /*
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -0800155 * For now we create a device in sysfs for the vibrator, but odds are
156 * there is a "real" device somewhere in the kernel for this, but I
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800157 * can't find it at the moment...
158 */
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700159 vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -0800160 if (vib->minor < 0) {
161 retval = vib->minor;
Johan Hovolde0deb072016-01-21 17:34:24 +0100162 goto err_connection_disable;
Greg Kroah-Hartman396671b2014-11-17 17:12:50 -0800163 }
Johan Hovolde0deb072016-01-21 17:34:24 +0100164 dev = device_create(&vibrator_class, &bundle->dev,
Greg Kroah-Hartman5ae2f552015-10-14 11:12:06 -0700165 MKDEV(0, 0), vib, "vibrator%d", vib->minor);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800166 if (IS_ERR(dev)) {
167 retval = -EINVAL;
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700168 goto err_ida_remove;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800169 }
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800170 vib->dev = dev;
171
172#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
173 /*
Greg Kroah-Hartman4efe6062014-11-17 16:55:54 -0800174 * Newer kernels handle this in a race-free manner, by the dev_groups
175 * field in the struct class up above. But for older kernels, we need
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800176 * to "open code this :(
177 */
178 retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]);
179 if (retval) {
180 device_unregister(dev);
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700181 goto err_ida_remove;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800182 }
183#endif
184
Ann Chen0900845a2016-08-17 16:38:56 +0800185 INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker);
186
Ann Chen633e45e2016-07-22 15:33:55 +0800187 gb_pm_runtime_put_autosuspend(bundle);
188
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800189 return 0;
190
Greg Kroah-Hartman6b174922015-05-14 10:39:35 -0700191err_ida_remove:
192 ida_simple_remove(&minors, vib->minor);
Johan Hovolde0deb072016-01-21 17:34:24 +0100193err_connection_disable:
194 gb_connection_disable(connection);
195err_connection_destroy:
196 gb_connection_destroy(connection);
197err_free_vib:
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800198 kfree(vib);
Johan Hovolde0deb072016-01-21 17:34:24 +0100199
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800200 return retval;
201}
202
Johan Hovolde0deb072016-01-21 17:34:24 +0100203static void gb_vibrator_disconnect(struct gb_bundle *bundle)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800204{
Johan Hovolde0deb072016-01-21 17:34:24 +0100205 struct gb_vibrator_device *vib = greybus_get_drvdata(bundle);
Ann Chen633e45e2016-07-22 15:33:55 +0800206 int ret;
207
208 ret = gb_pm_runtime_get_sync(bundle);
209 if (ret)
210 gb_pm_runtime_get_noresume(bundle);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800211
Ann Chen0900845a2016-08-17 16:38:56 +0800212 if (cancel_delayed_work_sync(&vib->delayed_work))
213 turn_off(vib);
214
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800215#if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
216 sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]);
217#endif
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800218 device_unregister(vib->dev);
Johan Hovoldd7849bf2015-09-14 20:19:02 +0200219 ida_simple_remove(&minors, vib->minor);
Johan Hovolde0deb072016-01-21 17:34:24 +0100220 gb_connection_disable(vib->connection);
221 gb_connection_destroy(vib->connection);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800222 kfree(vib);
223}
224
Johan Hovolde0deb072016-01-21 17:34:24 +0100225static const struct greybus_bundle_id gb_vibrator_id_table[] = {
226 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) },
227 { }
228};
229MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table);
230
231static struct greybus_driver gb_vibrator_driver = {
232 .name = "vibrator",
233 .probe = gb_vibrator_probe,
234 .disconnect = gb_vibrator_disconnect,
235 .id_table = gb_vibrator_id_table,
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800236};
237
Johan Hovolde0deb072016-01-21 17:34:24 +0100238static __init int gb_vibrator_init(void)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800239{
240 int retval;
241
242 retval = class_register(&vibrator_class);
243 if (retval)
244 return retval;
245
Johan Hovolde0deb072016-01-21 17:34:24 +0100246 retval = greybus_register(&gb_vibrator_driver);
Johan Hovoldd4efa682015-10-13 19:10:27 +0200247 if (retval)
248 goto err_class_unregister;
249
250 return 0;
251
252err_class_unregister:
253 class_unregister(&vibrator_class);
254
255 return retval;
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800256}
Johan Hovolde0deb072016-01-21 17:34:24 +0100257module_init(gb_vibrator_init);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800258
Johan Hovolde0deb072016-01-21 17:34:24 +0100259static __exit void gb_vibrator_exit(void)
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800260{
Johan Hovolde0deb072016-01-21 17:34:24 +0100261 greybus_deregister(&gb_vibrator_driver);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800262 class_unregister(&vibrator_class);
Greg Kroah-Hartman5c1ac692015-07-08 10:44:09 -0700263 ida_destroy(&minors);
Greg Kroah-Hartmanac4029f2014-11-17 16:03:34 -0800264}
Johan Hovolde0deb072016-01-21 17:34:24 +0100265module_exit(gb_vibrator_exit);
Greg Kroah-Hartman66b676f2014-12-24 13:01:41 -0800266
267MODULE_LICENSE("GPL v2");