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