| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ChromeOS EC multi-function device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2012 Google, Inc | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This software is licensed under the terms of the GNU General Public | 
					
						
							|  |  |  |  * License version 2, as published by the Free Software Foundation, and | 
					
						
							|  |  |  |  * may be copied, distributed, and modified under those terms. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The ChromeOS EC multi function device is used to mux all the requests | 
					
						
							|  |  |  |  * to the EC device for its multiple features: keyboard controller, | 
					
						
							|  |  |  |  * battery charging and regulator control, firmware update. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							| 
									
										
										
										
											2013-03-20 09:46:15 +01:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | #include <linux/mfd/core.h>
 | 
					
						
							|  |  |  | #include <linux/mfd/cros_ec.h>
 | 
					
						
							|  |  |  | #include <linux/mfd/cros_ec_commands.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:02 -07:00
										 |  |  | 		       struct cros_ec_command *msg) | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | { | 
					
						
							|  |  |  | 	uint8_t *out; | 
					
						
							|  |  |  | 	int csum, i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:02 -07:00
										 |  |  | 	BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 	out = ec_dev->dout; | 
					
						
							|  |  |  | 	out[0] = EC_CMD_VERSION0 + msg->version; | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:02 -07:00
										 |  |  | 	out[1] = msg->command; | 
					
						
							|  |  |  | 	out[2] = msg->outsize; | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 	csum = out[0] + out[1] + out[2]; | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:02 -07:00
										 |  |  | 	for (i = 0; i < msg->outsize; i++) | 
					
						
							|  |  |  | 		csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i]; | 
					
						
							|  |  |  | 	out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:02 -07:00
										 |  |  | 	return EC_MSG_TX_PROTO_BYTES + msg->outsize; | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-20 09:46:15 +01:00
										 |  |  | EXPORT_SYMBOL(cros_ec_prepare_tx); | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:05 -07:00
										 |  |  | int cros_ec_check_result(struct cros_ec_device *ec_dev, | 
					
						
							|  |  |  | 			 struct cros_ec_command *msg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (msg->result) { | 
					
						
							|  |  |  | 	case EC_RES_SUCCESS: | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	case EC_RES_IN_PROGRESS: | 
					
						
							|  |  |  | 		dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", | 
					
						
							|  |  |  | 			msg->command); | 
					
						
							|  |  |  | 		return -EAGAIN; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", | 
					
						
							|  |  |  | 			msg->command, msg->result); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL(cros_ec_check_result); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-18 14:33:06 +01:00
										 |  |  | static const struct mfd_cell cros_devs[] = { | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		.name = "cros-ec-keyb", | 
					
						
							|  |  |  | 		.id = 1, | 
					
						
							|  |  |  | 		.of_compatible = "google,cros-ec-keyb", | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2014-04-30 10:44:09 -07:00
										 |  |  | 	{ | 
					
						
							|  |  |  | 		.name = "cros-ec-i2c-tunnel", | 
					
						
							|  |  |  | 		.id = 2, | 
					
						
							|  |  |  | 		.of_compatible = "google,cros-ec-i2c-tunnel", | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int cros_ec_register(struct cros_ec_device *ec_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = ec_dev->dev; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ec_dev->din_size) { | 
					
						
							| 
									
										
										
										
											2013-05-23 16:25:10 +01:00
										 |  |  | 		ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); | 
					
						
							|  |  |  | 		if (!ec_dev->din) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (ec_dev->dout_size) { | 
					
						
							| 
									
										
										
										
											2013-05-23 16:25:10 +01:00
										 |  |  | 		ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); | 
					
						
							|  |  |  | 		if (!ec_dev->dout) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = mfd_add_devices(dev, 0, cros_devs, | 
					
						
							|  |  |  | 			      ARRAY_SIZE(cros_devs), | 
					
						
							|  |  |  | 			      NULL, ec_dev->irq, NULL); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		dev_err(dev, "failed to add mfd devices\n"); | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:07 -07:00
										 |  |  | 		return err; | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-18 11:14:03 -07:00
										 |  |  | 	dev_info(dev, "Chrome EC device registered\n"); | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-20 09:46:15 +01:00
										 |  |  | EXPORT_SYMBOL(cros_ec_register); | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | int cros_ec_remove(struct cros_ec_device *ec_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	mfd_remove_devices(ec_dev->dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-20 09:46:15 +01:00
										 |  |  | EXPORT_SYMBOL(cros_ec_remove); | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM_SLEEP
 | 
					
						
							|  |  |  | int cros_ec_suspend(struct cros_ec_device *ec_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = ec_dev->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (device_may_wakeup(dev)) | 
					
						
							|  |  |  | 		ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	disable_irq(ec_dev->irq); | 
					
						
							|  |  |  | 	ec_dev->was_wake_device = ec_dev->wake_enabled; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-20 09:46:15 +01:00
										 |  |  | EXPORT_SYMBOL(cros_ec_suspend); | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | int cros_ec_resume(struct cros_ec_device *ec_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	enable_irq(ec_dev->irq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ec_dev->wake_enabled) { | 
					
						
							|  |  |  | 		disable_irq_wake(ec_dev->irq); | 
					
						
							|  |  |  | 		ec_dev->wake_enabled = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-03-20 09:46:15 +01:00
										 |  |  | EXPORT_SYMBOL(cros_ec_resume); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-25 14:08:37 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-04-17 12:32:17 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("ChromeOS EC core driver"); |