]>
Commit | Line | Data |
---|---|---|
9d230c9e DA |
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 | ||
97720706 DB |
19 | #define I2C_MAX_RETRIES 3 |
20 | ||
9d230c9e DA |
21 | /** |
22 | * struct ec_i2c_device - Driver data for I2C tunnel | |
23 | * | |
24 | * @dev: Device node | |
25 | * @adap: I2C adapter | |
26 | * @ec: Pointer to EC device | |
27 | * @remote_bus: The EC bus number we tunnel to on the other side. | |
28 | * @request_buf: Buffer for transmitting data; we expect most transfers to fit. | |
29 | * @response_buf: Buffer for receiving data; we expect most transfers to fit. | |
30 | */ | |
31 | ||
32 | struct ec_i2c_device { | |
33 | struct device *dev; | |
34 | struct i2c_adapter adap; | |
35 | struct cros_ec_device *ec; | |
36 | ||
37 | u16 remote_bus; | |
38 | ||
39 | u8 request_buf[256]; | |
40 | u8 response_buf[256]; | |
41 | }; | |
42 | ||
43 | /** | |
44 | * ec_i2c_count_message - Count bytes needed for ec_i2c_construct_message | |
45 | * | |
46 | * @i2c_msgs: The i2c messages to read | |
47 | * @num: The number of i2c messages. | |
48 | * | |
49 | * Returns the number of bytes the messages will take up. | |
50 | */ | |
51 | static int ec_i2c_count_message(const struct i2c_msg i2c_msgs[], int num) | |
52 | { | |
53 | int i; | |
54 | int size; | |
55 | ||
56 | size = sizeof(struct ec_params_i2c_passthru); | |
57 | size += num * sizeof(struct ec_params_i2c_passthru_msg); | |
58 | for (i = 0; i < num; i++) | |
59 | if (!(i2c_msgs[i].flags & I2C_M_RD)) | |
60 | size += i2c_msgs[i].len; | |
61 | ||
62 | return size; | |
63 | } | |
64 | ||
65 | /** | |
66 | * ec_i2c_construct_message - construct a message to go to the EC | |
67 | * | |
68 | * This function effectively stuffs the standard i2c_msg format of Linux into | |
69 | * a format that the EC understands. | |
70 | * | |
71 | * @buf: The buffer to fill. We assume that the buffer is big enough. | |
72 | * @i2c_msgs: The i2c messages to read. | |
73 | * @num: The number of i2c messages. | |
74 | * @bus_num: The remote bus number we want to talk to. | |
75 | * | |
76 | * Returns 0 or a negative error number. | |
77 | */ | |
78 | static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[], | |
79 | int num, u16 bus_num) | |
80 | { | |
81 | struct ec_params_i2c_passthru *params; | |
82 | u8 *out_data; | |
83 | int i; | |
84 | ||
85 | out_data = buf + sizeof(struct ec_params_i2c_passthru) + | |
86 | num * sizeof(struct ec_params_i2c_passthru_msg); | |
87 | ||
88 | params = (struct ec_params_i2c_passthru *)buf; | |
89 | params->port = bus_num; | |
90 | params->num_msgs = num; | |
91 | for (i = 0; i < num; i++) { | |
92 | const struct i2c_msg *i2c_msg = &i2c_msgs[i]; | |
93 | struct ec_params_i2c_passthru_msg *msg = ¶ms->msg[i]; | |
94 | ||
95 | msg->len = i2c_msg->len; | |
96 | msg->addr_flags = i2c_msg->addr; | |
97 | ||
98 | if (i2c_msg->flags & I2C_M_TEN) | |
d8e0a86f | 99 | return -EINVAL; |
9d230c9e DA |
100 | |
101 | if (i2c_msg->flags & I2C_M_RD) { | |
102 | msg->addr_flags |= EC_I2C_FLAG_READ; | |
103 | } else { | |
104 | memcpy(out_data, i2c_msg->buf, msg->len); | |
105 | out_data += msg->len; | |
106 | } | |
107 | } | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
112 | /** | |
113 | * ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response | |
114 | * | |
115 | * @i2c_msgs: The i2c messages to to fill up. | |
116 | * @num: The number of i2c messages expected. | |
117 | * | |
118 | * Returns the number of response bytes expeced. | |
119 | */ | |
120 | static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num) | |
121 | { | |
122 | int size; | |
123 | int i; | |
124 | ||
125 | size = sizeof(struct ec_response_i2c_passthru); | |
126 | for (i = 0; i < num; i++) | |
127 | if (i2c_msgs[i].flags & I2C_M_RD) | |
128 | size += i2c_msgs[i].len; | |
129 | ||
130 | return size; | |
131 | } | |
132 | ||
133 | /** | |
134 | * ec_i2c_parse_response - Parse a response from the EC | |
135 | * | |
136 | * We'll take the EC's response and copy it back into msgs. | |
137 | * | |
138 | * @buf: The buffer to parse. | |
139 | * @i2c_msgs: The i2c messages to to fill up. | |
140 | * @num: The number of i2c messages; will be modified to include the actual | |
141 | * number received. | |
142 | * | |
143 | * Returns 0 or a negative error number. | |
144 | */ | |
145 | static int ec_i2c_parse_response(const u8 *buf, struct i2c_msg i2c_msgs[], | |
146 | int *num) | |
147 | { | |
148 | const struct ec_response_i2c_passthru *resp; | |
149 | const u8 *in_data; | |
150 | int i; | |
151 | ||
152 | in_data = buf + sizeof(struct ec_response_i2c_passthru); | |
153 | ||
154 | resp = (const struct ec_response_i2c_passthru *)buf; | |
155 | if (resp->i2c_status & EC_I2C_STATUS_TIMEOUT) | |
156 | return -ETIMEDOUT; | |
157 | else if (resp->i2c_status & EC_I2C_STATUS_ERROR) | |
158 | return -EREMOTEIO; | |
159 | ||
160 | /* Other side could send us back fewer messages, but not more */ | |
161 | if (resp->num_msgs > *num) | |
162 | return -EPROTO; | |
163 | *num = resp->num_msgs; | |
164 | ||
165 | for (i = 0; i < *num; i++) { | |
166 | struct i2c_msg *i2c_msg = &i2c_msgs[i]; | |
167 | ||
168 | if (i2c_msgs[i].flags & I2C_M_RD) { | |
169 | memcpy(i2c_msg->buf, in_data, i2c_msg->len); | |
170 | in_data += i2c_msg->len; | |
171 | } | |
172 | } | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[], | |
178 | int num) | |
179 | { | |
180 | struct ec_i2c_device *bus = adap->algo_data; | |
181 | struct device *dev = bus->dev; | |
182 | const u16 bus_num = bus->remote_bus; | |
183 | int request_len; | |
184 | int response_len; | |
a8411784 | 185 | int alloc_size; |
9d230c9e | 186 | int result; |
a8411784 | 187 | struct cros_ec_command *msg; |
9d230c9e DA |
188 | |
189 | request_len = ec_i2c_count_message(i2c_msgs, num); | |
190 | if (request_len < 0) { | |
191 | dev_warn(dev, "Error constructing message %d\n", request_len); | |
1b84f2a4 | 192 | return request_len; |
9d230c9e | 193 | } |
1b84f2a4 | 194 | |
9d230c9e DA |
195 | response_len = ec_i2c_count_response(i2c_msgs, num); |
196 | if (response_len < 0) { | |
197 | /* Unexpected; no errors should come when NULL response */ | |
198 | dev_warn(dev, "Error preparing response %d\n", response_len); | |
1b84f2a4 | 199 | return response_len; |
9d230c9e DA |
200 | } |
201 | ||
a8411784 JMC |
202 | alloc_size = max(request_len, response_len); |
203 | msg = kmalloc(sizeof(*msg) + alloc_size, GFP_KERNEL); | |
204 | if (!msg) | |
205 | return -ENOMEM; | |
5799f95a | 206 | |
a8411784 JMC |
207 | result = ec_i2c_construct_message(msg->data, i2c_msgs, num, bus_num); |
208 | if (result) { | |
209 | dev_err(dev, "Error constructing EC i2c message %d\n", result); | |
210 | goto exit; | |
211 | } | |
5799f95a | 212 | |
a8411784 JMC |
213 | msg->version = 0; |
214 | msg->command = EC_CMD_I2C_PASSTHRU; | |
215 | msg->outsize = request_len; | |
216 | msg->insize = response_len; | |
9d230c9e | 217 | |
a8411784 JMC |
218 | result = cros_ec_cmd_xfer(bus->ec, msg); |
219 | if (result < 0) { | |
220 | dev_err(dev, "Error transferring EC i2c message %d\n", result); | |
221 | goto exit; | |
222 | } | |
223 | ||
224 | result = ec_i2c_parse_response(msg->data, i2c_msgs, &num); | |
225 | if (result < 0) { | |
226 | dev_err(dev, "Error parsing EC i2c message %d\n", result); | |
227 | goto exit; | |
228 | } | |
9d230c9e DA |
229 | |
230 | /* Indicate success by saying how many messages were sent */ | |
a8411784 JMC |
231 | result = num; |
232 | exit: | |
233 | kfree(msg); | |
234 | return result; | |
9d230c9e DA |
235 | } |
236 | ||
237 | static u32 ec_i2c_functionality(struct i2c_adapter *adap) | |
238 | { | |
239 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | |
240 | } | |
241 | ||
242 | static const struct i2c_algorithm ec_i2c_algorithm = { | |
243 | .master_xfer = ec_i2c_xfer, | |
244 | .functionality = ec_i2c_functionality, | |
245 | }; | |
246 | ||
247 | static int ec_i2c_probe(struct platform_device *pdev) | |
248 | { | |
249 | struct device_node *np = pdev->dev.of_node; | |
250 | struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); | |
251 | struct device *dev = &pdev->dev; | |
252 | struct ec_i2c_device *bus = NULL; | |
253 | u32 remote_bus; | |
254 | int err; | |
255 | ||
5799f95a | 256 | if (!ec->cmd_xfer) { |
9d230c9e DA |
257 | dev_err(dev, "Missing sendrecv\n"); |
258 | return -EINVAL; | |
259 | } | |
260 | ||
261 | bus = devm_kzalloc(dev, sizeof(*bus), GFP_KERNEL); | |
262 | if (bus == NULL) | |
263 | return -ENOMEM; | |
264 | ||
265 | err = of_property_read_u32(np, "google,remote-bus", &remote_bus); | |
266 | if (err) { | |
267 | dev_err(dev, "Couldn't read remote-bus property\n"); | |
268 | return err; | |
269 | } | |
270 | bus->remote_bus = remote_bus; | |
271 | ||
272 | bus->ec = ec; | |
273 | bus->dev = dev; | |
274 | ||
275 | bus->adap.owner = THIS_MODULE; | |
276 | strlcpy(bus->adap.name, "cros-ec-i2c-tunnel", sizeof(bus->adap.name)); | |
277 | bus->adap.algo = &ec_i2c_algorithm; | |
278 | bus->adap.algo_data = bus; | |
279 | bus->adap.dev.parent = &pdev->dev; | |
280 | bus->adap.dev.of_node = np; | |
97720706 | 281 | bus->adap.retries = I2C_MAX_RETRIES; |
9d230c9e DA |
282 | |
283 | err = i2c_add_adapter(&bus->adap); | |
284 | if (err) { | |
285 | dev_err(dev, "cannot register i2c adapter\n"); | |
286 | return err; | |
287 | } | |
288 | platform_set_drvdata(pdev, bus); | |
289 | ||
290 | return err; | |
291 | } | |
292 | ||
293 | static int ec_i2c_remove(struct platform_device *dev) | |
294 | { | |
295 | struct ec_i2c_device *bus = platform_get_drvdata(dev); | |
296 | ||
297 | i2c_del_adapter(&bus->adap); | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
6c97c9c1 SS |
302 | #ifdef CONFIG_OF |
303 | static const struct of_device_id cros_ec_i2c_of_match[] = { | |
304 | { .compatible = "google,cros-ec-i2c-tunnel" }, | |
305 | {}, | |
306 | }; | |
307 | MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match); | |
308 | #endif | |
309 | ||
9d230c9e DA |
310 | static struct platform_driver ec_i2c_tunnel_driver = { |
311 | .probe = ec_i2c_probe, | |
312 | .remove = ec_i2c_remove, | |
313 | .driver = { | |
314 | .name = "cros-ec-i2c-tunnel", | |
6c97c9c1 | 315 | .of_match_table = of_match_ptr(cros_ec_i2c_of_match), |
9d230c9e DA |
316 | }, |
317 | }; | |
318 | ||
319 | module_platform_driver(ec_i2c_tunnel_driver); | |
320 | ||
321 | MODULE_LICENSE("GPL"); | |
322 | MODULE_DESCRIPTION("EC I2C tunnel driver"); | |
323 | MODULE_ALIAS("platform:cros-ec-i2c-tunnel"); |