1eb50fd3aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 298abb414SViresh Kumar /* 398abb414SViresh Kumar * Greybus Vibrator protocol driver. 498abb414SViresh Kumar * 598abb414SViresh Kumar * Copyright 2014 Google Inc. 698abb414SViresh Kumar * Copyright 2014 Linaro Ltd. 798abb414SViresh Kumar */ 898abb414SViresh Kumar 998abb414SViresh Kumar #include <linux/kernel.h> 1098abb414SViresh Kumar #include <linux/module.h> 1198abb414SViresh Kumar #include <linux/slab.h> 1298abb414SViresh Kumar #include <linux/device.h> 1398abb414SViresh Kumar #include <linux/kdev_t.h> 1498abb414SViresh Kumar #include <linux/idr.h> 15633e45eaSAnn Chen #include <linux/pm_runtime.h> 16ec0ad868SGreg Kroah-Hartman #include <linux/greybus.h> 1798abb414SViresh Kumar 1898abb414SViresh Kumar struct gb_vibrator_device { 1998abb414SViresh Kumar struct gb_connection *connection; 2098abb414SViresh Kumar struct device *dev; 2198abb414SViresh Kumar int minor; /* vibrator minor number */ 220900845aSAnn Chen struct delayed_work delayed_work; 2398abb414SViresh Kumar }; 2498abb414SViresh Kumar 256d653370SAlex Elder /* Greybus Vibrator operation types */ 2698abb414SViresh Kumar #define GB_VIBRATOR_TYPE_ON 0x02 2798abb414SViresh Kumar #define GB_VIBRATOR_TYPE_OFF 0x03 2898abb414SViresh Kumar 2998abb414SViresh Kumar static int turn_off(struct gb_vibrator_device *vib) 3098abb414SViresh Kumar { 31633e45eaSAnn Chen struct gb_bundle *bundle = vib->connection->bundle; 32633e45eaSAnn Chen int ret; 33633e45eaSAnn Chen 34633e45eaSAnn Chen ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, 3598abb414SViresh Kumar NULL, 0, NULL, 0); 36633e45eaSAnn Chen 37633e45eaSAnn Chen gb_pm_runtime_put_autosuspend(bundle); 38633e45eaSAnn Chen 39633e45eaSAnn Chen return ret; 4098abb414SViresh Kumar } 4198abb414SViresh Kumar 420900845aSAnn Chen static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) 430900845aSAnn Chen { 440900845aSAnn Chen struct gb_bundle *bundle = vib->connection->bundle; 450900845aSAnn Chen int ret; 460900845aSAnn Chen 470900845aSAnn Chen ret = gb_pm_runtime_get_sync(bundle); 480900845aSAnn Chen if (ret) 490900845aSAnn Chen return ret; 500900845aSAnn Chen 510900845aSAnn Chen /* Vibrator was switched ON earlier */ 520900845aSAnn Chen if (cancel_delayed_work_sync(&vib->delayed_work)) 530900845aSAnn Chen turn_off(vib); 540900845aSAnn Chen 550900845aSAnn Chen ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, 560900845aSAnn Chen NULL, 0, NULL, 0); 570900845aSAnn Chen if (ret) { 580900845aSAnn Chen gb_pm_runtime_put_autosuspend(bundle); 590900845aSAnn Chen return ret; 600900845aSAnn Chen } 610900845aSAnn Chen 620900845aSAnn Chen schedule_delayed_work(&vib->delayed_work, msecs_to_jiffies(timeout_ms)); 630900845aSAnn Chen 640900845aSAnn Chen return 0; 650900845aSAnn Chen } 660900845aSAnn Chen 670900845aSAnn Chen static void gb_vibrator_worker(struct work_struct *work) 680900845aSAnn Chen { 690900845aSAnn Chen struct delayed_work *delayed_work = to_delayed_work(work); 700900845aSAnn Chen struct gb_vibrator_device *vib = 71461ab807SGioh Kim container_of(delayed_work, 72461ab807SGioh Kim struct gb_vibrator_device, 73461ab807SGioh Kim delayed_work); 740900845aSAnn Chen 750900845aSAnn Chen turn_off(vib); 760900845aSAnn Chen } 770900845aSAnn Chen 7898abb414SViresh Kumar static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, 7998abb414SViresh Kumar const char *buf, size_t count) 8098abb414SViresh Kumar { 8198abb414SViresh Kumar struct gb_vibrator_device *vib = dev_get_drvdata(dev); 8298abb414SViresh Kumar unsigned long val; 8398abb414SViresh Kumar int retval; 8498abb414SViresh Kumar 8598abb414SViresh Kumar retval = kstrtoul(buf, 10, &val); 8698abb414SViresh Kumar if (retval < 0) { 8798abb414SViresh Kumar dev_err(dev, "could not parse timeout value %d\n", retval); 8898abb414SViresh Kumar return retval; 8998abb414SViresh Kumar } 9098abb414SViresh Kumar 9198abb414SViresh Kumar if (val) 9298abb414SViresh Kumar retval = turn_on(vib, (u16)val); 9398abb414SViresh Kumar else 9498abb414SViresh Kumar retval = turn_off(vib); 9598abb414SViresh Kumar if (retval) 9698abb414SViresh Kumar return retval; 9798abb414SViresh Kumar 9898abb414SViresh Kumar return count; 9998abb414SViresh Kumar } 10098abb414SViresh Kumar static DEVICE_ATTR_WO(timeout); 10198abb414SViresh Kumar 10298abb414SViresh Kumar static struct attribute *vibrator_attrs[] = { 10398abb414SViresh Kumar &dev_attr_timeout.attr, 10498abb414SViresh Kumar NULL, 10598abb414SViresh Kumar }; 10698abb414SViresh Kumar ATTRIBUTE_GROUPS(vibrator); 10798abb414SViresh Kumar 10898abb414SViresh Kumar static struct class vibrator_class = { 10998abb414SViresh Kumar .name = "vibrator", 11098abb414SViresh Kumar .owner = THIS_MODULE, 11198abb414SViresh Kumar .dev_groups = vibrator_groups, 11298abb414SViresh Kumar }; 11398abb414SViresh Kumar 1146b17492eSGreg Kroah-Hartman static DEFINE_IDA(minors); 11598abb414SViresh Kumar 116e0deb079SJohan Hovold static int gb_vibrator_probe(struct gb_bundle *bundle, 117e0deb079SJohan Hovold const struct greybus_bundle_id *id) 11898abb414SViresh Kumar { 119e0deb079SJohan Hovold struct greybus_descriptor_cport *cport_desc; 120e0deb079SJohan Hovold struct gb_connection *connection; 12198abb414SViresh Kumar struct gb_vibrator_device *vib; 12298abb414SViresh Kumar struct device *dev; 12398abb414SViresh Kumar int retval; 12498abb414SViresh Kumar 125e0deb079SJohan Hovold if (bundle->num_cports != 1) 126e0deb079SJohan Hovold return -ENODEV; 127e0deb079SJohan Hovold 128e0deb079SJohan Hovold cport_desc = &bundle->cport_desc[0]; 129e0deb079SJohan Hovold if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR) 130e0deb079SJohan Hovold return -ENODEV; 131e0deb079SJohan Hovold 13298abb414SViresh Kumar vib = kzalloc(sizeof(*vib), GFP_KERNEL); 13398abb414SViresh Kumar if (!vib) 13498abb414SViresh Kumar return -ENOMEM; 13598abb414SViresh Kumar 136e0deb079SJohan Hovold connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), 137e0deb079SJohan Hovold NULL); 138e0deb079SJohan Hovold if (IS_ERR(connection)) { 139e0deb079SJohan Hovold retval = PTR_ERR(connection); 140e0deb079SJohan Hovold goto err_free_vib; 141e0deb079SJohan Hovold } 1420ec30632SGreg Kroah-Hartman gb_connection_set_data(connection, vib); 14398abb414SViresh Kumar 144e0deb079SJohan Hovold vib->connection = connection; 145e0deb079SJohan Hovold 146e0deb079SJohan Hovold greybus_set_drvdata(bundle, vib); 147e0deb079SJohan Hovold 148e0deb079SJohan Hovold retval = gb_connection_enable(connection); 149e0deb079SJohan Hovold if (retval) 150e0deb079SJohan Hovold goto err_connection_destroy; 151e0deb079SJohan Hovold 15298abb414SViresh Kumar /* 15398abb414SViresh Kumar * For now we create a device in sysfs for the vibrator, but odds are 15498abb414SViresh Kumar * there is a "real" device somewhere in the kernel for this, but I 15598abb414SViresh Kumar * can't find it at the moment... 15698abb414SViresh Kumar */ 1576b17492eSGreg Kroah-Hartman vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); 15898abb414SViresh Kumar if (vib->minor < 0) { 15998abb414SViresh Kumar retval = vib->minor; 160e0deb079SJohan Hovold goto err_connection_disable; 16198abb414SViresh Kumar } 162e0deb079SJohan Hovold dev = device_create(&vibrator_class, &bundle->dev, 1635ae2f55bSGreg Kroah-Hartman MKDEV(0, 0), vib, "vibrator%d", vib->minor); 16498abb414SViresh Kumar if (IS_ERR(dev)) { 16598abb414SViresh Kumar retval = -EINVAL; 1666b17492eSGreg Kroah-Hartman goto err_ida_remove; 16798abb414SViresh Kumar } 16898abb414SViresh Kumar vib->dev = dev; 16998abb414SViresh Kumar 1700900845aSAnn Chen INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker); 1710900845aSAnn Chen 172633e45eaSAnn Chen gb_pm_runtime_put_autosuspend(bundle); 173633e45eaSAnn Chen 17498abb414SViresh Kumar return 0; 17598abb414SViresh Kumar 1766b17492eSGreg Kroah-Hartman err_ida_remove: 1776b17492eSGreg Kroah-Hartman ida_simple_remove(&minors, vib->minor); 178e0deb079SJohan Hovold err_connection_disable: 179e0deb079SJohan Hovold gb_connection_disable(connection); 180e0deb079SJohan Hovold err_connection_destroy: 181e0deb079SJohan Hovold gb_connection_destroy(connection); 182e0deb079SJohan Hovold err_free_vib: 18398abb414SViresh Kumar kfree(vib); 184e0deb079SJohan Hovold 18598abb414SViresh Kumar return retval; 18698abb414SViresh Kumar } 18798abb414SViresh Kumar 188e0deb079SJohan Hovold static void gb_vibrator_disconnect(struct gb_bundle *bundle) 18998abb414SViresh Kumar { 190e0deb079SJohan Hovold struct gb_vibrator_device *vib = greybus_get_drvdata(bundle); 191633e45eaSAnn Chen int ret; 192633e45eaSAnn Chen 193633e45eaSAnn Chen ret = gb_pm_runtime_get_sync(bundle); 194633e45eaSAnn Chen if (ret) 195633e45eaSAnn Chen gb_pm_runtime_get_noresume(bundle); 19698abb414SViresh Kumar 1970900845aSAnn Chen if (cancel_delayed_work_sync(&vib->delayed_work)) 1980900845aSAnn Chen turn_off(vib); 1990900845aSAnn Chen 20098abb414SViresh Kumar device_unregister(vib->dev); 201d7849bffSJohan Hovold ida_simple_remove(&minors, vib->minor); 202e0deb079SJohan Hovold gb_connection_disable(vib->connection); 203e0deb079SJohan Hovold gb_connection_destroy(vib->connection); 20498abb414SViresh Kumar kfree(vib); 20598abb414SViresh Kumar } 20698abb414SViresh Kumar 207e0deb079SJohan Hovold static const struct greybus_bundle_id gb_vibrator_id_table[] = { 208e0deb079SJohan Hovold { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) }, 209e0deb079SJohan Hovold { } 210e0deb079SJohan Hovold }; 211e0deb079SJohan Hovold MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table); 212e0deb079SJohan Hovold 213e0deb079SJohan Hovold static struct greybus_driver gb_vibrator_driver = { 21498abb414SViresh Kumar .name = "vibrator", 215e0deb079SJohan Hovold .probe = gb_vibrator_probe, 216e0deb079SJohan Hovold .disconnect = gb_vibrator_disconnect, 217e0deb079SJohan Hovold .id_table = gb_vibrator_id_table, 21898abb414SViresh Kumar }; 21998abb414SViresh Kumar 220e0deb079SJohan Hovold static __init int gb_vibrator_init(void) 22198abb414SViresh Kumar { 22298abb414SViresh Kumar int retval; 22398abb414SViresh Kumar 22498abb414SViresh Kumar retval = class_register(&vibrator_class); 22598abb414SViresh Kumar if (retval) 22698abb414SViresh Kumar return retval; 22798abb414SViresh Kumar 228e0deb079SJohan Hovold retval = greybus_register(&gb_vibrator_driver); 229d4efa688SJohan Hovold if (retval) 230d4efa688SJohan Hovold goto err_class_unregister; 231d4efa688SJohan Hovold 232d4efa688SJohan Hovold return 0; 233d4efa688SJohan Hovold 234d4efa688SJohan Hovold err_class_unregister: 235d4efa688SJohan Hovold class_unregister(&vibrator_class); 236d4efa688SJohan Hovold 237d4efa688SJohan Hovold return retval; 23898abb414SViresh Kumar } 239e0deb079SJohan Hovold module_init(gb_vibrator_init); 24098abb414SViresh Kumar 241e0deb079SJohan Hovold static __exit void gb_vibrator_exit(void) 24298abb414SViresh Kumar { 243e0deb079SJohan Hovold greybus_deregister(&gb_vibrator_driver); 24498abb414SViresh Kumar class_unregister(&vibrator_class); 2455c1ac694SGreg Kroah-Hartman ida_destroy(&minors); 24698abb414SViresh Kumar } 247e0deb079SJohan Hovold module_exit(gb_vibrator_exit); 24898abb414SViresh Kumar 24998abb414SViresh Kumar MODULE_LICENSE("GPL v2"); 250