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(vib->connection, GB_VIBRATOR_TYPE_OFF, 35 NULL, 0, NULL, 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(&vib->delayed_work)) 53 turn_off(vib); 54 55 ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, 56 NULL, 0, NULL, 0); 57 if (ret) { 58 gb_pm_runtime_put_autosuspend(bundle); 59 return ret; 60 } 61 62 schedule_delayed_work(&vib->delayed_work, msecs_to_jiffies(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(buf, 10, &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, (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, 111 .dev_groups = vibrator_groups, 112 }; 113 114 static DEFINE_IDA(minors); 115 116 static int gb_vibrator_probe(struct gb_bundle *bundle, 117 const struct greybus_bundle_id *id) 118 { 119 struct greybus_descriptor_cport *cport_desc; 120 struct gb_connection *connection; 121 struct gb_vibrator_device *vib; 122 struct device *dev; 123 int retval; 124 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 132 vib = kzalloc(sizeof(*vib), GFP_KERNEL); 133 if (!vib) 134 return -ENOMEM; 135 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 } 142 gb_connection_set_data(connection, vib); 143 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 152 /* 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 155 * can't find it at the moment... 156 */ 157 vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); 158 if (vib->minor < 0) { 159 retval = vib->minor; 160 goto err_connection_disable; 161 } 162 dev = device_create(&vibrator_class, &bundle->dev, 163 MKDEV(0, 0), vib, "vibrator%d", vib->minor); 164 if (IS_ERR(dev)) { 165 retval = -EINVAL; 166 goto err_ida_remove; 167 } 168 vib->dev = dev; 169 170 INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker); 171 172 gb_pm_runtime_put_autosuspend(bundle); 173 174 return 0; 175 176 err_ida_remove: 177 ida_simple_remove(&minors, vib->minor); 178 err_connection_disable: 179 gb_connection_disable(connection); 180 err_connection_destroy: 181 gb_connection_destroy(connection); 182 err_free_vib: 183 kfree(vib); 184 185 return retval; 186 } 187 188 static void gb_vibrator_disconnect(struct gb_bundle *bundle) 189 { 190 struct gb_vibrator_device *vib = greybus_get_drvdata(bundle); 191 int ret; 192 193 ret = gb_pm_runtime_get_sync(bundle); 194 if (ret) 195 gb_pm_runtime_get_noresume(bundle); 196 197 if (cancel_delayed_work_sync(&vib->delayed_work)) 198 turn_off(vib); 199 200 device_unregister(vib->dev); 201 ida_simple_remove(&minors, vib->minor); 202 gb_connection_disable(vib->connection); 203 gb_connection_destroy(vib->connection); 204 kfree(vib); 205 } 206 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, 218 }; 219 220 static __init int gb_vibrator_init(void) 221 { 222 int retval; 223 224 retval = class_register(&vibrator_class); 225 if (retval) 226 return retval; 227 228 retval = greybus_register(&gb_vibrator_driver); 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; 238 } 239 module_init(gb_vibrator_init); 240 241 static __exit void gb_vibrator_exit(void) 242 { 243 greybus_deregister(&gb_vibrator_driver); 244 class_unregister(&vibrator_class); 245 ida_destroy(&minors); 246 } 247 module_exit(gb_vibrator_exit); 248 249 MODULE_LICENSE("GPL v2"); 250