Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.18-rc6 195 lines 4.6 kB view raw
1/* 2 * ChromeOS EC multi-function device 3 * 4 * Copyright (C) 2012 Google, Inc 5 * 6 * This software is licensed under the terms of the GNU General Public 7 * License version 2, as published by the Free Software Foundation, and 8 * may be copied, distributed, and modified under those terms. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * The ChromeOS EC multi function device is used to mux all the requests 16 * to the EC device for its multiple features: keyboard controller, 17 * battery charging and regulator control, firmware update. 18 */ 19 20#include <linux/interrupt.h> 21#include <linux/slab.h> 22#include <linux/module.h> 23#include <linux/mfd/core.h> 24#include <linux/mfd/cros_ec.h> 25#include <linux/mfd/cros_ec_commands.h> 26#include <linux/delay.h> 27 28#define EC_COMMAND_RETRIES 50 29 30int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, 31 struct cros_ec_command *msg) 32{ 33 uint8_t *out; 34 int csum, i; 35 36 BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); 37 out = ec_dev->dout; 38 out[0] = EC_CMD_VERSION0 + msg->version; 39 out[1] = msg->command; 40 out[2] = msg->outsize; 41 csum = out[0] + out[1] + out[2]; 42 for (i = 0; i < msg->outsize; i++) 43 csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i]; 44 out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); 45 46 return EC_MSG_TX_PROTO_BYTES + msg->outsize; 47} 48EXPORT_SYMBOL(cros_ec_prepare_tx); 49 50int cros_ec_check_result(struct cros_ec_device *ec_dev, 51 struct cros_ec_command *msg) 52{ 53 switch (msg->result) { 54 case EC_RES_SUCCESS: 55 return 0; 56 case EC_RES_IN_PROGRESS: 57 dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", 58 msg->command); 59 return -EAGAIN; 60 default: 61 dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", 62 msg->command, msg->result); 63 return 0; 64 } 65} 66EXPORT_SYMBOL(cros_ec_check_result); 67 68int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, 69 struct cros_ec_command *msg) 70{ 71 int ret; 72 73 mutex_lock(&ec_dev->lock); 74 ret = ec_dev->cmd_xfer(ec_dev, msg); 75 if (msg->result == EC_RES_IN_PROGRESS) { 76 int i; 77 struct cros_ec_command status_msg; 78 struct ec_response_get_comms_status status; 79 80 status_msg.version = 0; 81 status_msg.command = EC_CMD_GET_COMMS_STATUS; 82 status_msg.outdata = NULL; 83 status_msg.outsize = 0; 84 status_msg.indata = (uint8_t *)&status; 85 status_msg.insize = sizeof(status); 86 87 /* 88 * Query the EC's status until it's no longer busy or 89 * we encounter an error. 90 */ 91 for (i = 0; i < EC_COMMAND_RETRIES; i++) { 92 usleep_range(10000, 11000); 93 94 ret = ec_dev->cmd_xfer(ec_dev, &status_msg); 95 if (ret < 0) 96 break; 97 98 msg->result = status_msg.result; 99 if (status_msg.result != EC_RES_SUCCESS) 100 break; 101 if (!(status.flags & EC_COMMS_STATUS_PROCESSING)) 102 break; 103 } 104 } 105 mutex_unlock(&ec_dev->lock); 106 107 return ret; 108} 109EXPORT_SYMBOL(cros_ec_cmd_xfer); 110 111static const struct mfd_cell cros_devs[] = { 112 { 113 .name = "cros-ec-keyb", 114 .id = 1, 115 .of_compatible = "google,cros-ec-keyb", 116 }, 117 { 118 .name = "cros-ec-i2c-tunnel", 119 .id = 2, 120 .of_compatible = "google,cros-ec-i2c-tunnel", 121 }, 122}; 123 124int cros_ec_register(struct cros_ec_device *ec_dev) 125{ 126 struct device *dev = ec_dev->dev; 127 int err = 0; 128 129 if (ec_dev->din_size) { 130 ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 131 if (!ec_dev->din) 132 return -ENOMEM; 133 } 134 if (ec_dev->dout_size) { 135 ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 136 if (!ec_dev->dout) 137 return -ENOMEM; 138 } 139 140 mutex_init(&ec_dev->lock); 141 142 err = mfd_add_devices(dev, 0, cros_devs, 143 ARRAY_SIZE(cros_devs), 144 NULL, ec_dev->irq, NULL); 145 if (err) { 146 dev_err(dev, "failed to add mfd devices\n"); 147 return err; 148 } 149 150 dev_info(dev, "Chrome EC device registered\n"); 151 152 return 0; 153} 154EXPORT_SYMBOL(cros_ec_register); 155 156int cros_ec_remove(struct cros_ec_device *ec_dev) 157{ 158 mfd_remove_devices(ec_dev->dev); 159 160 return 0; 161} 162EXPORT_SYMBOL(cros_ec_remove); 163 164#ifdef CONFIG_PM_SLEEP 165int cros_ec_suspend(struct cros_ec_device *ec_dev) 166{ 167 struct device *dev = ec_dev->dev; 168 169 if (device_may_wakeup(dev)) 170 ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); 171 172 disable_irq(ec_dev->irq); 173 ec_dev->was_wake_device = ec_dev->wake_enabled; 174 175 return 0; 176} 177EXPORT_SYMBOL(cros_ec_suspend); 178 179int cros_ec_resume(struct cros_ec_device *ec_dev) 180{ 181 enable_irq(ec_dev->irq); 182 183 if (ec_dev->wake_enabled) { 184 disable_irq_wake(ec_dev->irq); 185 ec_dev->wake_enabled = 0; 186 } 187 188 return 0; 189} 190EXPORT_SYMBOL(cros_ec_resume); 191 192#endif 193 194MODULE_LICENSE("GPL"); 195MODULE_DESCRIPTION("ChromeOS EC core driver");