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
turn_off(struct gb_vibrator_device * vib)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
turn_on(struct gb_vibrator_device * vib,u16 timeout_ms)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
gb_vibrator_worker(struct work_struct * work)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
timeout_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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 .dev_groups = vibrator_groups,
11198abb414SViresh Kumar };
11298abb414SViresh Kumar
1136b17492eSGreg Kroah-Hartman static DEFINE_IDA(minors);
11498abb414SViresh Kumar
gb_vibrator_probe(struct gb_bundle * bundle,const struct greybus_bundle_id * id)115e0deb079SJohan Hovold static int gb_vibrator_probe(struct gb_bundle *bundle,
116e0deb079SJohan Hovold const struct greybus_bundle_id *id)
11798abb414SViresh Kumar {
118e0deb079SJohan Hovold struct greybus_descriptor_cport *cport_desc;
119e0deb079SJohan Hovold struct gb_connection *connection;
12098abb414SViresh Kumar struct gb_vibrator_device *vib;
12198abb414SViresh Kumar struct device *dev;
12298abb414SViresh Kumar int retval;
12398abb414SViresh Kumar
124e0deb079SJohan Hovold if (bundle->num_cports != 1)
125e0deb079SJohan Hovold return -ENODEV;
126e0deb079SJohan Hovold
127e0deb079SJohan Hovold cport_desc = &bundle->cport_desc[0];
128e0deb079SJohan Hovold if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR)
129e0deb079SJohan Hovold return -ENODEV;
130e0deb079SJohan Hovold
13198abb414SViresh Kumar vib = kzalloc(sizeof(*vib), GFP_KERNEL);
13298abb414SViresh Kumar if (!vib)
13398abb414SViresh Kumar return -ENOMEM;
13498abb414SViresh Kumar
135e0deb079SJohan Hovold connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
136e0deb079SJohan Hovold NULL);
137e0deb079SJohan Hovold if (IS_ERR(connection)) {
138e0deb079SJohan Hovold retval = PTR_ERR(connection);
139e0deb079SJohan Hovold goto err_free_vib;
140e0deb079SJohan Hovold }
1410ec30632SGreg Kroah-Hartman gb_connection_set_data(connection, vib);
14298abb414SViresh Kumar
143e0deb079SJohan Hovold vib->connection = connection;
144e0deb079SJohan Hovold
145e0deb079SJohan Hovold greybus_set_drvdata(bundle, vib);
146e0deb079SJohan Hovold
147e0deb079SJohan Hovold retval = gb_connection_enable(connection);
148e0deb079SJohan Hovold if (retval)
149e0deb079SJohan Hovold goto err_connection_destroy;
150e0deb079SJohan Hovold
15198abb414SViresh Kumar /*
15298abb414SViresh Kumar * For now we create a device in sysfs for the vibrator, but odds are
15398abb414SViresh Kumar * there is a "real" device somewhere in the kernel for this, but I
15498abb414SViresh Kumar * can't find it at the moment...
15598abb414SViresh Kumar */
1566b17492eSGreg Kroah-Hartman vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
15798abb414SViresh Kumar if (vib->minor < 0) {
15898abb414SViresh Kumar retval = vib->minor;
159e0deb079SJohan Hovold goto err_connection_disable;
16098abb414SViresh Kumar }
161e0deb079SJohan Hovold dev = device_create(&vibrator_class, &bundle->dev,
1625ae2f55bSGreg Kroah-Hartman MKDEV(0, 0), vib, "vibrator%d", vib->minor);
16398abb414SViresh Kumar if (IS_ERR(dev)) {
16498abb414SViresh Kumar retval = -EINVAL;
1656b17492eSGreg Kroah-Hartman goto err_ida_remove;
16698abb414SViresh Kumar }
16798abb414SViresh Kumar vib->dev = dev;
16898abb414SViresh Kumar
1690900845aSAnn Chen INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker);
1700900845aSAnn Chen
171633e45eaSAnn Chen gb_pm_runtime_put_autosuspend(bundle);
172633e45eaSAnn Chen
17398abb414SViresh Kumar return 0;
17498abb414SViresh Kumar
1756b17492eSGreg Kroah-Hartman err_ida_remove:
1766b17492eSGreg Kroah-Hartman ida_simple_remove(&minors, vib->minor);
177e0deb079SJohan Hovold err_connection_disable:
178e0deb079SJohan Hovold gb_connection_disable(connection);
179e0deb079SJohan Hovold err_connection_destroy:
180e0deb079SJohan Hovold gb_connection_destroy(connection);
181e0deb079SJohan Hovold err_free_vib:
18298abb414SViresh Kumar kfree(vib);
183e0deb079SJohan Hovold
18498abb414SViresh Kumar return retval;
18598abb414SViresh Kumar }
18698abb414SViresh Kumar
gb_vibrator_disconnect(struct gb_bundle * bundle)187e0deb079SJohan Hovold static void gb_vibrator_disconnect(struct gb_bundle *bundle)
18898abb414SViresh Kumar {
189e0deb079SJohan Hovold struct gb_vibrator_device *vib = greybus_get_drvdata(bundle);
190633e45eaSAnn Chen int ret;
191633e45eaSAnn Chen
192633e45eaSAnn Chen ret = gb_pm_runtime_get_sync(bundle);
193633e45eaSAnn Chen if (ret)
194633e45eaSAnn Chen gb_pm_runtime_get_noresume(bundle);
19598abb414SViresh Kumar
1960900845aSAnn Chen if (cancel_delayed_work_sync(&vib->delayed_work))
1970900845aSAnn Chen turn_off(vib);
1980900845aSAnn Chen
19998abb414SViresh Kumar device_unregister(vib->dev);
200d7849bffSJohan Hovold ida_simple_remove(&minors, vib->minor);
201e0deb079SJohan Hovold gb_connection_disable(vib->connection);
202e0deb079SJohan Hovold gb_connection_destroy(vib->connection);
20398abb414SViresh Kumar kfree(vib);
20498abb414SViresh Kumar }
20598abb414SViresh Kumar
206e0deb079SJohan Hovold static const struct greybus_bundle_id gb_vibrator_id_table[] = {
207e0deb079SJohan Hovold { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) },
208e0deb079SJohan Hovold { }
209e0deb079SJohan Hovold };
210e0deb079SJohan Hovold MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table);
211e0deb079SJohan Hovold
212e0deb079SJohan Hovold static struct greybus_driver gb_vibrator_driver = {
21398abb414SViresh Kumar .name = "vibrator",
214e0deb079SJohan Hovold .probe = gb_vibrator_probe,
215e0deb079SJohan Hovold .disconnect = gb_vibrator_disconnect,
216e0deb079SJohan Hovold .id_table = gb_vibrator_id_table,
21798abb414SViresh Kumar };
21898abb414SViresh Kumar
gb_vibrator_init(void)219e0deb079SJohan Hovold static __init int gb_vibrator_init(void)
22098abb414SViresh Kumar {
22198abb414SViresh Kumar int retval;
22298abb414SViresh Kumar
22398abb414SViresh Kumar retval = class_register(&vibrator_class);
22498abb414SViresh Kumar if (retval)
22598abb414SViresh Kumar return retval;
22698abb414SViresh Kumar
227e0deb079SJohan Hovold retval = greybus_register(&gb_vibrator_driver);
228d4efa688SJohan Hovold if (retval)
229d4efa688SJohan Hovold goto err_class_unregister;
230d4efa688SJohan Hovold
231d4efa688SJohan Hovold return 0;
232d4efa688SJohan Hovold
233d4efa688SJohan Hovold err_class_unregister:
234d4efa688SJohan Hovold class_unregister(&vibrator_class);
235d4efa688SJohan Hovold
236d4efa688SJohan Hovold return retval;
23798abb414SViresh Kumar }
238e0deb079SJohan Hovold module_init(gb_vibrator_init);
23998abb414SViresh Kumar
gb_vibrator_exit(void)240e0deb079SJohan Hovold static __exit void gb_vibrator_exit(void)
24198abb414SViresh Kumar {
242e0deb079SJohan Hovold greybus_deregister(&gb_vibrator_driver);
24398abb414SViresh Kumar class_unregister(&vibrator_class);
2445c1ac694SGreg Kroah-Hartman ida_destroy(&minors);
24598abb414SViresh Kumar }
246e0deb079SJohan Hovold module_exit(gb_vibrator_exit);
24798abb414SViresh Kumar
24898abb414SViresh Kumar MODULE_LICENSE("GPL v2");
249