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