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 | |
18 | struct 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 | |
29 | static 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 | |
42 | static 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 | |
67 | static 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 | |
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(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 | } |
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 | .dev_groups = vibrator_groups, |
111 | }; |
112 | |
113 | static DEFINE_IDA(minors); |
114 | |
115 | static 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 | |
175 | err_ida_remove: |
176 | ida_free(&minors, id: vib->minor); |
177 | err_connection_disable: |
178 | gb_connection_disable(connection); |
179 | err_connection_destroy: |
180 | gb_connection_destroy(connection); |
181 | err_free_vib: |
182 | kfree(objp: vib); |
183 | |
184 | return retval; |
185 | } |
186 | |
187 | static 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 | |
206 | static const struct greybus_bundle_id gb_vibrator_id_table[] = { |
207 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) }, |
208 | { } |
209 | }; |
210 | MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table); |
211 | |
212 | static 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 | |
219 | static __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 | |
233 | err_class_unregister: |
234 | class_unregister(class: &vibrator_class); |
235 | |
236 | return retval; |
237 | } |
238 | module_init(gb_vibrator_init); |
239 | |
240 | static __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 | } |
246 | module_exit(gb_vibrator_exit); |
247 | |
248 | MODULE_LICENSE("GPL v2" ); |
249 | |