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

source code of linux/drivers/staging/greybus/vibrator.c