diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 9 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-cros-ec-tunnel.c | 318 | ||||
-rw-r--r-- | drivers/mfd/cros_ec.c | 5 |
4 files changed, 333 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 | ||
996 | config 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 | |||
996 | config SCx200_I2C | 1005 | config 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 |
96 | obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o | 96 | obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o |
97 | obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o | 97 | obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o |
98 | obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o | ||
98 | obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o | 99 | obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o |
99 | obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o | 100 | obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o |
100 | obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o | 101 | obj-$(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 | |||
30 | struct 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 | */ | ||
49 | static 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 | */ | ||
76 | static 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 = ¶ms->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 | */ | ||
118 | static 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 | */ | ||
143 | static 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 | |||
175 | static 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; | ||
233 | exit: | ||
234 | if (request != bus->request_buf) | ||
235 | kfree(request); | ||
236 | if (response != bus->response_buf) | ||
237 | kfree(response); | ||
238 | |||
239 | return result; | ||
240 | } | ||
241 | |||
242 | static u32 ec_i2c_functionality(struct i2c_adapter *adap) | ||
243 | { | ||
244 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
245 | } | ||
246 | |||
247 | static const struct i2c_algorithm ec_i2c_algorithm = { | ||
248 | .master_xfer = ec_i2c_xfer, | ||
249 | .functionality = ec_i2c_functionality, | ||
250 | }; | ||
251 | |||
252 | static 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 | |||
297 | static 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 | |||
306 | static 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 | |||
314 | module_platform_driver(ec_i2c_tunnel_driver); | ||
315 | |||
316 | MODULE_LICENSE("GPL"); | ||
317 | MODULE_DESCRIPTION("EC I2C tunnel driver"); | ||
318 | MODULE_ALIAS("platform:cros-ec-i2c-tunnel"); | ||
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index 8b6fb34c9b31..38fe9bf0d169 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c | |||
@@ -90,6 +90,11 @@ static const struct mfd_cell cros_devs[] = { | |||
90 | .id = 1, | 90 | .id = 1, |
91 | .of_compatible = "google,cros-ec-keyb", | 91 | .of_compatible = "google,cros-ec-keyb", |
92 | }, | 92 | }, |
93 | { | ||
94 | .name = "cros-ec-i2c-tunnel", | ||
95 | .id = 2, | ||
96 | .of_compatible = "google,cros-ec-i2c-tunnel", | ||
97 | }, | ||
93 | }; | 98 | }; |
94 | 99 | ||
95 | int cros_ec_register(struct cros_ec_device *ec_dev) | 100 | int cros_ec_register(struct cros_ec_device *ec_dev) |