Commit 57b33ff0 authored by Gwendal Grignou's avatar Gwendal Grignou Committed by Lee Jones
Browse files

mfd: cros_ec: Support multiple EC in a system



Chromebooks can have more than one Embedded Controller so the
cros_ec device id has to be incremented for each EC registered.

Add a new structure to represent multiple EC as different char
devices (e.g: /dev/cros_ec, /dev/cros_pd). It connects to
cros_ec_device and allows sysfs inferface for cros_pd.

Also reduce number of allocated objects, make chromeos sysfs
class object a static and add refcounting to prevent object
deletion while command is in progress.

Signed-off-by: default avatarGwendal Grignou <gwendal@chromium.org>
Reviewed-by: default avatarDmitry Torokhov <dtor@chromium.org>
Signed-off-by: default avatarJavier Martinez Canillas <javier.martinez@collabora.co.uk>
Tested-by: default avatarHeiko Stuebner <heiko@sntech.de>
Acked-by: default avatarLee Jones <lee.jones@linaro.org>
Acked-by: default avatarOlof Johansson <olof@lixom.net>
Signed-off-by: default avatarLee Jones <lee.jones@linaro.org>
parent d3654070
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -275,7 +275,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
	ckdev->dev = dev;
	dev_set_drvdata(&pdev->dev, ckdev);

	idev->name = ec->ec_name;
	idev->name = CROS_EC_DEV_NAME;
	idev->phys = ec->phys_name;
	__set_bit(EV_REP, idev->evbit);

+46 −8
Original line number Diff line number Diff line
@@ -24,11 +24,29 @@
#include <linux/mfd/core.h>
#include <linux/mfd/cros_ec.h>

static const struct mfd_cell cros_devs[] = {
	{
#define CROS_EC_DEV_EC_INDEX 0
#define CROS_EC_DEV_PD_INDEX 1

struct cros_ec_platform ec_p = {
	.ec_name = CROS_EC_DEV_NAME,
	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX),
};

struct cros_ec_platform pd_p = {
	.ec_name = CROS_EC_DEV_PD_NAME,
	.cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX),
};

struct mfd_cell ec_cell = {
	.name = "cros-ec-ctl",
	.platform_data = &ec_p,
	.pdata_size = sizeof(ec_p),
};

struct mfd_cell ec_pd_cell = {
	.name = "cros-ec-ctl",
		.id = PLATFORM_DEVID_AUTO,
	},
	.platform_data = &pd_p,
	.pdata_size = sizeof(pd_p),
};

int cros_ec_register(struct cros_ec_device *ec_dev)
@@ -52,13 +70,33 @@ int cros_ec_register(struct cros_ec_device *ec_dev)

	cros_ec_query_all(ec_dev);

	err = mfd_add_devices(dev, 0, cros_devs,
			      ARRAY_SIZE(cros_devs),
	err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO, &ec_cell, 1,
			      NULL, ec_dev->irq, NULL);
	if (err) {
		dev_err(dev, "failed to add mfd devices\n");
		dev_err(dev,
			"Failed to register Embedded Controller subdevice %d\n",
			err);
		return err;
	}

	if (ec_dev->max_passthru) {
		/*
		 * Register a PD device as well on top of this device.
		 * We make the following assumptions:
		 * - behind an EC, we have a pd
		 * - only one device added.
		 * - the EC is responsive at init time (it is not true for a
		 *   sensor hub.
		 */
		err = mfd_add_devices(ec_dev->dev, PLATFORM_DEVID_AUTO,
				      &ec_pd_cell, 1, NULL, ec_dev->irq, NULL);
		if (err) {
			dev_err(dev,
				"Failed to register Power Delivery subdevice %d\n",
				err);
			return err;
		}
	}

	if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
		err = of_platform_populate(dev->of_node, NULL, NULL, dev);
+0 −1
Original line number Diff line number Diff line
@@ -302,7 +302,6 @@ static int cros_ec_i2c_probe(struct i2c_client *client,
	ec_dev->irq = client->irq;
	ec_dev->cmd_xfer = cros_ec_cmd_xfer_i2c;
	ec_dev->pkt_xfer = cros_ec_pkt_xfer_i2c;
	ec_dev->ec_name = client->name;
	ec_dev->phys_name = client->adapter->name;
	ec_dev->din_size = sizeof(struct ec_host_response_i2c) +
			   sizeof(struct ec_response_get_protocol_info);
+0 −1
Original line number Diff line number Diff line
@@ -637,7 +637,6 @@ static int cros_ec_spi_probe(struct spi_device *spi)
	ec_dev->irq = spi->irq;
	ec_dev->cmd_xfer = cros_ec_cmd_xfer_spi;
	ec_dev->pkt_xfer = cros_ec_pkt_xfer_spi;
	ec_dev->ec_name = ec_spi->spi->modalias;
	ec_dev->phys_name = dev_name(&ec_spi->spi->dev);
	ec_dev->din_size = EC_MSG_PREAMBLE_COUNT +
			   sizeof(struct ec_host_response) +
+91 −39
Original line number Diff line number Diff line
@@ -27,11 +27,22 @@

/* Device variables */
#define CROS_MAX_DEV 128
static struct class *cros_class;
static int ec_major;

static const struct attribute_group *cros_ec_groups[] = {
	&cros_ec_attr_group,
	&cros_ec_lightbar_attr_group,
	NULL,
};

static struct class cros_class = {
	.owner          = THIS_MODULE,
	.name           = "chromeos",
	.dev_groups     = cros_ec_groups,
};

/* Basic communication */
static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
{
	struct ec_response_get_version *resp;
	static const char * const current_image_name[] = {
@@ -45,11 +56,11 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
		return -ENOMEM;

	msg->version = 0;
	msg->command = EC_CMD_GET_VERSION;
	msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
	msg->insize = sizeof(*resp);
	msg->outsize = 0;

	ret = cros_ec_cmd_xfer(ec, msg);
	ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
	if (ret < 0)
		goto exit;

@@ -78,8 +89,10 @@ exit:
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
	filp->private_data = container_of(inode->i_cdev,
					  struct cros_ec_device, cdev);
	struct cros_ec_dev *ec = container_of(inode->i_cdev,
					      struct cros_ec_dev, cdev);
	filp->private_data = ec;
	nonseekable_open(inode, filp);
	return 0;
}

@@ -91,7 +104,7 @@ static int ec_device_release(struct inode *inode, struct file *filp)
static ssize_t ec_device_read(struct file *filp, char __user *buffer,
			      size_t length, loff_t *offset)
{
	struct cros_ec_device *ec = filp->private_data;
	struct cros_ec_dev *ec = filp->private_data;
	char msg[sizeof(struct ec_response_get_version) +
		 sizeof(CROS_EC_DEV_VERSION)];
	size_t count;
@@ -114,7 +127,7 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
}

/* Ioctls */
static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
{
	long ret;
	struct cros_ec_command u_cmd;
@@ -133,7 +146,8 @@ static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
		goto exit;
	}

	ret = cros_ec_cmd_xfer(ec, s_cmd);
	s_cmd->command += ec->cmd_offset;
	ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
	/* Only copy data to userland if data was received. */
	if (ret < 0)
		goto exit;
@@ -145,19 +159,21 @@ exit:
	return ret;
}

static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg)
{
	struct cros_ec_device *ec_dev = ec->ec_dev;
	struct cros_ec_readmem s_mem = { };
	long num;

	/* Not every platform supports direct reads */
	if (!ec->cmd_readmem)
	if (!ec_dev->cmd_readmem)
		return -ENOTTY;

	if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
		return -EFAULT;

	num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer);
	num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
				  s_mem.buffer);
	if (num <= 0)
		return num;

@@ -170,7 +186,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
static long ec_device_ioctl(struct file *filp, unsigned int cmd,
			    unsigned long arg)
{
	struct cros_ec_device *ec = filp->private_data;
	struct cros_ec_dev *ec = filp->private_data;

	if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
		return -ENOTTY;
@@ -193,45 +209,81 @@ static const struct file_operations fops = {
	.unlocked_ioctl = ec_device_ioctl,
};

static void __remove(struct device *dev)
{
	struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
					      class_dev);
	kfree(ec);
}

static int ec_device_probe(struct platform_device *pdev)
{
	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
	int retval = -ENOTTY;
	dev_t devno = MKDEV(ec_major, 0);
	int retval = -ENOMEM;
	struct device *dev = &pdev->dev;
	struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
	dev_t devno = MKDEV(ec_major, pdev->id);
	struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);

	/* Instantiate it (and remember the EC) */
	if (!ec)
		return retval;

	dev_set_drvdata(dev, ec);
	ec->ec_dev = dev_get_drvdata(dev->parent);
	ec->dev = dev;
	ec->cmd_offset = ec_platform->cmd_offset;
	device_initialize(&ec->class_dev);
	cdev_init(&ec->cdev, &fops);

	/*
	 * Add the character device
	 * Link cdev to the class device to be sure device is not used
	 * before unbinding it.
	 */
	ec->cdev.kobj.parent = &ec->class_dev.kobj;
	retval = cdev_add(&ec->cdev, devno, 1);
	if (retval) {
		dev_err(&pdev->dev, ": failed to add character device\n");
		return retval;
		dev_err(dev, ": failed to add character device\n");
		goto cdev_add_failed;
	}

	ec->vdev = device_create(cros_class, NULL, devno, ec,
				 CROS_EC_DEV_NAME);
	if (IS_ERR(ec->vdev)) {
		retval = PTR_ERR(ec->vdev);
		dev_err(&pdev->dev, ": failed to create device\n");
		cdev_del(&ec->cdev);
		return retval;
	/*
	 * Add the class device
	 * Link to the character device for creating the /dev entry
	 * in devtmpfs.
	 */
	ec->class_dev.devt = ec->cdev.dev;
	ec->class_dev.class = &cros_class;
	ec->class_dev.parent = dev;
	ec->class_dev.release = __remove;

	retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
	if (retval) {
		dev_err(dev, "dev_set_name failed => %d\n", retval);
		goto set_named_failed;
	}

	/* Initialize extra interfaces */
	ec_dev_sysfs_init(ec);
	ec_dev_lightbar_init(ec);
	retval = device_add(&ec->class_dev);
	if (retval) {
		dev_err(dev, "device_register failed => %d\n", retval);
		goto dev_reg_failed;
	}

	return 0;

dev_reg_failed:
set_named_failed:
	dev_set_drvdata(dev, NULL);
	cdev_del(&ec->cdev);
cdev_add_failed:
	kfree(ec);
	return retval;
}

static int ec_device_remove(struct platform_device *pdev)
{
	struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);

	ec_dev_lightbar_remove(ec);
	ec_dev_sysfs_remove(ec);
	device_destroy(cros_class, MKDEV(ec_major, 0));
	struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
	cdev_del(&ec->cdev);
	device_unregister(&ec->class_dev);
	return 0;
}

@@ -248,10 +300,10 @@ static int __init cros_ec_dev_init(void)
	int ret;
	dev_t dev = 0;

	cros_class = class_create(THIS_MODULE, "chromeos");
	if (IS_ERR(cros_class)) {
	ret  = class_register(&cros_class);
	if (ret) {
		pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
		return PTR_ERR(cros_class);
		return ret;
	}

	/* Get a range of minor numbers (starting with 0) to work with */
@@ -273,7 +325,7 @@ static int __init cros_ec_dev_init(void)
failed_devreg:
	unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
failed_chrdevreg:
	class_destroy(cros_class);
	class_unregister(&cros_class);
	return ret;
}

@@ -281,7 +333,7 @@ static void __exit cros_ec_dev_exit(void)
{
	platform_driver_unregister(&cros_ec_dev_driver);
	unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
	class_destroy(cros_class);
	class_unregister(&cros_class);
}

module_init(cros_ec_dev_init);
Loading