cros_ec.c revision 533cec8f34778de10412dfabac991cf458ebf3c9
14ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass/* 24ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * ChromeOS EC multi-function device 34ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * 44ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * Copyright (C) 2012 Google, Inc 54ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * 64ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * This software is licensed under the terms of the GNU General Public 74ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * License version 2, as published by the Free Software Foundation, and 84ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * may be copied, distributed, and modified under those terms. 94ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * 104ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * This program is distributed in the hope that it will be useful, 114ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * but WITHOUT ANY WARRANTY; without even the implied warranty of 124ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 134ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * GNU General Public License for more details. 144ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * 154ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * The ChromeOS EC multi function device is used to mux all the requests 164ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * to the EC device for its multiple features: keyboard controller, 174ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass * battery charging and regulator control, firmware update. 184ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass */ 194ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 204ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#include <linux/interrupt.h> 214ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#include <linux/slab.h> 225ebeaff5c5be9f21680b91aac77ee155d935f888Samuel Ortiz#include <linux/module.h> 234ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#include <linux/mfd/core.h> 244ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#include <linux/mfd/cros_ec.h> 254ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#include <linux/mfd/cros_ec_commands.h> 264ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 274ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_prepare_tx(struct cros_ec_device *ec_dev, 285d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson struct cros_ec_command *msg) 294ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 304ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass uint8_t *out; 314ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass int csum, i; 324ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 335d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); 344ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out = ec_dev->dout; 354ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out[0] = EC_CMD_VERSION0 + msg->version; 365d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson out[1] = msg->command; 375d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson out[2] = msg->outsize; 384ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass csum = out[0] + out[1] + out[2]; 395d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson for (i = 0; i < msg->outsize; i++) 405d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i]; 415d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); 424ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 435d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson return EC_MSG_TX_PROTO_BYTES + msg->outsize; 444ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 455ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_prepare_tx); 464ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 474ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassstatic int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, 484ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass uint16_t cmd, void *out_buf, int out_len, 494ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass void *in_buf, int in_len) 504ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 515d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson struct cros_ec_command msg; 524ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 534ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass msg.version = cmd >> 8; 545d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson msg.command = cmd & 0xff; 555d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson msg.outdata = out_buf; 565d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson msg.outsize = out_len; 575d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson msg.indata = in_buf; 585d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson msg.insize = in_len; 594ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 607e6cb5b4dbbc4b1d98289c88d0bc4092cac328beBill Richardson return ec_dev->cmd_xfer(ec_dev, &msg); 614ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 624ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 634ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassstatic int cros_ec_command_recv(struct cros_ec_device *ec_dev, 644ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass uint16_t cmd, void *buf, int buf_len) 654ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 664ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len); 674ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 684ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 694ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassstatic int cros_ec_command_send(struct cros_ec_device *ec_dev, 704ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass uint16_t cmd, void *buf, int buf_len) 714ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 724ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0); 734ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 744ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 754ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassstatic irqreturn_t ec_irq_thread(int irq, void *data) 764ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 774ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct cros_ec_device *ec_dev = data; 784ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 794ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (device_may_wakeup(ec_dev->dev)) 804ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass pm_wakeup_event(ec_dev->dev, 0); 814ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 824ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev); 834ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 844ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return IRQ_HANDLED; 854ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 864ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 875ac98553afe41ffb5513fa8aac6df699a70231a3Geert Uytterhoevenstatic const struct mfd_cell cros_devs[] = { 884ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass { 894ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass .name = "cros-ec-keyb", 904ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass .id = 1, 914ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass .of_compatible = "google,cros-ec-keyb", 924ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass }, 939d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson { 949d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson .name = "cros-ec-i2c-tunnel", 959d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson .id = 2, 969d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson .of_compatible = "google,cros-ec-i2c-tunnel", 979d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson }, 984ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass}; 994ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1004ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_register(struct cros_ec_device *ec_dev) 1014ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1024ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct device *dev = ec_dev->dev; 1034ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass int err = 0; 1044ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1054ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); 1064ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1074ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->command_send = cros_ec_command_send; 1084ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->command_recv = cros_ec_command_recv; 1094ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->command_sendrecv = cros_ec_command_sendrecv; 1104ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1114ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->din_size) { 11222f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 11322f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones if (!ec_dev->din) 11422f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return -ENOMEM; 1154ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1164ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->dout_size) { 11722f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 11822f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones if (!ec_dev->dout) 11922f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return -ENOMEM; 1204ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1214ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1224ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (!ec_dev->irq) { 1234ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq); 12422f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return err; 1254ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1264ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1274ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, 1284ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass IRQF_TRIGGER_LOW | IRQF_ONESHOT, 1294ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass "chromeos-ec", ec_dev); 1304ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (err) { 1314ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err); 13222f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return err; 1334ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1344ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1354ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass err = mfd_add_devices(dev, 0, cros_devs, 1364ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ARRAY_SIZE(cros_devs), 1374ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass NULL, ec_dev->irq, NULL); 1384ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (err) { 1394ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_err(dev, "failed to add mfd devices\n"); 1404ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass goto fail_mfd; 1414ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1424ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 143533cec8f34778de10412dfabac991cf458ebf3c9Bill Richardson dev_info(dev, "Chrome EC device registered\n"); 1444ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1454ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1464ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1474ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassfail_mfd: 1484ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass free_irq(ec_dev->irq, ec_dev); 14922f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones 1504ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return err; 1514ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1525ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_register); 1534ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1544ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_remove(struct cros_ec_device *ec_dev) 1554ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1564ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass mfd_remove_devices(ec_dev->dev); 1574ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass free_irq(ec_dev->irq, ec_dev); 1584ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1594ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1604ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1615ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_remove); 1624ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1634ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#ifdef CONFIG_PM_SLEEP 1644ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_suspend(struct cros_ec_device *ec_dev) 1654ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1664ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct device *dev = ec_dev->dev; 1674ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1684ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (device_may_wakeup(dev)) 1694ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); 1704ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1714ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass disable_irq(ec_dev->irq); 1724ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->was_wake_device = ec_dev->wake_enabled; 1734ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1744ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1754ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1765ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_suspend); 1774ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1784ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_resume(struct cros_ec_device *ec_dev) 1794ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1804ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass enable_irq(ec_dev->irq); 1814ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1824ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->wake_enabled) { 1834ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass disable_irq_wake(ec_dev->irq); 1844ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->wake_enabled = 0; 1854ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1864ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1874ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1884ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1895ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_resume); 1905ebeaff5c5be9f21680b91aac77ee155d935f888Samuel Ortiz 1914ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#endif 192a865a589144da6dbf26750b7193a9748da159305Bill Richardson 193a865a589144da6dbf26750b7193a9748da159305Bill RichardsonMODULE_LICENSE("GPL"); 194a865a589144da6dbf26750b7193a9748da159305Bill RichardsonMODULE_DESCRIPTION("ChromeOS EC core driver"); 195