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 	u8			version_major;
2398abb414SViresh Kumar 	u8			version_minor;
2498abb414SViresh Kumar };
2598abb414SViresh Kumar 
2698abb414SViresh Kumar /* Version of the Greybus vibrator protocol we support */
2798abb414SViresh Kumar #define	GB_VIBRATOR_VERSION_MAJOR		0x00
2898abb414SViresh Kumar #define	GB_VIBRATOR_VERSION_MINOR		0x01
2998abb414SViresh Kumar 
306d653370SAlex Elder /* Greybus Vibrator operation types */
3198abb414SViresh Kumar #define	GB_VIBRATOR_TYPE_INVALID		0x00
3298abb414SViresh Kumar #define	GB_VIBRATOR_TYPE_PROTOCOL_VERSION	0x01
3398abb414SViresh Kumar #define	GB_VIBRATOR_TYPE_ON			0x02
3498abb414SViresh Kumar #define	GB_VIBRATOR_TYPE_OFF			0x03
3598abb414SViresh Kumar 
3698abb414SViresh Kumar struct gb_vibrator_on_request {
3798abb414SViresh Kumar 	__le16	timeout_ms;
3898abb414SViresh Kumar };
3998abb414SViresh Kumar 
4036e79decSViresh Kumar /* Define get_version() routine */
4136e79decSViresh Kumar define_get_version(gb_vibrator_device, VIBRATOR);
4298abb414SViresh Kumar 
4398abb414SViresh Kumar static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms)
4498abb414SViresh Kumar {
4598abb414SViresh Kumar 	struct gb_vibrator_on_request request;
4698abb414SViresh Kumar 
4798abb414SViresh Kumar 	request.timeout_ms = cpu_to_le16(timeout_ms);
4898abb414SViresh Kumar 	return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON,
4998abb414SViresh Kumar 				 &request, sizeof(request), NULL, 0);
5098abb414SViresh Kumar }
5198abb414SViresh Kumar 
5298abb414SViresh Kumar static int turn_off(struct gb_vibrator_device *vib)
5398abb414SViresh Kumar {
5498abb414SViresh Kumar 	return gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF,
5598abb414SViresh Kumar 				 NULL, 0, NULL, 0);
5698abb414SViresh Kumar }
5798abb414SViresh Kumar 
5898abb414SViresh Kumar static ssize_t timeout_store(struct device *dev, struct device_attribute *attr,
5998abb414SViresh Kumar 			     const char *buf, size_t count)
6098abb414SViresh Kumar {
6198abb414SViresh Kumar 	struct gb_vibrator_device *vib = dev_get_drvdata(dev);
6298abb414SViresh Kumar 	unsigned long val;
6398abb414SViresh Kumar 	int retval;
6498abb414SViresh Kumar 
6598abb414SViresh Kumar 	retval = kstrtoul(buf, 10, &val);
6698abb414SViresh Kumar 	if (retval < 0) {
6798abb414SViresh Kumar 		dev_err(dev, "could not parse timeout value %d\n", retval);
6898abb414SViresh Kumar 		return retval;
6998abb414SViresh Kumar 	}
7098abb414SViresh Kumar 
7198abb414SViresh Kumar 	if (val)
7298abb414SViresh Kumar 		retval = turn_on(vib, (u16)val);
7398abb414SViresh Kumar 	else
7498abb414SViresh Kumar 		retval = turn_off(vib);
7598abb414SViresh Kumar 	if (retval)
7698abb414SViresh Kumar 		return retval;
7798abb414SViresh Kumar 
7898abb414SViresh Kumar 	return count;
7998abb414SViresh Kumar }
8098abb414SViresh Kumar static DEVICE_ATTR_WO(timeout);
8198abb414SViresh Kumar 
8298abb414SViresh Kumar static struct attribute *vibrator_attrs[] = {
8398abb414SViresh Kumar 	&dev_attr_timeout.attr,
8498abb414SViresh Kumar 	NULL,
8598abb414SViresh Kumar };
8698abb414SViresh Kumar ATTRIBUTE_GROUPS(vibrator);
8798abb414SViresh Kumar 
8898abb414SViresh Kumar static struct class vibrator_class = {
8998abb414SViresh Kumar 	.name		= "vibrator",
9098abb414SViresh Kumar 	.owner		= THIS_MODULE,
9198abb414SViresh Kumar #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
9298abb414SViresh Kumar 	.dev_groups	= vibrator_groups,
9398abb414SViresh Kumar #endif
9498abb414SViresh Kumar };
9598abb414SViresh Kumar 
966b17492eSGreg Kroah-Hartman static DEFINE_IDA(minors);
9798abb414SViresh Kumar 
9898abb414SViresh Kumar static int gb_vibrator_connection_init(struct gb_connection *connection)
9998abb414SViresh Kumar {
10098abb414SViresh Kumar 	struct gb_vibrator_device *vib;
10198abb414SViresh Kumar 	struct device *dev;
10298abb414SViresh Kumar 	int retval;
10398abb414SViresh Kumar 
10498abb414SViresh Kumar 	vib = kzalloc(sizeof(*vib), GFP_KERNEL);
10598abb414SViresh Kumar 	if (!vib)
10698abb414SViresh Kumar 		return -ENOMEM;
10798abb414SViresh Kumar 
10898abb414SViresh Kumar 	vib->connection = connection;
10998abb414SViresh Kumar 	connection->private = vib;
11098abb414SViresh Kumar 
11198abb414SViresh Kumar 	retval = get_version(vib);
11298abb414SViresh Kumar 	if (retval)
11398abb414SViresh Kumar 		goto error;
11498abb414SViresh Kumar 
11598abb414SViresh Kumar 	/*
11698abb414SViresh Kumar 	 * For now we create a device in sysfs for the vibrator, but odds are
11798abb414SViresh Kumar 	 * there is a "real" device somewhere in the kernel for this, but I
11898abb414SViresh Kumar 	 * can't find it at the moment...
11998abb414SViresh Kumar 	 */
1206b17492eSGreg Kroah-Hartman 	vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL);
12198abb414SViresh Kumar 	if (vib->minor < 0) {
12298abb414SViresh Kumar 		retval = vib->minor;
12398abb414SViresh Kumar 		goto error;
12498abb414SViresh Kumar 	}
12598abb414SViresh Kumar 	dev = device_create(&vibrator_class, &connection->dev, MKDEV(0, 0), vib,
12698abb414SViresh Kumar 			    "vibrator%d", vib->minor);
12798abb414SViresh Kumar 	if (IS_ERR(dev)) {
12898abb414SViresh Kumar 		retval = -EINVAL;
1296b17492eSGreg Kroah-Hartman 		goto err_ida_remove;
13098abb414SViresh Kumar 	}
13198abb414SViresh Kumar 	vib->dev = dev;
13298abb414SViresh Kumar 
13398abb414SViresh Kumar #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
13498abb414SViresh Kumar 	/*
13598abb414SViresh Kumar 	 * Newer kernels handle this in a race-free manner, by the dev_groups
13698abb414SViresh Kumar 	 * field in the struct class up above.  But for older kernels, we need
13798abb414SViresh Kumar 	 * to "open code this :(
13898abb414SViresh Kumar 	 */
13998abb414SViresh Kumar 	retval = sysfs_create_group(&dev->kobj, vibrator_groups[0]);
14098abb414SViresh Kumar 	if (retval) {
14198abb414SViresh Kumar 		device_unregister(dev);
1426b17492eSGreg Kroah-Hartman 		goto err_ida_remove;
14398abb414SViresh Kumar 	}
14498abb414SViresh Kumar #endif
14598abb414SViresh Kumar 
14698abb414SViresh Kumar 	return 0;
14798abb414SViresh Kumar 
1486b17492eSGreg Kroah-Hartman err_ida_remove:
1496b17492eSGreg Kroah-Hartman 	ida_simple_remove(&minors, vib->minor);
15098abb414SViresh Kumar error:
15198abb414SViresh Kumar 	kfree(vib);
15298abb414SViresh Kumar 	return retval;
15398abb414SViresh Kumar }
15498abb414SViresh Kumar 
15598abb414SViresh Kumar static void gb_vibrator_connection_exit(struct gb_connection *connection)
15698abb414SViresh Kumar {
15798abb414SViresh Kumar 	struct gb_vibrator_device *vib = connection->private;
15898abb414SViresh Kumar 
15998abb414SViresh Kumar #if LINUX_VERSION_CODE <= KERNEL_VERSION(3,11,0)
16098abb414SViresh Kumar 	sysfs_remove_group(&vib->dev->kobj, vibrator_groups[0]);
16198abb414SViresh Kumar #endif
1626b17492eSGreg Kroah-Hartman 	ida_simple_remove(&minors, vib->minor);
16398abb414SViresh Kumar 	device_unregister(vib->dev);
16498abb414SViresh Kumar 	kfree(vib);
16598abb414SViresh Kumar }
16698abb414SViresh Kumar 
16798abb414SViresh Kumar static struct gb_protocol vibrator_protocol = {
16898abb414SViresh Kumar 	.name			= "vibrator",
16998abb414SViresh Kumar 	.id			= GREYBUS_PROTOCOL_VIBRATOR,
17098abb414SViresh Kumar 	.major			= 0,
17198abb414SViresh Kumar 	.minor			= 1,
17298abb414SViresh Kumar 	.connection_init	= gb_vibrator_connection_init,
17398abb414SViresh Kumar 	.connection_exit	= gb_vibrator_connection_exit,
17498abb414SViresh Kumar 	.request_recv		= NULL,	/* no incoming requests */
17598abb414SViresh Kumar };
17698abb414SViresh Kumar 
17798abb414SViresh Kumar static __init int protocol_init(void)
17898abb414SViresh Kumar {
17998abb414SViresh Kumar 	int retval;
18098abb414SViresh Kumar 
18198abb414SViresh Kumar 	retval = class_register(&vibrator_class);
18298abb414SViresh Kumar 	if (retval)
18398abb414SViresh Kumar 		return retval;
18498abb414SViresh Kumar 
18598abb414SViresh Kumar 	return gb_protocol_register(&vibrator_protocol);
18698abb414SViresh Kumar }
1870d34be75SViresh Kumar module_init(protocol_init);
18898abb414SViresh Kumar 
18998abb414SViresh Kumar static __exit void protocol_exit(void)
19098abb414SViresh Kumar {
19198abb414SViresh Kumar 	gb_protocol_deregister(&vibrator_protocol);
19298abb414SViresh Kumar 	class_unregister(&vibrator_class);
19398abb414SViresh Kumar }
19498abb414SViresh Kumar module_exit(protocol_exit);
19598abb414SViresh Kumar 
19698abb414SViresh Kumar MODULE_LICENSE("GPL v2");
197