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.16-rc6 194 lines 4.7 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 27int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, 28 struct cros_ec_msg *msg) 29{ 30 uint8_t *out; 31 int csum, i; 32 33 BUG_ON(msg->out_len > EC_PROTO2_MAX_PARAM_SIZE); 34 out = ec_dev->dout; 35 out[0] = EC_CMD_VERSION0 + msg->version; 36 out[1] = msg->cmd; 37 out[2] = msg->out_len; 38 csum = out[0] + out[1] + out[2]; 39 for (i = 0; i < msg->out_len; i++) 40 csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i]; 41 out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff); 42 43 return EC_MSG_TX_PROTO_BYTES + msg->out_len; 44} 45EXPORT_SYMBOL(cros_ec_prepare_tx); 46 47static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, 48 uint16_t cmd, void *out_buf, int out_len, 49 void *in_buf, int in_len) 50{ 51 struct cros_ec_msg msg; 52 53 msg.version = cmd >> 8; 54 msg.cmd = cmd & 0xff; 55 msg.out_buf = out_buf; 56 msg.out_len = out_len; 57 msg.in_buf = in_buf; 58 msg.in_len = in_len; 59 60 return ec_dev->command_xfer(ec_dev, &msg); 61} 62 63static int cros_ec_command_recv(struct cros_ec_device *ec_dev, 64 uint16_t cmd, void *buf, int buf_len) 65{ 66 return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len); 67} 68 69static int cros_ec_command_send(struct cros_ec_device *ec_dev, 70 uint16_t cmd, void *buf, int buf_len) 71{ 72 return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0); 73} 74 75static irqreturn_t ec_irq_thread(int irq, void *data) 76{ 77 struct cros_ec_device *ec_dev = data; 78 79 if (device_may_wakeup(ec_dev->dev)) 80 pm_wakeup_event(ec_dev->dev, 0); 81 82 blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev); 83 84 return IRQ_HANDLED; 85} 86 87static const struct mfd_cell cros_devs[] = { 88 { 89 .name = "cros-ec-keyb", 90 .id = 1, 91 .of_compatible = "google,cros-ec-keyb", 92 }, 93 { 94 .name = "cros-ec-i2c-tunnel", 95 .id = 2, 96 .of_compatible = "google,cros-ec-i2c-tunnel", 97 }, 98}; 99 100int cros_ec_register(struct cros_ec_device *ec_dev) 101{ 102 struct device *dev = ec_dev->dev; 103 int err = 0; 104 105 BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); 106 107 ec_dev->command_send = cros_ec_command_send; 108 ec_dev->command_recv = cros_ec_command_recv; 109 ec_dev->command_sendrecv = cros_ec_command_sendrecv; 110 111 if (ec_dev->din_size) { 112 ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 113 if (!ec_dev->din) 114 return -ENOMEM; 115 } 116 if (ec_dev->dout_size) { 117 ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 118 if (!ec_dev->dout) 119 return -ENOMEM; 120 } 121 122 if (!ec_dev->irq) { 123 dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq); 124 return err; 125 } 126 127 err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, 128 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 129 "chromeos-ec", ec_dev); 130 if (err) { 131 dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err); 132 return err; 133 } 134 135 err = mfd_add_devices(dev, 0, cros_devs, 136 ARRAY_SIZE(cros_devs), 137 NULL, ec_dev->irq, NULL); 138 if (err) { 139 dev_err(dev, "failed to add mfd devices\n"); 140 goto fail_mfd; 141 } 142 143 dev_info(dev, "Chrome EC (%s)\n", ec_dev->name); 144 145 return 0; 146 147fail_mfd: 148 free_irq(ec_dev->irq, ec_dev); 149 150 return err; 151} 152EXPORT_SYMBOL(cros_ec_register); 153 154int cros_ec_remove(struct cros_ec_device *ec_dev) 155{ 156 mfd_remove_devices(ec_dev->dev); 157 free_irq(ec_dev->irq, ec_dev); 158 159 return 0; 160} 161EXPORT_SYMBOL(cros_ec_remove); 162 163#ifdef CONFIG_PM_SLEEP 164int cros_ec_suspend(struct cros_ec_device *ec_dev) 165{ 166 struct device *dev = ec_dev->dev; 167 168 if (device_may_wakeup(dev)) 169 ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); 170 171 disable_irq(ec_dev->irq); 172 ec_dev->was_wake_device = ec_dev->wake_enabled; 173 174 return 0; 175} 176EXPORT_SYMBOL(cros_ec_suspend); 177 178int cros_ec_resume(struct cros_ec_device *ec_dev) 179{ 180 enable_irq(ec_dev->irq); 181 182 if (ec_dev->wake_enabled) { 183 disable_irq_wake(ec_dev->irq); 184 ec_dev->wake_enabled = 0; 185 } 186 187 return 0; 188} 189EXPORT_SYMBOL(cros_ec_resume); 190 191#endif 192 193MODULE_LICENSE("GPL"); 194MODULE_DESCRIPTION("ChromeOS EC core driver");