198abb414SViresh Kumar /*
298abb414SViresh Kumar  * Greybus Vibrator protocol driver.
398abb414SViresh Kumar  *
498abb414SViresh Kumar  * Copyright 2014 Google Inc.
598abb414SViresh Kumar  * Copyright 2014 Linaro Ltd.
698abb414SViresh Kumar  *
798abb414SViresh Kumar  * Released under the GPLv2 only.
898abb414SViresh Kumar  */
998abb414SViresh Kumar 
1098abb414SViresh Kumar #include <linux/kernel.h>
1198abb414SViresh Kumar #include <linux/module.h>
1298abb414SViresh Kumar #include <linux/slab.h>
1398abb414SViresh Kumar #include <linux/device.h>
1498abb414SViresh Kumar #include <linux/kdev_t.h>
1598abb414SViresh Kumar #include <linux/idr.h>
1698abb414SViresh Kumar #include "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 */
2298abb414SViresh Kumar };
2398abb414SViresh Kumar 
2498abb414SViresh Kumar /* Version of the Greybus vibrator protocol we support */
2598abb414SViresh Kumar #define	GB_VIBRATOR_VERSION_MAJOR		0x00
2698abb414SViresh Kumar #define	GB_VIBRATOR_VERSION_MINOR		0x01
2798abb414SViresh Kumar 
286d653370SAlex Elder /* Greybus Vibrator operation types */
2998abb414SViresh Kumar #define	GB_VIBRATOR_TYPE_ON			0x02
3098abb414SViresh Kumar #define	GB_VIBRATOR_TYPE_OFF			0x03
3198abb414SViresh Kumar 
3298abb414SViresh Kumar struct gb_vibrator_on_request {
3398abb414SViresh Kumar 	__le16	timeout_ms;
3498abb414SViresh Kumar };
3598abb414SViresh Kumar 
3698abb414SViresh Kumar static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
3798abb414SViresh Kumar {
3898abb414SViresh Kumar 	struct gb_vibrator_on_request request;
3998abb414SViresh Kumar 
4098abb414SViresh Kumar 	request.timeout_ms = cpu_to_le16(timeout_ms);
4198abb414SViresh Kumar 	return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON,
4298abb414SViresh Kumar 				 &request, sizeof(request), NULL, 0);
4398abb414SViresh Kumar }
4498abb414SViresh Kumar 
4598abb414SViresh Kumar static int turn_off(struct gb_vibrator_device *vib)
4698abb414SViresh Kumar {
4798abb414SViresh Kumar 	return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF,
4898abb414SViresh Kumar 				 NULL, 0, NULL, 0);
4998abb414SViresh Kumar }
5098abb414SViresh Kumar 
5198abb414SViresh Kumar static ssize_t timeout_store(struct device *dev, struct device_attribute *attr,
5298abb414SViresh Kumar 			     const char *buf, size_t count)
5398abb414SViresh Kumar {
5498abb414SViresh Kumar 	struct gb_vibrator_device *vib = dev_get_drvdata(dev);
5598abb414SViresh Kumar 	unsigned long val;
5698abb414SViresh Kumar 	int retval;
5798abb414SViresh Kumar 
5898abb414SViresh Kumar 	retval = kstrtoul(buf, 10, &val);
5998abb414SViresh Kumar 	if (retval < 0) {
6098abb414SViresh Kumar 		dev_err(dev, "could not parse timeout value %d\n", retval);
6198abb414SViresh Kumar 		return retval;
6298abb414SViresh Kumar 	}
6398abb414SViresh Kumar 
6498abb414SViresh Kumar 	if (val)
6598abb414SViresh Kumar 		retval = turn_on(vib, (u16)val);
6698abb414SViresh Kumar 	else
6798abb414SViresh Kumar 		retval = turn_off(vib);
6898abb414SViresh Kumar 	if (retval)
6998abb414SViresh Kumar 		return retval;
7098abb414SViresh Kumar 
7198abb414SViresh Kumar 	return count;
7298abb414SViresh Kumar }
7398abb414SViresh Kumar static DEVICE_ATTR_WO(timeout);
7498abb414SViresh Kumar 
7598abb414SViresh Kumar static struct attribute *vibrator_attrs[] = {
7698abb414SViresh Kumar 	&dev_attr_timeout.attr,
7798abb414SViresh Kumar 	NULL,
7898abb414SViresh Kumar };
7998abb414SViresh Kumar ATTRIBUTE_GROUPS(vibrator);
8098abb414SViresh Kumar 
8198abb414SViresh Kumar static struct class vibrator_class = {
8298abb414SViresh Kumar 	.name		= "vibrator",
8398abb414SViresh Kumar 	.owner		= THIS_MODULE,
8498abb414SViresh Kumar #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
8598abb414SViresh Kumar 	.dev_groups	= vibrator_groups,
8698abb414SViresh Kumar #endif
8798abb414SViresh Kumar };
8898abb414SViresh Kumar 
896b17492eSGreg Kroah-Hartman static DEFINE_IDA(minors);
9098abb414SViresh Kumar 
9198abb414SViresh Kumar static int gb_vibrator_connection_init(struct gb_connection *connection)
9298abb414SViresh Kumar {
9398abb414SViresh Kumar 	struct gb_vibrator_device *vib;
9498abb414SViresh Kumar 	struct device *dev;
9598abb414SViresh Kumar 	int retval;
9698abb414SViresh Kumar 
9798abb414SViresh Kumar 	vib = kzalloc(sizeof(*vib), GFP_KERNEL);
9898abb414SViresh Kumar 	if (!vib)
9998abb414SViresh Kumar 		return -ENOMEM;
10098abb414SViresh Kumar 
10198abb414SViresh Kumar 	vib->connection = connection;
10298abb414SViresh Kumar 	connection->private = vib;
10398abb414SViresh Kumar 
10498abb414SViresh Kumar 	/*
10598abb414SViresh Kumar 	 * For now we create a device in sysfs for the vibrator, but odds are
10698abb414SViresh Kumar 	 * there is a "real" device somewhere in the kernel for this, but I
10798abb414SViresh Kumar 	 * can't find it at the moment...
10898abb414SViresh Kumar 	 */
1096b17492eSGreg Kroah-Hartman 	vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
11098abb414SViresh Kumar 	if (vib->minor < 0) {
11198abb414SViresh Kumar 		retval = vib->minor;
11298abb414SViresh Kumar 		goto error;
11398abb414SViresh Kumar 	}
11498abb414SViresh Kumar 	dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib,
11598abb414SViresh Kumar 			    "vibrator%d", vib->minor);
11698abb414SViresh Kumar 	if (IS_ERR(dev)) {
11798abb414SViresh Kumar 		retval = -EINVAL;
1186b17492eSGreg Kroah-Hartman 		goto err_ida_remove;
11998abb414SViresh Kumar 	}
12098abb414SViresh Kumar 	vib->dev = dev;
12198abb414SViresh Kumar 
12298abb414SViresh Kumar #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
12398abb414SViresh Kumar 	/*
12498abb414SViresh Kumar 	 * Newer kernels handle this in a race-free manner, by the dev_groups
12598abb414SViresh Kumar 	 * field in the struct class up above.  But for older kernels, we need
12698abb414SViresh Kumar 	 * to "open code this :(
12798abb414SViresh Kumar 	 */
12898abb414SViresh Kumar 	retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]);
12998abb414SViresh Kumar 	if (retval) {
13098abb414SViresh Kumar 		device_unregister(dev);
1316b17492eSGreg Kroah-Hartman 		goto err_ida_remove;
13298abb414SViresh Kumar 	}
13398abb414SViresh Kumar #endif
13498abb414SViresh Kumar 
13598abb414SViresh Kumar 	return 0;
13698abb414SViresh Kumar 
1376b17492eSGreg Kroah-Hartman err_ida_remove:
1386b17492eSGreg Kroah-Hartman 	ida_simple_remove(&minors, vib->minor);
13998abb414SViresh Kumar error:
14098abb414SViresh Kumar 	kfree(vib);
14198abb414SViresh Kumar 	return retval;
14298abb414SViresh Kumar }
14398abb414SViresh Kumar 
14498abb414SViresh Kumar static void gb_vibrator_connection_exit(struct gb_connection *connection)
14598abb414SViresh Kumar {
14698abb414SViresh Kumar 	struct gb_vibrator_device *vib = connection->private;
14798abb414SViresh Kumar 
14898abb414SViresh Kumar #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
14998abb414SViresh Kumar 	sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]);
15098abb414SViresh Kumar #endif
15198abb414SViresh Kumar 	device_unregister(vib->dev);
152d7849bffSJohan Hovold 	ida_simple_remove(&minors, vib->minor);
15398abb414SViresh Kumar 	kfree(vib);
15498abb414SViresh Kumar }
15598abb414SViresh Kumar 
15698abb414SViresh Kumar static struct gb_protocol vibrator_protocol = {
15798abb414SViresh Kumar 	.name			= "vibrator",
15898abb414SViresh Kumar 	.id			= GREYBUS_PROTOCOL_VIBRATOR,
159f0a1698fSViresh Kumar 	.major			= GB_VIBRATOR_VERSION_MAJOR,
160f0a1698fSViresh Kumar 	.minor			= GB_VIBRATOR_VERSION_MINOR,
16198abb414SViresh Kumar 	.connection_init	= gb_vibrator_connection_init,
16298abb414SViresh Kumar 	.connection_exit	= gb_vibrator_connection_exit,
16398abb414SViresh Kumar 	.request_recv		= NULL,	/* no incoming requests */
16498abb414SViresh Kumar };
16598abb414SViresh Kumar 
16698abb414SViresh Kumar static __init int protocol_init(void)
16798abb414SViresh Kumar {
16898abb414SViresh Kumar 	int retval;
16998abb414SViresh Kumar 
17098abb414SViresh Kumar 	retval = class_register(&vibrator_class);
17198abb414SViresh Kumar 	if (retval)
17298abb414SViresh Kumar 		return retval;
17398abb414SViresh Kumar 
174d4efa688SJohan Hovold 	retval = gb_protocol_register(&vibrator_protocol);
175d4efa688SJohan Hovold 	if (retval)
176d4efa688SJohan Hovold 		goto err_class_unregister;
177d4efa688SJohan Hovold 
178d4efa688SJohan Hovold 	return 0;
179d4efa688SJohan Hovold 
180d4efa688SJohan Hovold err_class_unregister:
181d4efa688SJohan Hovold 	class_unregister(&vibrator_class);
182d4efa688SJohan Hovold 
183d4efa688SJohan Hovold 	return retval;
18498abb414SViresh Kumar }
1850d34be75SViresh Kumar module_init(protocol_init);
18698abb414SViresh Kumar 
18798abb414SViresh Kumar static __exit void protocol_exit(void)
18898abb414SViresh Kumar {
18998abb414SViresh Kumar 	gb_protocol_deregister(&vibrator_protocol);
19098abb414SViresh Kumar 	class_unregister(&vibrator_class);
1915c1ac694SGreg Kroah-Hartman 	ida_destroy(&minors);
19298abb414SViresh Kumar }
19398abb414SViresh Kumar module_exit(protocol_exit);
19498abb414SViresh Kumar 
19598abb414SViresh Kumar MODULE_LICENSE("GPL v2");
196