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> 26d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker#include <linux/delay.h> 27d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker 28d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker#define EC_COMMAND_RETRIES 50 294ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 304ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_prepare_tx(struct cros_ec_device *ec_dev, 315d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson struct cros_ec_command *msg) 324ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 334ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass uint8_t *out; 344ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass int csum, i; 354ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 365d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); 374ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out = ec_dev->dout; 384ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass out[0] = EC_CMD_VERSION0 + msg->version; 395d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson out[1] = msg->command; 405d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson out[2] = msg->outsize; 414ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass csum = out[0] + out[1] + out[2]; 425d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson for (i = 0; i < msg->outsize; i++) 435d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i]; 445d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff); 454ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 465d4773e27e8ab37491767a6ef99ffd7100fe6341Bill Richardson return EC_MSG_TX_PROTO_BYTES + msg->outsize; 474ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 485ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_prepare_tx); 494ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 506db07b6336589ff480528173e41f8f6af3f0097fBill Richardsonint cros_ec_check_result(struct cros_ec_device *ec_dev, 516db07b6336589ff480528173e41f8f6af3f0097fBill Richardson struct cros_ec_command *msg) 526db07b6336589ff480528173e41f8f6af3f0097fBill Richardson{ 536db07b6336589ff480528173e41f8f6af3f0097fBill Richardson switch (msg->result) { 546db07b6336589ff480528173e41f8f6af3f0097fBill Richardson case EC_RES_SUCCESS: 556db07b6336589ff480528173e41f8f6af3f0097fBill Richardson return 0; 566db07b6336589ff480528173e41f8f6af3f0097fBill Richardson case EC_RES_IN_PROGRESS: 576db07b6336589ff480528173e41f8f6af3f0097fBill Richardson dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", 586db07b6336589ff480528173e41f8f6af3f0097fBill Richardson msg->command); 596db07b6336589ff480528173e41f8f6af3f0097fBill Richardson return -EAGAIN; 606db07b6336589ff480528173e41f8f6af3f0097fBill Richardson default: 616db07b6336589ff480528173e41f8f6af3f0097fBill Richardson dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", 626db07b6336589ff480528173e41f8f6af3f0097fBill Richardson msg->command, msg->result); 636db07b6336589ff480528173e41f8f6af3f0097fBill Richardson return 0; 646db07b6336589ff480528173e41f8f6af3f0097fBill Richardson } 656db07b6336589ff480528173e41f8f6af3f0097fBill Richardson} 666db07b6336589ff480528173e41f8f6af3f0097fBill RichardsonEXPORT_SYMBOL(cros_ec_check_result); 676db07b6336589ff480528173e41f8f6af3f0097fBill Richardson 68a6551a76fff15056fde2342d0f7de41ee605264eAndrew Brestickerint cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, 69a6551a76fff15056fde2342d0f7de41ee605264eAndrew Bresticker struct cros_ec_command *msg) 70a6551a76fff15056fde2342d0f7de41ee605264eAndrew Bresticker{ 7163427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker int ret; 7263427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker 7363427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker mutex_lock(&ec_dev->lock); 7463427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker ret = ec_dev->cmd_xfer(ec_dev, msg); 75d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker if (msg->result == EC_RES_IN_PROGRESS) { 76d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker int i; 77d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker struct cros_ec_command status_msg; 78d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker struct ec_response_get_comms_status status; 79d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker 80d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker status_msg.version = 0; 81d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker status_msg.command = EC_CMD_GET_COMMS_STATUS; 82d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker status_msg.outdata = NULL; 83d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker status_msg.outsize = 0; 84d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker status_msg.indata = (uint8_t *)&status; 85d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker status_msg.insize = sizeof(status); 86d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker 87d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker /* 88d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker * Query the EC's status until it's no longer busy or 89d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker * we encounter an error. 90d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker */ 91d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker for (i = 0; i < EC_COMMAND_RETRIES; i++) { 92d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker usleep_range(10000, 11000); 93d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker 94d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker ret = ec_dev->cmd_xfer(ec_dev, &status_msg); 95d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker if (ret < 0) 96d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker break; 97d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker 98d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker msg->result = status_msg.result; 99d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker if (status_msg.result != EC_RES_SUCCESS) 100d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker break; 101d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker if (!(status.flags & EC_COMMS_STATUS_PROCESSING)) 102d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker break; 103d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker } 104d86c21fd31114e3ef9fae64be335c76aa22859dcAndrew Bresticker } 10563427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker mutex_unlock(&ec_dev->lock); 10663427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker 10763427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker return ret; 108a6551a76fff15056fde2342d0f7de41ee605264eAndrew Bresticker} 109a6551a76fff15056fde2342d0f7de41ee605264eAndrew BrestickerEXPORT_SYMBOL(cros_ec_cmd_xfer); 110a6551a76fff15056fde2342d0f7de41ee605264eAndrew Bresticker 1115ac98553afe41ffb5513fa8aac6df699a70231a3Geert Uytterhoevenstatic const struct mfd_cell cros_devs[] = { 1124ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass { 1134ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass .name = "cros-ec-keyb", 1144ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass .id = 1, 1154ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass .of_compatible = "google,cros-ec-keyb", 1164ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass }, 1179d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson { 1189d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson .name = "cros-ec-i2c-tunnel", 1199d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson .id = 2, 1209d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson .of_compatible = "google,cros-ec-i2c-tunnel", 1219d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796Doug Anderson }, 1224ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass}; 1234ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1244ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_register(struct cros_ec_device *ec_dev) 1254ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1264ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct device *dev = ec_dev->dev; 1274ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass int err = 0; 1284ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1294ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->din_size) { 13022f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 13122f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones if (!ec_dev->din) 13222f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return -ENOMEM; 1334ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1344ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->dout_size) { 13522f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 13622f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones if (!ec_dev->dout) 13722f9ee756bfd9ce9b066e2f90cf9f002aa7d1d44Lee Jones return -ENOMEM; 1384ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1394ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 14063427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker mutex_init(&ec_dev->lock); 14163427530fa7a78b42a19f47fb0c12b303c0666caAndrew Bresticker 1424ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass err = mfd_add_devices(dev, 0, cros_devs, 1434ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ARRAY_SIZE(cros_devs), 1444ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass NULL, ec_dev->irq, NULL); 1454ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (err) { 1464ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass dev_err(dev, "failed to add mfd devices\n"); 147d1fd345e2087f0362c92bd3b0a1cea7fe636ac3aAndrew Bresticker return err; 1484ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1494ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 150533cec8f34778de10412dfabac991cf458ebf3c9Bill Richardson dev_info(dev, "Chrome EC device registered\n"); 1514ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1524ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1534ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1545ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_register); 1554ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1564ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_remove(struct cros_ec_device *ec_dev) 1574ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1584ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass mfd_remove_devices(ec_dev->dev); 1594ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1604ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1614ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1625ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_remove); 1634ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1644ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#ifdef CONFIG_PM_SLEEP 1654ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_suspend(struct cros_ec_device *ec_dev) 1664ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1674ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass struct device *dev = ec_dev->dev; 1684ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1694ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (device_may_wakeup(dev)) 1704ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); 1714ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1724ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass disable_irq(ec_dev->irq); 1734ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->was_wake_device = ec_dev->wake_enabled; 1744ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1754ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1764ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1775ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_suspend); 1784ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1794ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glassint cros_ec_resume(struct cros_ec_device *ec_dev) 1804ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass{ 1814ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass enable_irq(ec_dev->irq); 1824ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1834ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass if (ec_dev->wake_enabled) { 1844ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass disable_irq_wake(ec_dev->irq); 1854ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass ec_dev->wake_enabled = 0; 1864ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass } 1874ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass 1884ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass return 0; 1894ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass} 1905ebeaff5c5be9f21680b91aac77ee155d935f888Samuel OrtizEXPORT_SYMBOL(cros_ec_resume); 1915ebeaff5c5be9f21680b91aac77ee155d935f888Samuel Ortiz 1924ab6174e8cdb007cf500e484bdf454b8d14d524aSimon Glass#endif 193a865a589144da6dbf26750b7193a9748da159305Bill Richardson 194a865a589144da6dbf26750b7193a9748da159305Bill RichardsonMODULE_LICENSE("GPL"); 195a865a589144da6dbf26750b7193a9748da159305Bill RichardsonMODULE_DESCRIPTION("ChromeOS EC core driver"); 196