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

mfd: Add ChromeOS EC I2C driver

This uses an I2C bus to talk to the ChromeOS EC. The protocol
is defined by the EC and is fairly simple, with a length byte,
checksum, command byte and version byte (to permit easy creation
of new commands).

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Che-Liang Chiou <clchiou@chromium.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

authored by

Simon Glass and committed by
Samuel Ortiz
89969009 4ab6174e

+212
+10
drivers/mfd/Kconfig
··· 29 29 You also ned to enable the driver for the bus you are using. The 30 30 protocol for talking to the EC is defined by the bus driver. 31 31 32 + config MFD_CROS_EC_I2C 33 + tristate "ChromeOS Embedded Controller (I2C)" 34 + depends on MFD_CROS_EC && I2C 35 + 36 + help 37 + If you say Y here, you get support for talking to the ChromeOS 38 + EC through an I2C bus. This uses a simple byte-level protocol with 39 + a checksum. Failing accesses will be retried three times to 40 + improve reliability. 41 + 32 42 config MFD_88PM800 33 43 tristate "Support Marvell 88PM800" 34 44 depends on I2C=y && GENERIC_HARDIRQS
+1
drivers/mfd/Makefile
··· 9 9 obj-$(CONFIG_MFD_SM501) += sm501.o 10 10 obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o 11 11 obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o 12 + obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o 12 13 13 14 rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o 14 15 obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
+201
drivers/mfd/cros_ec_i2c.c
··· 1 + /* 2 + * ChromeOS EC multi-function device (I2C) 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 + 16 + #include <linux/kernel.h> 17 + #include <linux/module.h> 18 + #include <linux/i2c.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/mfd/cros_ec.h> 21 + #include <linux/mfd/cros_ec_commands.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/slab.h> 24 + 25 + static inline struct cros_ec_device *to_ec_dev(struct device *dev) 26 + { 27 + struct i2c_client *client = to_i2c_client(dev); 28 + 29 + return i2c_get_clientdata(client); 30 + } 31 + 32 + static int cros_ec_command_xfer(struct cros_ec_device *ec_dev, 33 + struct cros_ec_msg *msg) 34 + { 35 + struct i2c_client *client = ec_dev->priv; 36 + int ret = -ENOMEM; 37 + int i; 38 + int packet_len; 39 + u8 *out_buf = NULL; 40 + u8 *in_buf = NULL; 41 + u8 sum; 42 + struct i2c_msg i2c_msg[2]; 43 + 44 + i2c_msg[0].addr = client->addr; 45 + i2c_msg[0].flags = 0; 46 + i2c_msg[1].addr = client->addr; 47 + i2c_msg[1].flags = I2C_M_RD; 48 + 49 + /* 50 + * allocate larger packet (one byte for checksum, one byte for 51 + * length, and one for result code) 52 + */ 53 + packet_len = msg->in_len + 3; 54 + in_buf = kzalloc(packet_len, GFP_KERNEL); 55 + if (!in_buf) 56 + goto done; 57 + i2c_msg[1].len = packet_len; 58 + i2c_msg[1].buf = (char *)in_buf; 59 + 60 + /* 61 + * allocate larger packet (one byte for checksum, one for 62 + * command code, one for length, and one for command version) 63 + */ 64 + packet_len = msg->out_len + 4; 65 + out_buf = kzalloc(packet_len, GFP_KERNEL); 66 + if (!out_buf) 67 + goto done; 68 + i2c_msg[0].len = packet_len; 69 + i2c_msg[0].buf = (char *)out_buf; 70 + 71 + out_buf[0] = EC_CMD_VERSION0 + msg->version; 72 + out_buf[1] = msg->cmd; 73 + out_buf[2] = msg->out_len; 74 + 75 + /* copy message payload and compute checksum */ 76 + sum = out_buf[0] + out_buf[1] + out_buf[2]; 77 + for (i = 0; i < msg->out_len; i++) { 78 + out_buf[3 + i] = msg->out_buf[i]; 79 + sum += out_buf[3 + i]; 80 + } 81 + out_buf[3 + msg->out_len] = sum; 82 + 83 + /* send command to EC and read answer */ 84 + ret = i2c_transfer(client->adapter, i2c_msg, 2); 85 + if (ret < 0) { 86 + dev_err(ec_dev->dev, "i2c transfer failed: %d\n", ret); 87 + goto done; 88 + } else if (ret != 2) { 89 + dev_err(ec_dev->dev, "failed to get response: %d\n", ret); 90 + ret = -EIO; 91 + goto done; 92 + } 93 + 94 + /* check response error code */ 95 + if (i2c_msg[1].buf[0]) { 96 + dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", 97 + msg->cmd, i2c_msg[1].buf[0]); 98 + ret = -EINVAL; 99 + goto done; 100 + } 101 + 102 + /* copy response packet payload and compute checksum */ 103 + sum = in_buf[0] + in_buf[1]; 104 + for (i = 0; i < msg->in_len; i++) { 105 + msg->in_buf[i] = in_buf[2 + i]; 106 + sum += in_buf[2 + i]; 107 + } 108 + dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n", 109 + i2c_msg[1].len, in_buf, sum); 110 + if (sum != in_buf[2 + msg->in_len]) { 111 + dev_err(ec_dev->dev, "bad packet checksum\n"); 112 + ret = -EBADMSG; 113 + goto done; 114 + } 115 + 116 + ret = 0; 117 + done: 118 + kfree(in_buf); 119 + kfree(out_buf); 120 + return ret; 121 + } 122 + 123 + static int cros_ec_probe_i2c(struct i2c_client *client, 124 + const struct i2c_device_id *dev_id) 125 + { 126 + struct device *dev = &client->dev; 127 + struct cros_ec_device *ec_dev = NULL; 128 + int err; 129 + 130 + ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); 131 + if (!ec_dev) 132 + return -ENOMEM; 133 + 134 + i2c_set_clientdata(client, ec_dev); 135 + ec_dev->name = "I2C"; 136 + ec_dev->dev = dev; 137 + ec_dev->priv = client; 138 + ec_dev->irq = client->irq; 139 + ec_dev->command_xfer = cros_ec_command_xfer; 140 + ec_dev->ec_name = client->name; 141 + ec_dev->phys_name = client->adapter->name; 142 + ec_dev->parent = &client->dev; 143 + 144 + err = cros_ec_register(ec_dev); 145 + if (err) { 146 + dev_err(dev, "cannot register EC\n"); 147 + return err; 148 + } 149 + 150 + return 0; 151 + } 152 + 153 + static int cros_ec_remove_i2c(struct i2c_client *client) 154 + { 155 + struct cros_ec_device *ec_dev = i2c_get_clientdata(client); 156 + 157 + cros_ec_remove(ec_dev); 158 + 159 + return 0; 160 + } 161 + 162 + #ifdef CONFIG_PM_SLEEP 163 + static int cros_ec_i2c_suspend(struct device *dev) 164 + { 165 + struct cros_ec_device *ec_dev = to_ec_dev(dev); 166 + 167 + return cros_ec_suspend(ec_dev); 168 + } 169 + 170 + static int cros_ec_i2c_resume(struct device *dev) 171 + { 172 + struct cros_ec_device *ec_dev = to_ec_dev(dev); 173 + 174 + return cros_ec_resume(ec_dev); 175 + } 176 + #endif 177 + 178 + static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend, 179 + cros_ec_i2c_resume); 180 + 181 + static const struct i2c_device_id cros_ec_i2c_id[] = { 182 + { "cros-ec-i2c", 0 }, 183 + { } 184 + }; 185 + MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id); 186 + 187 + static struct i2c_driver cros_ec_driver = { 188 + .driver = { 189 + .name = "cros-ec-i2c", 190 + .owner = THIS_MODULE, 191 + .pm = &cros_ec_i2c_pm_ops, 192 + }, 193 + .probe = cros_ec_probe_i2c, 194 + .remove = cros_ec_remove_i2c, 195 + .id_table = cros_ec_i2c_id, 196 + }; 197 + 198 + module_i2c_driver(cros_ec_driver); 199 + 200 + MODULE_LICENSE("GPL"); 201 + MODULE_DESCRIPTION("ChromeOS EC multi function device");