cros_ec.c revision 5ac98553afe41ffb5513fa8aac6df699a70231a3
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, 284ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct cros_ec_msg *msg) 294ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 304ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass uint8_t *out; 314ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass int csum, i; 324ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 334ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE); 344ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out = ec_dev->dout; 354ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out[0] = EC_CMD_VERSION0 + msg->version; 364ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out[1] = msg->cmd; 374ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out[2] = msg->out_len; 384ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass csum = out[0] + out[1] + out[2]; 394ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass for (i = 0; i < msg->out_len; i++) 404ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i]; 414ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff); 424ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 434ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return EC_MSG_TX_PROTO_BYTES + msg->out_len; 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{ 514ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct cros_ec_msg msg; 524ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 534ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass msg.version = cmd >> 8; 544ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass msg.cmd = cmd & 0xff; 554ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass msg.out_buf = out_buf; 564ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass msg.out_len = out_len; 574ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass msg.in_buf = in_buf; 584ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass msg.in_len = in_len; 594ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 604ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return ec_dev->command_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 }, 934ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass}; 944ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 954ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_register(struct cros_ec_device *ec_dev) 964ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 974ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct device *dev = ec_dev->dev; 984ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass int err = 0; 994ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1004ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); 1014ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1024ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->command_send = cros_ec_command_send; 1034ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->command_recv = cros_ec_command_recv; 1044ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->command_sendrecv = cros_ec_command_sendrecv; 1054ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1064ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->din_size) { 10722f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 10822f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones if (!ec_dev->din) 10922f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return -ENOMEM; 1104ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1114ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->dout_size) { 11222f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 11322f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones if (!ec_dev->dout) 11422f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return -ENOMEM; 1154ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1164ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1174ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (!ec_dev->irq) { 1184ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq); 11922f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return err; 1204ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1214ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1224ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, 1234ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass IRQF_TRIGGER_LOW | IRQF_ONESHOT, 1244ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass "chromeos-ec", ec_dev); 1254ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (err) { 1264ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err); 12722f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return err; 1284ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1294ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1304ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass err = mfd_add_devices(dev, 0, cros_devs, 1314ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ARRAY_SIZE(cros_devs), 1324ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass NULL, ec_dev->irq, NULL); 1334ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (err) { 1344ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_err(dev, "failed to add mfd devices\n"); 1354ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass goto fail_mfd; 1364ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1374ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1384ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_info(dev, "Chrome EC (%s)\n", ec_dev->name); 1394ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1404ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1414ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1424ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassfail_mfd: 1434ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass free_irq(ec_dev->irq, ec_dev); 14422f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones 1454ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return err; 1464ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1475ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_register); 1484ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1494ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_remove(struct cros_ec_device *ec_dev) 1504ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1514ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass mfd_remove_devices(ec_dev->dev); 1524ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass free_irq(ec_dev->irq, ec_dev); 1534ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1544ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1554ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1565ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_remove); 1574ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1584ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#ifdef CONFIG_PM_SLEEP 1594ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_suspend(struct cros_ec_device *ec_dev) 1604ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1614ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct device *dev = ec_dev->dev; 1624ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1634ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (device_may_wakeup(dev)) 1644ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); 1654ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1664ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass disable_irq(ec_dev->irq); 1674ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->was_wake_device = ec_dev->wake_enabled; 1684ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1694ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1704ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1715ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_suspend); 1724ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1734ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_resume(struct cros_ec_device *ec_dev) 1744ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1754ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass enable_irq(ec_dev->irq); 1764ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1774ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->wake_enabled) { 1784ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass disable_irq_wake(ec_dev->irq); 1794ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->wake_enabled = 0; 1804ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1814ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1824ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1834ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1845ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_resume); 1855ebeaff5c5be9f21680b91aac77ee155d935f888Samuel Ortiz 1864ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#endif 187