aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorDoug Anderson <dianders@chromium.org>2014-04-30 13:44:09 -0400
committerLee Jones <lee.jones@linaro.org>2014-06-03 03:11:49 -0400
commit9d230c9e4f4e67cb1c1cb9e0f6142da16b0f2796 (patch)
tree92a9a0a098a25f9b779bc22f8b47e01464860d94 /drivers/i2c
parent5271db29d7199fe0ffb303ca4bbbb1485bba28c3 (diff)
i2c: ChromeOS EC tunnel driver
On ARM Chromebooks we have a few devices that are accessed by both the AP (the main "Application Processor") and the EC (the Embedded Controller). These are: * The battery (sbs-battery). * The power management unit tps65090. On the original Samsung ARM Chromebook these devices were on an I2C bus that was shared between the AP and the EC and arbitrated using some extranal GPIOs (see i2c-arb-gpio-challenge). The original arbitration scheme worked well enough but had some downsides: * It was nonstandard (not using standard I2C multimaster) * It only worked if the EC-AP communication was I2C * It was relatively hard to debug problems (hard to tell if i2c issues were caused by the EC, the AP, or some device on the bus). On the HP Chromebook 11 the design was changed to: * The AP/EC comms were still i2c, but the battery/tps65090 were no longer on the bus used for AP/EC communication. The battery was exposed to the AP through a limited i2c tunnel and tps65090 was exposed to the AP through a custom Linux driver. On the Samsung ARM Chromebook 2 the scheme is changed yet again, now: * The AP/EC comms are now using SPI for faster speeds. * The EC's i2c bus is exposed to the AP through a full i2c tunnel. The upstream "tegra124-venice2" uses the same scheme as the Samsung ARM Chromebook 2, though it has a different set of components on the other side of the bus. This driver supports the scheme used by the Samsung ARM Chromebook 2. Future patches to this driver could add support for the battery tunnel on the HP Chromebook 11 (and perhaps could even be used to access tps65090 on the HP Chromebook 11 instead of using a special driver, but I haven't researched that enough). Signed-off-by: Vincent Palatin <vpalatin@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> Signed-off-by: Doug Anderson <dianders@chromium.org> Reviewed-by: Wolfram Sang <wsa@the-dreams.de> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/Kconfig9
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-cros-ec-tunnel.c318
3 files changed, 328 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c94db1c5e353..9a0a6cc7f4ba 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -993,6 +993,15 @@ config I2C_SIBYTE
993 help 993 help
994 Supports the SiByte SOC on-chip I2C interfaces (2 channels). 994 Supports the SiByte SOC on-chip I2C interfaces (2 channels).
995 995
996config I2C_CROS_EC_TUNNEL
997 tristate "ChromeOS EC tunnel I2C bus"
998 depends on MFD_CROS_EC
999 help
1000 If you say yes here you get an I2C bus that will tunnel i2c commands
1001 through to the other side of the ChromeOS EC to the i2c bus
1002 connected there. This will work whatever the interface used to
1003 talk to the EC (SPI, I2C or LPC).
1004
996config SCx200_I2C 1005config SCx200_I2C
997 tristate "NatSemi SCx200 I2C using GPIO pins (DEPRECATED)" 1006 tristate "NatSemi SCx200 I2C using GPIO pins (DEPRECATED)"
998 depends on SCx200_GPIO 1007 depends on SCx200_GPIO
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 18d18ff9db93..e110ca932918 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_I2C_VIPERBOARD) += i2c-viperboard.o
95# Other I2C/SMBus bus drivers 95# Other I2C/SMBus bus drivers
96obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o 96obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
97obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o 97obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o
98obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
98obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o 99obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
99obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o 100obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
100obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o 101obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
new file mode 100644
index 000000000000..8e7a71487bb1
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
@@ -0,0 +1,318 @@
1/*
2 * Copyright (C) 2013 Google, Inc
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * Expose an I2C passthrough to the ChromeOS EC.
10 */
11
12#include <linux/module.h>
13#include <linux/i2c.h>
14#include <linux/mfd/cros_ec.h>
15#include <linux/mfd/cros_ec_commands.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18
19/**
20 * struct ec_i2c_device - Driver data for I2C tunnel
21 *
22 * @dev: Device node
23 * @adap: I2C adapter
24 * @ec: Pointer to EC device
25 * @remote_bus: The EC bus number we tunnel to on the other side.
26 * @request_buf: Buffer for transmitting data; we expect most transfers to fit.
27 * @response_buf: Buffer for receiving data; we expect most transfers to fit.
28 */
29
30struct ec_i2c_device {
31 struct device *dev;
32 struct i2c_adapter adap;
33 struct cros_ec_device *ec;
34
35 u16 remote_bus;
36
37 u8 request_buf[256];
38 u8 response_buf[256];
39};
40
41/**
42 * ec_i2c_count_message - Count bytes needed for ec_i2c_construct_message
43 *
44 * @i2c_msgs: The i2c messages to read
45 * @num: The number of i2c messages.
46 *
47 * Returns the number of bytes the messages will take up.
48 */
49static int ec_i2c_count_message(const struct i2c_msg i2c_msgs[], int num)
50{
51 int i;
52 int size;
53
54 size = sizeof(struct ec_params_i2c_passthru);
55 size += num * sizeof(struct ec_params_i2c_passthru_msg);
56 for (i = 0; i < num; i++)
57 if (!(i2c_msgs[i].flags & I2C_M_RD))
58 size += i2c_msgs[i].len;
59
60 return size;
61}
62
63/**
64 * ec_i2c_construct_message - construct a message to go to the EC
65 *
66 * This function effectively stuffs the standard i2c_msg format of Linux into
67 * a format that the EC understands.
68 *
69 * @buf: The buffer to fill. We assume that the buffer is big enough.
70 * @i2c_msgs: The i2c messages to read.
71 * @num: The number of i2c messages.
72 * @bus_num: The remote bus number we want to talk to.
73 *
74 * Returns 0 or a negative error number.
75 */
76static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[],
77 int num, u16 bus_num)
78{
79 struct ec_params_i2c_passthru *params;
80 u8 *out_data;
81 int i;
82
83 out_data = buf + sizeof(struct ec_params_i2c_passthru) +
84 num * sizeof(struct ec_params_i2c_passthru_msg);
85
86 params = (struct ec_params_i2c_passthru *)buf;
87 params->port = bus_num;
88 params->num_msgs = num;
89 for (i = 0; i < num; i++) {
90 const struct i2c_msg *i2c_msg = &i2c_msgs[i];
91 struct ec_params_i2c_passthru_msg *msg = &params->msg[i];
92
93 msg->len = i2c_msg->len;
94 msg->addr_flags = i2c_msg->addr;
95
96 if (i2c_msg->flags & I2C_M_TEN)
97 msg->addr_flags |= EC_I2C_FLAG_10BIT;
98
99 if (i2c_msg->flags & I2C_M_RD) {
100 msg->addr_flags |= EC_I2C_FLAG_READ;
101 } else {
102 memcpy(out_data, i2c_msg->buf, msg->len);
103 out_data += msg->len;
104 }
105 }
106
107 return 0;
108}
109
110/**
111 * ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response
112 *
113 * @i2c_msgs: The i2c messages to to fill up.
114 * @num: The number of i2c messages expected.
115 *
116 * Returns the number of response bytes expeced.
117 */
118static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num)
119{
120 int size;
121 int i;
122
123 size = sizeof(struct ec_response_i2c_passthru);
124 for (i = 0; i < num; i++)
125 if (i2c_msgs[i].flags & I2C_M_RD)
126 size += i2c_msgs[i].len;
127
128 return size;
129}
130
131/**
132 * ec_i2c_parse_response - Parse a response from the EC
133 *
134 * We'll take the EC's response and copy it back into msgs.
135 *
136 * @buf: The buffer to parse.
137 * @i2c_msgs: The i2c messages to to fill up.
138 * @num: The number of i2c messages; will be modified to include the actual
139 * number received.
140 *
141 * Returns 0 or a negative error number.
142 */
143static int ec_i2c_parse_response(const u8 *buf, struct i2c_msg i2c_msgs[],
144 int *num)
145{
146 const struct ec_response_i2c_passthru *resp;
147 const u8 *in_data;
148 int i;
149
150 in_data = buf + sizeof(struct ec_response_i2c_passthru);
151
152 resp = (const struct ec_response_i2c_passthru *)buf;
153 if (resp->i2c_status & EC_I2C_STATUS_TIMEOUT)
154 return -ETIMEDOUT;
155 else if (resp->i2c_status & EC_I2C_STATUS_ERROR)
156 return -EREMOTEIO;
157
158 /* Other side could send us back fewer messages, but not more */
159 if (resp->num_msgs > *num)
160 return -EPROTO;
161 *num = resp->num_msgs;
162
163 for (i = 0; i < *num; i++) {
164 struct i2c_msg *i2c_msg = &i2c_msgs[i];
165
166 if (i2c_msgs[i].flags & I2C_M_RD) {
167 memcpy(i2c_msg->buf, in_data, i2c_msg->len);
168 in_data += i2c_msg->len;
169 }
170 }
171
172 return 0;
173}
174
175static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
176 int num)
177{
178 struct ec_i2c_device *bus = adap->algo_data;
179 struct device *dev = bus->dev;
180 const u16 bus_num = bus->remote_bus;
181 int request_len;
182 int response_len;
183 u8 *request = NULL;
184 u8 *response = NULL;
185 int result;
186
187 request_len = ec_i2c_count_message(i2c_msgs, num);
188 if (request_len < 0) {
189 dev_warn(dev, "Error constructing message %d\n", request_len);
190 result = request_len;
191 goto exit;
192 }
193 response_len = ec_i2c_count_response(i2c_msgs, num);
194 if (response_len < 0) {
195 /* Unexpected; no errors should come when NULL response */
196 dev_warn(dev, "Error preparing response %d\n", response_len);
197 result = response_len;
198 goto exit;
199 }
200
201 if (request_len <= ARRAY_SIZE(bus->request_buf)) {
202 request = bus->request_buf;
203 } else {
204 request = kzalloc(request_len, GFP_KERNEL);
205 if (request == NULL) {
206 result = -ENOMEM;
207 goto exit;
208 }
209 }
210 if (response_len <= ARRAY_SIZE(bus->response_buf)) {
211 response = bus->response_buf;
212 } else {
213 response = kzalloc(response_len, GFP_KERNEL);
214 if (response == NULL) {
215 result = -ENOMEM;
216 goto exit;
217 }
218 }
219
220 ec_i2c_construct_message(request, i2c_msgs, num, bus_num);
221 result = bus->ec->command_sendrecv(bus->ec, EC_CMD_I2C_PASSTHRU,
222 request, request_len,
223 response, response_len);
224 if (result)
225 goto exit;
226
227 result = ec_i2c_parse_response(response, i2c_msgs, &num);
228 if (result < 0)
229 goto exit;
230
231 /* Indicate success by saying how many messages were sent */
232 result = num;
233exit:
234 if (request != bus->request_buf)
235 kfree(request);
236 if (response != bus->response_buf)
237 kfree(response);
238
239 return result;
240}
241
242static u32 ec_i2c_functionality(struct i2c_adapter *adap)
243{
244 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
245}
246
247static const struct i2c_algorithm ec_i2c_algorithm = {
248 .master_xfer = ec_i2c_xfer,
249 .functionality = ec_i2c_functionality,
250};
251
252static int ec_i2c_probe(struct platform_device *pdev)
253{
254 struct device_node *np = pdev->dev.of_node;
255 struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
256 struct device *dev = &pdev->dev;
257 struct ec_i2c_device *bus = NULL;
258 u32 remote_bus;
259 int err;
260
261 if (!ec->command_sendrecv) {
262 dev_err(dev, "Missing sendrecv\n");
263 return -EINVAL;
264 }
265
266 bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL);
267 if (bus == NULL)
268 return -ENOMEM;
269
270 err = of_property_read_u32(np, "google,remote-bus", &remote_bus);
271 if (err) {
272 dev_err(dev, "Couldn't read remote-bus property\n");
273 return err;
274 }
275 bus->remote_bus = remote_bus;
276
277 bus->ec = ec;
278 bus->dev = dev;
279
280 bus->adap.owner = THIS_MODULE;
281 strlcpy(bus->adap.name, "cros-ec-i2c-tunnel", sizeof(bus->adap.name));
282 bus->adap.algo = &ec_i2c_algorithm;
283 bus->adap.algo_data = bus;
284 bus->adap.dev.parent = &pdev->dev;
285 bus->adap.dev.of_node = np;
286
287 err = i2c_add_adapter(&bus->adap);
288 if (err) {
289 dev_err(dev, "cannot register i2c adapter\n");
290 return err;
291 }
292 platform_set_drvdata(pdev, bus);
293
294 return err;
295}
296
297static int ec_i2c_remove(struct platform_device *dev)
298{
299 struct ec_i2c_device *bus = platform_get_drvdata(dev);
300
301 i2c_del_adapter(&bus->adap);
302
303 return 0;
304}
305
306static struct platform_driver ec_i2c_tunnel_driver = {
307 .probe = ec_i2c_probe,
308 .remove = ec_i2c_remove,
309 .driver = {
310 .name = "cros-ec-i2c-tunnel",
311 },
312};
313
314module_platform_driver(ec_i2c_tunnel_driver);
315
316MODULE_LICENSE("GPL");
317MODULE_DESCRIPTION("EC I2C tunnel driver");
318MODULE_ALIAS("platform:cros-ec-i2c-tunnel");