aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2013-02-25 17:08:38 -0500
committerSamuel Ortiz <sameo@linux.intel.com>2013-04-05 05:20:13 -0400
commit89969009485fa9e62814afaa438c12c45d7d2def (patch)
tree91fda02a31a094727ade6f8bb35aebdd392b9e61
parent4ab6174e8cdb007cf500e484bdf454b8d14d524a (diff)
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>
-rw-r--r--drivers/mfd/Kconfig10
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/cros_ec_i2c.c201
3 files changed, 212 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index a8bafb560196..4e54b5b01295 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -29,6 +29,16 @@ config MFD_CROS_EC
29 You also ned to enable the driver for the bus you are using. The 29 You also ned to enable the driver for the bus you are using. The
30 protocol for talking to the EC is defined by the bus driver. 30 protocol for talking to the EC is defined by the bus driver.
31 31
32config 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
32config MFD_88PM800 42config MFD_88PM800
33 tristate "Support Marvell 88PM800" 43 tristate "Support Marvell 88PM800"
34 depends on I2C=y && GENERIC_HARDIRQS 44 depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 967767d3d759..895257bec849 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o
9obj-$(CONFIG_MFD_SM501) += sm501.o 9obj-$(CONFIG_MFD_SM501) += sm501.o
10obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o 10obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o
11obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o 11obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o
12obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
12 13
13rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o 14rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o
14obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o 15obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c
new file mode 100644
index 000000000000..123044608b63
--- /dev/null
+++ b/drivers/mfd/cros_ec_i2c.c
@@ -0,0 +1,201 @@
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
25static 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
32static 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
123static 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
153static 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
163static 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
170static 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
178static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend,
179 cros_ec_i2c_resume);
180
181static const struct i2c_device_id cros_ec_i2c_id[] = {
182 { "cros-ec-i2c", 0 },
183 { }
184};
185MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id);
186
187static 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
198module_i2c_driver(cros_ec_driver);
199
200MODULE_LICENSE("GPL");
201MODULE_DESCRIPTION("ChromeOS EC multi function device");