diff options
author | Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> | 2016-08-07 05:25:34 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2016-08-17 05:13:07 -0400 |
commit | 3703f53b99e4a7c373ce3568dd3f91f175ebb626 (patch) | |
tree | dd49617d6ba5537349792f1e0349957912491186 | |
parent | 17e2adf2a7d755e17fa75495d29cb98d553d4a66 (diff) |
HID: intel_ish-hid: ISH Transport layer
The ISH transport layer (ishtp) is a bi-directional protocol implemented
on the top of PCI based inter processor communication layer. This layer
offers:
- Connection management
- Flow control with the firmware
- Multiple client sessions
- Client message transfer
- Client message reception
- DMA for RX and TX for fast data transfer
Refer to Documentation/hid/intel-ish-hid.txt for
overview of the functionality implemented in this layer.
Original-author: Daniel Drubin <daniel.drubin@intel.com>
Reviewed-and-tested-by: Ooi, Joyce <joyce.ooi@intel.com>
Tested-by: Grant Likely <grant.likely@secretlab.ca>
Tested-by: Rann Bar-On <rb6@duke.edu>
Tested-by: Atri Bhattacharya <badshah400@aim.com>
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/Kconfig | 2 | ||||
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/Kconfig | 17 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/Makefile | 12 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/bus.c | 791 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/bus.h | 114 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/client-buffers.c | 258 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/client.c | 1054 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/client.h | 182 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/dma-if.c | 175 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/hbm.c | 1032 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/hbm.h | 321 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/init.c | 115 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h | 277 |
14 files changed, 4352 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 78ac4811bd3c..5f590e16e41b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -967,4 +967,6 @@ source "drivers/hid/usbhid/Kconfig" | |||
967 | 967 | ||
968 | source "drivers/hid/i2c-hid/Kconfig" | 968 | source "drivers/hid/i2c-hid/Kconfig" |
969 | 969 | ||
970 | source "drivers/hid/intel-ish-hid/Kconfig" | ||
971 | |||
970 | endmenu | 972 | endmenu |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fc4b2aa47f2e..86b2b5785fd2 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -113,3 +113,5 @@ obj-$(CONFIG_USB_MOUSE) += usbhid/ | |||
113 | obj-$(CONFIG_USB_KBD) += usbhid/ | 113 | obj-$(CONFIG_USB_KBD) += usbhid/ |
114 | 114 | ||
115 | obj-$(CONFIG_I2C_HID) += i2c-hid/ | 115 | obj-$(CONFIG_I2C_HID) += i2c-hid/ |
116 | |||
117 | obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/ | ||
diff --git a/drivers/hid/intel-ish-hid/Kconfig b/drivers/hid/intel-ish-hid/Kconfig new file mode 100644 index 000000000000..ea065b3684a2 --- /dev/null +++ b/drivers/hid/intel-ish-hid/Kconfig | |||
@@ -0,0 +1,17 @@ | |||
1 | menu "Intel ISH HID support" | ||
2 | depends on X86_64 && PCI | ||
3 | |||
4 | config INTEL_ISH_HID | ||
5 | tristate "Intel Integrated Sensor Hub" | ||
6 | default n | ||
7 | select HID | ||
8 | help | ||
9 | The Integrated Sensor Hub (ISH) enables the ability to offload | ||
10 | sensor polling and algorithm processing to a dedicated low power | ||
11 | processor in the chipset. This allows the core processor to go into | ||
12 | low power modes more often, resulting in the increased battery life. | ||
13 | The current processors that support ISH are: Cherrytrail, Skylake, | ||
14 | Broxton and Kaby Lake. | ||
15 | |||
16 | Say Y here if you want to support Intel ISH. If unsure, say N. | ||
17 | endmenu | ||
diff --git a/drivers/hid/intel-ish-hid/Makefile b/drivers/hid/intel-ish-hid/Makefile new file mode 100644 index 000000000000..7b32d49624ae --- /dev/null +++ b/drivers/hid/intel-ish-hid/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile - Intel ISH HID drivers | ||
3 | # Copyright (c) 2014-2016, Intel Corporation. | ||
4 | # | ||
5 | # | ||
6 | obj-$(CONFIG_INTEL_ISH_HID) += intel-ishtp.o | ||
7 | intel-ishtp-objs := ishtp/init.o | ||
8 | intel-ishtp-objs += ishtp/hbm.o | ||
9 | intel-ishtp-objs += ishtp/client.o | ||
10 | intel-ishtp-objs += ishtp/bus.o | ||
11 | intel-ishtp-objs += ishtp/dma-if.o | ||
12 | intel-ishtp-objs += ishtp/client-buffers.o | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c new file mode 100644 index 000000000000..0183eacaf6c9 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/bus.c | |||
@@ -0,0 +1,791 @@ | |||
1 | /* | ||
2 | * ISHTP bus driver | ||
3 | * | ||
4 | * Copyright (c) 2012-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/device.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include "bus.h" | ||
24 | #include "ishtp-dev.h" | ||
25 | #include "client.h" | ||
26 | #include "hbm.h" | ||
27 | |||
28 | static int ishtp_use_dma; | ||
29 | module_param_named(ishtp_use_dma, ishtp_use_dma, int, 0600); | ||
30 | MODULE_PARM_DESC(ishtp_use_dma, "Use DMA to send messages"); | ||
31 | |||
32 | #define to_ishtp_cl_driver(d) container_of(d, struct ishtp_cl_driver, driver) | ||
33 | #define to_ishtp_cl_device(d) container_of(d, struct ishtp_cl_device, dev) | ||
34 | static bool ishtp_device_ready; | ||
35 | |||
36 | /** | ||
37 | * ishtp_recv() - process ishtp message | ||
38 | * @dev: ishtp device | ||
39 | * | ||
40 | * If a message with valid header and size is received, then | ||
41 | * this function calls appropriate handler. The host or firmware | ||
42 | * address is zero, then they are host bus management message, | ||
43 | * otherwise they are message fo clients. | ||
44 | */ | ||
45 | void ishtp_recv(struct ishtp_device *dev) | ||
46 | { | ||
47 | uint32_t msg_hdr; | ||
48 | struct ishtp_msg_hdr *ishtp_hdr; | ||
49 | |||
50 | /* Read ISHTP header dword */ | ||
51 | msg_hdr = dev->ops->ishtp_read_hdr(dev); | ||
52 | if (!msg_hdr) | ||
53 | return; | ||
54 | |||
55 | dev->ops->sync_fw_clock(dev); | ||
56 | |||
57 | ishtp_hdr = (struct ishtp_msg_hdr *)&msg_hdr; | ||
58 | dev->ishtp_msg_hdr = msg_hdr; | ||
59 | |||
60 | /* Sanity check: ISHTP frag. length in header */ | ||
61 | if (ishtp_hdr->length > dev->mtu) { | ||
62 | dev_err(dev->devc, | ||
63 | "ISHTP hdr - bad length: %u; dropped [%08X]\n", | ||
64 | (unsigned int)ishtp_hdr->length, msg_hdr); | ||
65 | return; | ||
66 | } | ||
67 | |||
68 | /* ISHTP bus message */ | ||
69 | if (!ishtp_hdr->host_addr && !ishtp_hdr->fw_addr) | ||
70 | recv_hbm(dev, ishtp_hdr); | ||
71 | /* ISHTP fixed-client message */ | ||
72 | else if (!ishtp_hdr->host_addr) | ||
73 | recv_fixed_cl_msg(dev, ishtp_hdr); | ||
74 | else | ||
75 | /* ISHTP client message */ | ||
76 | recv_ishtp_cl_msg(dev, ishtp_hdr); | ||
77 | } | ||
78 | EXPORT_SYMBOL(ishtp_recv); | ||
79 | |||
80 | /** | ||
81 | * ishtp_send_msg() - Send ishtp message | ||
82 | * @dev: ishtp device | ||
83 | * @hdr: Message header | ||
84 | * @msg: Message contents | ||
85 | * @ipc_send_compl: completion callback | ||
86 | * @ipc_send_compl_prm: completion callback parameter | ||
87 | * | ||
88 | * Send a multi fragment message via IPC. After sending the first fragment | ||
89 | * the completion callback is called to schedule transmit of next fragment. | ||
90 | * | ||
91 | * Return: This returns IPC send message status. | ||
92 | */ | ||
93 | int ishtp_send_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, | ||
94 | void *msg, void(*ipc_send_compl)(void *), | ||
95 | void *ipc_send_compl_prm) | ||
96 | { | ||
97 | unsigned char ipc_msg[IPC_FULL_MSG_SIZE]; | ||
98 | uint32_t drbl_val; | ||
99 | |||
100 | drbl_val = dev->ops->ipc_get_header(dev, hdr->length + | ||
101 | sizeof(struct ishtp_msg_hdr), | ||
102 | 1); | ||
103 | |||
104 | memcpy(ipc_msg, &drbl_val, sizeof(uint32_t)); | ||
105 | memcpy(ipc_msg + sizeof(uint32_t), hdr, sizeof(uint32_t)); | ||
106 | memcpy(ipc_msg + 2 * sizeof(uint32_t), msg, hdr->length); | ||
107 | return dev->ops->write(dev, ipc_send_compl, ipc_send_compl_prm, | ||
108 | ipc_msg, 2 * sizeof(uint32_t) + hdr->length); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * ishtp_write_message() - Send ishtp single fragment message | ||
113 | * @dev: ishtp device | ||
114 | * @hdr: Message header | ||
115 | * @buf: message data | ||
116 | * | ||
117 | * Send a single fragment message via IPC. This returns IPC send message | ||
118 | * status. | ||
119 | * | ||
120 | * Return: This returns IPC send message status. | ||
121 | */ | ||
122 | int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, | ||
123 | unsigned char *buf) | ||
124 | { | ||
125 | return ishtp_send_msg(dev, hdr, buf, NULL, NULL); | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * ishtp_fw_cl_by_uuid() - locate index of fw client | ||
130 | * @dev: ishtp device | ||
131 | * @uuid: uuid of the client to search | ||
132 | * | ||
133 | * Search firmware client using UUID. | ||
134 | * | ||
135 | * Return: fw client index or -ENOENT if not found | ||
136 | */ | ||
137 | int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *uuid) | ||
138 | { | ||
139 | int i, res = -ENOENT; | ||
140 | |||
141 | for (i = 0; i < dev->fw_clients_num; ++i) { | ||
142 | if (uuid_le_cmp(*uuid, dev->fw_clients[i].props.protocol_name) | ||
143 | == 0) { | ||
144 | res = i; | ||
145 | break; | ||
146 | } | ||
147 | } | ||
148 | return res; | ||
149 | } | ||
150 | EXPORT_SYMBOL(ishtp_fw_cl_by_uuid); | ||
151 | |||
152 | /** | ||
153 | * ishtp_fw_cl_by_id() - return index to fw_clients for client_id | ||
154 | * @dev: the ishtp device structure | ||
155 | * @client_id: fw client id to search | ||
156 | * | ||
157 | * Search firmware client using client id. | ||
158 | * | ||
159 | * Return: index on success, -ENOENT on failure. | ||
160 | */ | ||
161 | int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id) | ||
162 | { | ||
163 | int i, res = -ENOENT; | ||
164 | unsigned long flags; | ||
165 | |||
166 | spin_lock_irqsave(&dev->fw_clients_lock, flags); | ||
167 | for (i = 0; i < dev->fw_clients_num; i++) { | ||
168 | if (dev->fw_clients[i].client_id == client_id) { | ||
169 | res = i; | ||
170 | break; | ||
171 | } | ||
172 | } | ||
173 | spin_unlock_irqrestore(&dev->fw_clients_lock, flags); | ||
174 | |||
175 | return res; | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * ishtp_cl_device_probe() - Bus probe() callback | ||
180 | * @dev: the device structure | ||
181 | * | ||
182 | * This is a bus probe callback and calls the drive probe function. | ||
183 | * | ||
184 | * Return: Return value from driver probe() call. | ||
185 | */ | ||
186 | static int ishtp_cl_device_probe(struct device *dev) | ||
187 | { | ||
188 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
189 | struct ishtp_cl_driver *driver; | ||
190 | |||
191 | if (!device) | ||
192 | return 0; | ||
193 | |||
194 | driver = to_ishtp_cl_driver(dev->driver); | ||
195 | if (!driver || !driver->probe) | ||
196 | return -ENODEV; | ||
197 | |||
198 | return driver->probe(device); | ||
199 | } | ||
200 | |||
201 | /** | ||
202 | * ishtp_cl_device_remove() - Bus remove() callback | ||
203 | * @dev: the device structure | ||
204 | * | ||
205 | * This is a bus remove callback and calls the drive remove function. | ||
206 | * Since the ISH driver model supports only built in, this is | ||
207 | * primarily can be called during pci driver init failure. | ||
208 | * | ||
209 | * Return: Return value from driver remove() call. | ||
210 | */ | ||
211 | static int ishtp_cl_device_remove(struct device *dev) | ||
212 | { | ||
213 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
214 | struct ishtp_cl_driver *driver; | ||
215 | |||
216 | if (!device || !dev->driver) | ||
217 | return 0; | ||
218 | |||
219 | if (device->event_cb) { | ||
220 | device->event_cb = NULL; | ||
221 | cancel_work_sync(&device->event_work); | ||
222 | } | ||
223 | |||
224 | driver = to_ishtp_cl_driver(dev->driver); | ||
225 | if (!driver->remove) { | ||
226 | dev->driver = NULL; | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | return driver->remove(device); | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * ishtp_cl_device_suspend() - Bus suspend callback | ||
236 | * @dev: device | ||
237 | * | ||
238 | * Called during device suspend process. | ||
239 | * | ||
240 | * Return: Return value from driver suspend() call. | ||
241 | */ | ||
242 | static int ishtp_cl_device_suspend(struct device *dev) | ||
243 | { | ||
244 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
245 | struct ishtp_cl_driver *driver; | ||
246 | int ret = 0; | ||
247 | |||
248 | if (!device) | ||
249 | return 0; | ||
250 | |||
251 | driver = to_ishtp_cl_driver(dev->driver); | ||
252 | if (driver && driver->driver.pm) { | ||
253 | if (driver->driver.pm->suspend) | ||
254 | ret = driver->driver.pm->suspend(dev); | ||
255 | } | ||
256 | |||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * ishtp_cl_device_resume() - Bus resume callback | ||
262 | * @dev: device | ||
263 | * | ||
264 | * Called during device resume process. | ||
265 | * | ||
266 | * Return: Return value from driver resume() call. | ||
267 | */ | ||
268 | static int ishtp_cl_device_resume(struct device *dev) | ||
269 | { | ||
270 | struct ishtp_cl_device *device = to_ishtp_cl_device(dev); | ||
271 | struct ishtp_cl_driver *driver; | ||
272 | int ret = 0; | ||
273 | |||
274 | if (!device) | ||
275 | return 0; | ||
276 | |||
277 | /* | ||
278 | * When ISH needs hard reset, it is done asynchrnously, hence bus | ||
279 | * resume will be called before full ISH resume | ||
280 | */ | ||
281 | if (device->ishtp_dev->resume_flag) | ||
282 | return 0; | ||
283 | |||
284 | driver = to_ishtp_cl_driver(dev->driver); | ||
285 | if (driver && driver->driver.pm) { | ||
286 | if (driver->driver.pm->resume) | ||
287 | ret = driver->driver.pm->resume(dev); | ||
288 | } | ||
289 | |||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * ishtp_cl_device_reset() - Reset callback | ||
295 | * @device: ishtp client device instance | ||
296 | * | ||
297 | * This is a callback when HW reset is done and the device need | ||
298 | * reinit. | ||
299 | * | ||
300 | * Return: Return value from driver reset() call. | ||
301 | */ | ||
302 | static int ishtp_cl_device_reset(struct ishtp_cl_device *device) | ||
303 | { | ||
304 | struct ishtp_cl_driver *driver; | ||
305 | int ret = 0; | ||
306 | |||
307 | device->event_cb = NULL; | ||
308 | cancel_work_sync(&device->event_work); | ||
309 | |||
310 | driver = to_ishtp_cl_driver(device->dev.driver); | ||
311 | if (driver && driver->reset) | ||
312 | ret = driver->reset(device); | ||
313 | |||
314 | return ret; | ||
315 | } | ||
316 | |||
317 | static ssize_t modalias_show(struct device *dev, struct device_attribute *a, | ||
318 | char *buf) | ||
319 | { | ||
320 | int len; | ||
321 | |||
322 | len = snprintf(buf, PAGE_SIZE, "ishtp:%s\n", dev_name(dev)); | ||
323 | return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; | ||
324 | } | ||
325 | |||
326 | static struct device_attribute ishtp_cl_dev_attrs[] = { | ||
327 | __ATTR_RO(modalias), | ||
328 | __ATTR_NULL, | ||
329 | }; | ||
330 | |||
331 | static int ishtp_cl_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
332 | { | ||
333 | if (add_uevent_var(env, "MODALIAS=ishtp:%s", dev_name(dev))) | ||
334 | return -ENOMEM; | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = { | ||
339 | /* Suspend callbacks */ | ||
340 | .suspend = ishtp_cl_device_suspend, | ||
341 | .resume = ishtp_cl_device_resume, | ||
342 | /* Hibernate callbacks */ | ||
343 | .freeze = ishtp_cl_device_suspend, | ||
344 | .thaw = ishtp_cl_device_resume, | ||
345 | .restore = ishtp_cl_device_resume, | ||
346 | }; | ||
347 | |||
348 | static struct bus_type ishtp_cl_bus_type = { | ||
349 | .name = "ishtp", | ||
350 | .dev_attrs = ishtp_cl_dev_attrs, | ||
351 | .probe = ishtp_cl_device_probe, | ||
352 | .remove = ishtp_cl_device_remove, | ||
353 | .pm = &ishtp_cl_bus_dev_pm_ops, | ||
354 | .uevent = ishtp_cl_uevent, | ||
355 | }; | ||
356 | |||
357 | static void ishtp_cl_dev_release(struct device *dev) | ||
358 | { | ||
359 | kfree(to_ishtp_cl_device(dev)); | ||
360 | } | ||
361 | |||
362 | static struct device_type ishtp_cl_device_type = { | ||
363 | .release = ishtp_cl_dev_release, | ||
364 | }; | ||
365 | |||
366 | /** | ||
367 | * ishtp_bus_add_device() - Function to create device on bus | ||
368 | * @dev: ishtp device | ||
369 | * @uuid: uuid of the client | ||
370 | * @name: Name of the client | ||
371 | * | ||
372 | * Allocate ISHTP bus client device, attach it to uuid | ||
373 | * and register with ISHTP bus. | ||
374 | * | ||
375 | * Return: ishtp_cl_device pointer or NULL on failure | ||
376 | */ | ||
377 | static struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev, | ||
378 | uuid_le uuid, char *name) | ||
379 | { | ||
380 | struct ishtp_cl_device *device; | ||
381 | int status; | ||
382 | unsigned long flags; | ||
383 | struct list_head *pos; | ||
384 | |||
385 | spin_lock_irqsave(&dev->device_list_lock, flags); | ||
386 | list_for_each(pos, &dev->device_list) { | ||
387 | device = list_entry(pos, struct ishtp_cl_device, device_link); | ||
388 | if (!strcmp(name, dev_name(&device->dev))) { | ||
389 | device->fw_client = &dev->fw_clients[ | ||
390 | dev->fw_client_presentation_num - 1]; | ||
391 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
392 | ishtp_cl_device_reset(device); | ||
393 | return device; | ||
394 | } | ||
395 | } | ||
396 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
397 | |||
398 | device = kzalloc(sizeof(struct ishtp_cl_device), GFP_KERNEL); | ||
399 | if (!device) | ||
400 | return NULL; | ||
401 | |||
402 | device->dev.parent = dev->devc; | ||
403 | device->dev.bus = &ishtp_cl_bus_type; | ||
404 | device->dev.type = &ishtp_cl_device_type; | ||
405 | device->ishtp_dev = dev; | ||
406 | |||
407 | device->fw_client = | ||
408 | &dev->fw_clients[dev->fw_client_presentation_num - 1]; | ||
409 | |||
410 | dev_set_name(&device->dev, "%s", name); | ||
411 | |||
412 | spin_lock_irqsave(&dev->device_list_lock, flags); | ||
413 | list_add_tail(&device->device_link, &dev->device_list); | ||
414 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
415 | |||
416 | status = device_register(&device->dev); | ||
417 | if (status) { | ||
418 | spin_lock_irqsave(&dev->device_list_lock, flags); | ||
419 | list_del(&device->device_link); | ||
420 | spin_unlock_irqrestore(&dev->device_list_lock, flags); | ||
421 | dev_err(dev->devc, "Failed to register ISHTP client device\n"); | ||
422 | kfree(device); | ||
423 | return NULL; | ||
424 | } | ||
425 | |||
426 | ishtp_device_ready = true; | ||
427 | |||
428 | return device; | ||
429 | } | ||
430 | |||
431 | /** | ||
432 | * ishtp_bus_remove_device() - Function to relase device on bus | ||
433 | * @device: client device instance | ||
434 | * | ||
435 | * This is a counterpart of ishtp_bus_add_device. | ||
436 | * Device is unregistered. | ||
437 | * the device structure is freed in 'ishtp_cl_dev_release' function | ||
438 | * Called only during error in pci driver init path. | ||
439 | */ | ||
440 | static void ishtp_bus_remove_device(struct ishtp_cl_device *device) | ||
441 | { | ||
442 | device_unregister(&device->dev); | ||
443 | } | ||
444 | |||
445 | /** | ||
446 | * __ishtp_cl_driver_register() - Client driver register | ||
447 | * @driver: the client driver instance | ||
448 | * @owner: Owner of this driver module | ||
449 | * | ||
450 | * Once a client driver is probed, it created a client | ||
451 | * instance and registers with the bus. | ||
452 | * | ||
453 | * Return: Return value of driver_register or -ENODEV if not ready | ||
454 | */ | ||
455 | int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver, | ||
456 | struct module *owner) | ||
457 | { | ||
458 | int err; | ||
459 | |||
460 | if (!ishtp_device_ready) | ||
461 | return -ENODEV; | ||
462 | |||
463 | driver->driver.name = driver->name; | ||
464 | driver->driver.owner = owner; | ||
465 | driver->driver.bus = &ishtp_cl_bus_type; | ||
466 | |||
467 | err = driver_register(&driver->driver); | ||
468 | if (err) | ||
469 | return err; | ||
470 | |||
471 | return 0; | ||
472 | } | ||
473 | EXPORT_SYMBOL(__ishtp_cl_driver_register); | ||
474 | |||
475 | /** | ||
476 | * ishtp_cl_driver_unregister() - Client driver unregister | ||
477 | * @driver: the client driver instance | ||
478 | * | ||
479 | * Unregister client during device removal process. | ||
480 | */ | ||
481 | void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver) | ||
482 | { | ||
483 | driver_unregister(&driver->driver); | ||
484 | } | ||
485 | EXPORT_SYMBOL(ishtp_cl_driver_unregister); | ||
486 | |||
487 | /** | ||
488 | * ishtp_bus_event_work() - event work function | ||
489 | * @work: work struct pointer | ||
490 | * | ||
491 | * Once an event is received for a client this work | ||
492 | * function is called. If the device has registered a | ||
493 | * callback then the callback is called. | ||
494 | */ | ||
495 | static void ishtp_bus_event_work(struct work_struct *work) | ||
496 | { | ||
497 | struct ishtp_cl_device *device; | ||
498 | |||
499 | device = container_of(work, struct ishtp_cl_device, event_work); | ||
500 | |||
501 | if (device->event_cb) | ||
502 | device->event_cb(device); | ||
503 | } | ||
504 | |||
505 | /** | ||
506 | * ishtp_cl_bus_rx_event() - schedule event work | ||
507 | * @device: client device instance | ||
508 | * | ||
509 | * Once an event is received for a client this schedules | ||
510 | * a work function to process. | ||
511 | */ | ||
512 | void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device) | ||
513 | { | ||
514 | if (!device || !device->event_cb) | ||
515 | return; | ||
516 | |||
517 | if (device->event_cb) | ||
518 | schedule_work(&device->event_work); | ||
519 | } | ||
520 | |||
521 | /** | ||
522 | * ishtp_register_event_cb() - Register callback | ||
523 | * @device: client device instance | ||
524 | * @event_cb: Event processor for an client | ||
525 | * | ||
526 | * Register a callback for events, called from client driver | ||
527 | * | ||
528 | * Return: Return 0 or -EALREADY if already registered | ||
529 | */ | ||
530 | int ishtp_register_event_cb(struct ishtp_cl_device *device, | ||
531 | void (*event_cb)(struct ishtp_cl_device *)) | ||
532 | { | ||
533 | if (device->event_cb) | ||
534 | return -EALREADY; | ||
535 | |||
536 | device->event_cb = event_cb; | ||
537 | INIT_WORK(&device->event_work, ishtp_bus_event_work); | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | EXPORT_SYMBOL(ishtp_register_event_cb); | ||
542 | |||
543 | /** | ||
544 | * ishtp_get_device() - update usage count for the device | ||
545 | * @cl_device: client device instance | ||
546 | * | ||
547 | * Increment the usage count. The device can't be deleted | ||
548 | */ | ||
549 | void ishtp_get_device(struct ishtp_cl_device *cl_device) | ||
550 | { | ||
551 | cl_device->reference_count++; | ||
552 | } | ||
553 | EXPORT_SYMBOL(ishtp_get_device); | ||
554 | |||
555 | /** | ||
556 | * ishtp_put_device() - decrement usage count for the device | ||
557 | * @cl_device: client device instance | ||
558 | * | ||
559 | * Decrement the usage count. The device can be deleted is count = 0 | ||
560 | */ | ||
561 | void ishtp_put_device(struct ishtp_cl_device *cl_device) | ||
562 | { | ||
563 | cl_device->reference_count--; | ||
564 | } | ||
565 | EXPORT_SYMBOL(ishtp_put_device); | ||
566 | |||
567 | /** | ||
568 | * ishtp_bus_new_client() - Create a new client | ||
569 | * @dev: ISHTP device instance | ||
570 | * | ||
571 | * Once bus protocol enumerates a client, this is called | ||
572 | * to add a device for the client. | ||
573 | * | ||
574 | * Return: 0 on success or error code on failure | ||
575 | */ | ||
576 | int ishtp_bus_new_client(struct ishtp_device *dev) | ||
577 | { | ||
578 | int i; | ||
579 | char *dev_name; | ||
580 | struct ishtp_cl_device *cl_device; | ||
581 | uuid_le device_uuid; | ||
582 | |||
583 | /* | ||
584 | * For all reported clients, create an unconnected client and add its | ||
585 | * device to ISHTP bus. | ||
586 | * If appropriate driver has loaded, this will trigger its probe(). | ||
587 | * Otherwise, probe() will be called when driver is loaded | ||
588 | */ | ||
589 | i = dev->fw_client_presentation_num - 1; | ||
590 | device_uuid = dev->fw_clients[i].props.protocol_name; | ||
591 | dev_name = kasprintf(GFP_KERNEL, | ||
592 | "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", | ||
593 | device_uuid.b[3], device_uuid.b[2], device_uuid.b[1], | ||
594 | device_uuid.b[0], device_uuid.b[5], device_uuid.b[4], | ||
595 | device_uuid.b[7], device_uuid.b[6], device_uuid.b[8], | ||
596 | device_uuid.b[9], device_uuid.b[10], device_uuid.b[11], | ||
597 | device_uuid.b[12], device_uuid.b[13], device_uuid.b[14], | ||
598 | device_uuid.b[15]); | ||
599 | if (!dev_name) | ||
600 | return -ENOMEM; | ||
601 | |||
602 | cl_device = ishtp_bus_add_device(dev, device_uuid, dev_name); | ||
603 | if (!cl_device) { | ||
604 | kfree(dev_name); | ||
605 | return -ENOENT; | ||
606 | } | ||
607 | |||
608 | kfree(dev_name); | ||
609 | |||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | /** | ||
614 | * ishtp_cl_device_bind() - bind a device | ||
615 | * @cl: ishtp client device | ||
616 | * | ||
617 | * Binds connected ishtp_cl to ISHTP bus device | ||
618 | * | ||
619 | * Return: 0 on success or fault code | ||
620 | */ | ||
621 | int ishtp_cl_device_bind(struct ishtp_cl *cl) | ||
622 | { | ||
623 | struct ishtp_cl_device *cl_device; | ||
624 | unsigned long flags; | ||
625 | int rv; | ||
626 | |||
627 | if (!cl->fw_client_id || cl->state != ISHTP_CL_CONNECTED) | ||
628 | return -EFAULT; | ||
629 | |||
630 | rv = -ENOENT; | ||
631 | spin_lock_irqsave(&cl->dev->device_list_lock, flags); | ||
632 | list_for_each_entry(cl_device, &cl->dev->device_list, | ||
633 | device_link) { | ||
634 | if (cl_device->fw_client->client_id == cl->fw_client_id) { | ||
635 | cl->device = cl_device; | ||
636 | rv = 0; | ||
637 | break; | ||
638 | } | ||
639 | } | ||
640 | spin_unlock_irqrestore(&cl->dev->device_list_lock, flags); | ||
641 | return rv; | ||
642 | } | ||
643 | |||
644 | /** | ||
645 | * ishtp_bus_remove_all_clients() - Remove all clients | ||
646 | * @ishtp_dev: ishtp device | ||
647 | * @warm_reset: Reset due to FW reset dure to errors or S3 suspend | ||
648 | * | ||
649 | * This is part of reset/remove flow. This function the main processing | ||
650 | * only targets error processing, if the FW has forced reset or | ||
651 | * error to remove connected clients. When warm reset the client devices are | ||
652 | * not removed. | ||
653 | */ | ||
654 | void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev, | ||
655 | bool warm_reset) | ||
656 | { | ||
657 | struct ishtp_cl_device *cl_device, *n; | ||
658 | struct ishtp_cl *cl; | ||
659 | unsigned long flags; | ||
660 | |||
661 | spin_lock_irqsave(&ishtp_dev->cl_list_lock, flags); | ||
662 | list_for_each_entry(cl, &ishtp_dev->cl_list, link) { | ||
663 | cl->state = ISHTP_CL_DISCONNECTED; | ||
664 | |||
665 | /* | ||
666 | * Wake any pending process. The waiter would check dev->state | ||
667 | * and determine that it's not enabled already, | ||
668 | * and will return error to its caller | ||
669 | */ | ||
670 | wake_up_interruptible(&cl->wait_ctrl_res); | ||
671 | |||
672 | /* Disband any pending read/write requests and free rb */ | ||
673 | ishtp_cl_flush_queues(cl); | ||
674 | |||
675 | /* Remove all free and in_process rings, both Rx and Tx */ | ||
676 | ishtp_cl_free_rx_ring(cl); | ||
677 | ishtp_cl_free_tx_ring(cl); | ||
678 | |||
679 | /* | ||
680 | * Free client and ISHTP bus client device structures | ||
681 | * don't free host client because it is part of the OS fd | ||
682 | * structure | ||
683 | */ | ||
684 | } | ||
685 | spin_unlock_irqrestore(&ishtp_dev->cl_list_lock, flags); | ||
686 | |||
687 | /* Release DMA buffers for client messages */ | ||
688 | ishtp_cl_free_dma_buf(ishtp_dev); | ||
689 | |||
690 | /* remove bus clients */ | ||
691 | spin_lock_irqsave(&ishtp_dev->device_list_lock, flags); | ||
692 | list_for_each_entry_safe(cl_device, n, &ishtp_dev->device_list, | ||
693 | device_link) { | ||
694 | if (warm_reset && cl_device->reference_count) | ||
695 | continue; | ||
696 | |||
697 | list_del(&cl_device->device_link); | ||
698 | spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags); | ||
699 | ishtp_bus_remove_device(cl_device); | ||
700 | spin_lock_irqsave(&ishtp_dev->device_list_lock, flags); | ||
701 | } | ||
702 | spin_unlock_irqrestore(&ishtp_dev->device_list_lock, flags); | ||
703 | |||
704 | /* Free all client structures */ | ||
705 | spin_lock_irqsave(&ishtp_dev->fw_clients_lock, flags); | ||
706 | kfree(ishtp_dev->fw_clients); | ||
707 | ishtp_dev->fw_clients = NULL; | ||
708 | ishtp_dev->fw_clients_num = 0; | ||
709 | ishtp_dev->fw_client_presentation_num = 0; | ||
710 | ishtp_dev->fw_client_index = 0; | ||
711 | bitmap_zero(ishtp_dev->fw_clients_map, ISHTP_CLIENTS_MAX); | ||
712 | spin_unlock_irqrestore(&ishtp_dev->fw_clients_lock, flags); | ||
713 | } | ||
714 | EXPORT_SYMBOL(ishtp_bus_remove_all_clients); | ||
715 | |||
716 | /** | ||
717 | * ishtp_reset_handler() - IPC reset handler | ||
718 | * @dev: ishtp device | ||
719 | * | ||
720 | * ISHTP Handler for IPC_RESET notification | ||
721 | */ | ||
722 | void ishtp_reset_handler(struct ishtp_device *dev) | ||
723 | { | ||
724 | unsigned long flags; | ||
725 | |||
726 | /* Handle FW-initiated reset */ | ||
727 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
728 | |||
729 | /* Clear BH processing queue - no further HBMs */ | ||
730 | spin_lock_irqsave(&dev->rd_msg_spinlock, flags); | ||
731 | dev->rd_msg_fifo_head = dev->rd_msg_fifo_tail = 0; | ||
732 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
733 | |||
734 | /* Handle ISH FW reset against upper layers */ | ||
735 | ishtp_bus_remove_all_clients(dev, true); | ||
736 | } | ||
737 | EXPORT_SYMBOL(ishtp_reset_handler); | ||
738 | |||
739 | /** | ||
740 | * ishtp_reset_compl_handler() - Reset completion handler | ||
741 | * @dev: ishtp device | ||
742 | * | ||
743 | * ISHTP handler for IPC_RESET sequence completion to start | ||
744 | * host message bus start protocol sequence. | ||
745 | */ | ||
746 | void ishtp_reset_compl_handler(struct ishtp_device *dev) | ||
747 | { | ||
748 | dev->dev_state = ISHTP_DEV_INIT_CLIENTS; | ||
749 | dev->hbm_state = ISHTP_HBM_START; | ||
750 | ishtp_hbm_start_req(dev); | ||
751 | } | ||
752 | EXPORT_SYMBOL(ishtp_reset_compl_handler); | ||
753 | |||
754 | /** | ||
755 | * ishtp_use_dma_transfer() - Function to use DMA | ||
756 | * | ||
757 | * This interface is used to enable usage of DMA | ||
758 | * | ||
759 | * Return non zero if DMA can be enabled | ||
760 | */ | ||
761 | int ishtp_use_dma_transfer(void) | ||
762 | { | ||
763 | return ishtp_use_dma; | ||
764 | } | ||
765 | |||
766 | /** | ||
767 | * ishtp_bus_register() - Function to register bus | ||
768 | * | ||
769 | * This register ishtp bus | ||
770 | * | ||
771 | * Return: Return output of bus_register | ||
772 | */ | ||
773 | static int __init ishtp_bus_register(void) | ||
774 | { | ||
775 | return bus_register(&ishtp_cl_bus_type); | ||
776 | } | ||
777 | |||
778 | /** | ||
779 | * ishtp_bus_unregister() - Function to unregister bus | ||
780 | * | ||
781 | * This unregister ishtp bus | ||
782 | */ | ||
783 | static void __exit ishtp_bus_unregister(void) | ||
784 | { | ||
785 | bus_unregister(&ishtp_cl_bus_type); | ||
786 | } | ||
787 | |||
788 | module_init(ishtp_bus_register); | ||
789 | module_exit(ishtp_bus_unregister); | ||
790 | |||
791 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.h b/drivers/hid/intel-ish-hid/ishtp/bus.h new file mode 100644 index 000000000000..a1ffae7f26ad --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/bus.h | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * ISHTP bus definitions | ||
3 | * | ||
4 | * Copyright (c) 2014-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | #ifndef _LINUX_ISHTP_CL_BUS_H | ||
16 | #define _LINUX_ISHTP_CL_BUS_H | ||
17 | |||
18 | #include <linux/device.h> | ||
19 | #include <linux/mod_devicetable.h> | ||
20 | |||
21 | struct ishtp_cl; | ||
22 | struct ishtp_cl_device; | ||
23 | struct ishtp_device; | ||
24 | struct ishtp_msg_hdr; | ||
25 | |||
26 | /** | ||
27 | * struct ishtp_cl_device - ISHTP device handle | ||
28 | * @dev: device pointer | ||
29 | * @ishtp_dev: pointer to ishtp device structure to primarily to access | ||
30 | * hw device operation callbacks and properties | ||
31 | * @fw_client: fw_client pointer to get fw information like protocol name | ||
32 | * max message length etc. | ||
33 | * @device_link: Link to next client in the list on a bus | ||
34 | * @event_work: Used to schedule rx event for client | ||
35 | * @driver_data: Storage driver private data | ||
36 | * @reference_count: Used for get/put device | ||
37 | * @event_cb: Callback to driver to send events | ||
38 | * | ||
39 | * An ishtp_cl_device pointer is returned from ishtp_add_device() | ||
40 | * and links ISHTP bus clients to their actual host client pointer. | ||
41 | * Drivers for ISHTP devices will get an ishtp_cl_device pointer | ||
42 | * when being probed and shall use it for doing bus I/O. | ||
43 | */ | ||
44 | struct ishtp_cl_device { | ||
45 | struct device dev; | ||
46 | struct ishtp_device *ishtp_dev; | ||
47 | struct ishtp_fw_client *fw_client; | ||
48 | struct list_head device_link; | ||
49 | struct work_struct event_work; | ||
50 | void *driver_data; | ||
51 | int reference_count; | ||
52 | void (*event_cb)(struct ishtp_cl_device *device); | ||
53 | }; | ||
54 | |||
55 | /** | ||
56 | * struct ishtp_cl_device - ISHTP device handle | ||
57 | * @driver: driver instance on a bus | ||
58 | * @name: Name of the device for probe | ||
59 | * @probe: driver callback for device probe | ||
60 | * @remove: driver callback on device removal | ||
61 | * | ||
62 | * Client drivers defines to get probed/removed for ISHTP client device. | ||
63 | */ | ||
64 | struct ishtp_cl_driver { | ||
65 | struct device_driver driver; | ||
66 | const char *name; | ||
67 | int (*probe)(struct ishtp_cl_device *dev); | ||
68 | int (*remove)(struct ishtp_cl_device *dev); | ||
69 | int (*reset)(struct ishtp_cl_device *dev); | ||
70 | const struct dev_pm_ops *pm; | ||
71 | }; | ||
72 | |||
73 | |||
74 | int ishtp_bus_new_client(struct ishtp_device *dev); | ||
75 | void ishtp_remove_all_clients(struct ishtp_device *dev); | ||
76 | int ishtp_cl_device_bind(struct ishtp_cl *cl); | ||
77 | void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device); | ||
78 | |||
79 | /* Write a multi-fragment message */ | ||
80 | int ishtp_send_msg(struct ishtp_device *dev, | ||
81 | struct ishtp_msg_hdr *hdr, void *msg, | ||
82 | void (*ipc_send_compl)(void *), | ||
83 | void *ipc_send_compl_prm); | ||
84 | |||
85 | /* Write a single-fragment message */ | ||
86 | int ishtp_write_message(struct ishtp_device *dev, | ||
87 | struct ishtp_msg_hdr *hdr, | ||
88 | unsigned char *buf); | ||
89 | |||
90 | /* Use DMA to send/receive messages */ | ||
91 | int ishtp_use_dma_transfer(void); | ||
92 | |||
93 | /* Exported functions */ | ||
94 | void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev, | ||
95 | bool warm_reset); | ||
96 | |||
97 | void ishtp_recv(struct ishtp_device *dev); | ||
98 | void ishtp_reset_handler(struct ishtp_device *dev); | ||
99 | void ishtp_reset_compl_handler(struct ishtp_device *dev); | ||
100 | |||
101 | void ishtp_put_device(struct ishtp_cl_device *); | ||
102 | void ishtp_get_device(struct ishtp_cl_device *); | ||
103 | |||
104 | int __ishtp_cl_driver_register(struct ishtp_cl_driver *driver, | ||
105 | struct module *owner); | ||
106 | #define ishtp_cl_driver_register(driver) \ | ||
107 | __ishtp_cl_driver_register(driver, THIS_MODULE) | ||
108 | void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver); | ||
109 | |||
110 | int ishtp_register_event_cb(struct ishtp_cl_device *device, | ||
111 | void (*read_cb)(struct ishtp_cl_device *)); | ||
112 | int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid); | ||
113 | |||
114 | #endif /* _LINUX_ISHTP_CL_BUS_H */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/client-buffers.c b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c new file mode 100644 index 000000000000..3f5ce5ee687e --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | * ISHTP Ring Buffers | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/slab.h> | ||
18 | #include "client.h" | ||
19 | |||
20 | /** | ||
21 | * ishtp_cl_alloc_rx_ring() - Allocate RX ring buffers | ||
22 | * @cl: client device instance | ||
23 | * | ||
24 | * Allocate and initialize RX ring buffers | ||
25 | * | ||
26 | * Return: 0 on success else -ENOMEM | ||
27 | */ | ||
28 | int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl) | ||
29 | { | ||
30 | size_t len = cl->device->fw_client->props.max_msg_length; | ||
31 | int j; | ||
32 | struct ishtp_cl_rb *rb; | ||
33 | int ret = 0; | ||
34 | unsigned long flags; | ||
35 | |||
36 | for (j = 0; j < cl->rx_ring_size; ++j) { | ||
37 | rb = ishtp_io_rb_init(cl); | ||
38 | if (!rb) { | ||
39 | ret = -ENOMEM; | ||
40 | goto out; | ||
41 | } | ||
42 | ret = ishtp_io_rb_alloc_buf(rb, len); | ||
43 | if (ret) | ||
44 | goto out; | ||
45 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
46 | list_add_tail(&rb->list, &cl->free_rb_list.list); | ||
47 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
48 | } | ||
49 | |||
50 | return 0; | ||
51 | |||
52 | out: | ||
53 | dev_err(&cl->device->dev, "error in allocating Rx buffers\n"); | ||
54 | ishtp_cl_free_rx_ring(cl); | ||
55 | return ret; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * ishtp_cl_alloc_tx_ring() - Allocate TX ring buffers | ||
60 | * @cl: client device instance | ||
61 | * | ||
62 | * Allocate and initialize TX ring buffers | ||
63 | * | ||
64 | * Return: 0 on success else -ENOMEM | ||
65 | */ | ||
66 | int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl) | ||
67 | { | ||
68 | size_t len = cl->device->fw_client->props.max_msg_length; | ||
69 | int j; | ||
70 | unsigned long flags; | ||
71 | |||
72 | /* Allocate pool to free Tx bufs */ | ||
73 | for (j = 0; j < cl->tx_ring_size; ++j) { | ||
74 | struct ishtp_cl_tx_ring *tx_buf; | ||
75 | |||
76 | tx_buf = kmalloc(sizeof(struct ishtp_cl_tx_ring), GFP_KERNEL); | ||
77 | if (!tx_buf) | ||
78 | goto out; | ||
79 | |||
80 | memset(tx_buf, 0, sizeof(struct ishtp_cl_tx_ring)); | ||
81 | tx_buf->send_buf.data = kmalloc(len, GFP_KERNEL); | ||
82 | if (!tx_buf->send_buf.data) { | ||
83 | kfree(tx_buf); | ||
84 | goto out; | ||
85 | } | ||
86 | |||
87 | spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); | ||
88 | list_add_tail(&tx_buf->list, &cl->tx_free_list.list); | ||
89 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); | ||
90 | } | ||
91 | return 0; | ||
92 | out: | ||
93 | dev_err(&cl->device->dev, "error in allocating Tx pool\n"); | ||
94 | ishtp_cl_free_rx_ring(cl); | ||
95 | return -ENOMEM; | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * ishtp_cl_free_rx_ring() - Free RX ring buffers | ||
100 | * @cl: client device instance | ||
101 | * | ||
102 | * Free RX ring buffers | ||
103 | */ | ||
104 | void ishtp_cl_free_rx_ring(struct ishtp_cl *cl) | ||
105 | { | ||
106 | struct ishtp_cl_rb *rb; | ||
107 | unsigned long flags; | ||
108 | |||
109 | /* release allocated memory - pass over free_rb_list */ | ||
110 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
111 | while (!list_empty(&cl->free_rb_list.list)) { | ||
112 | rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, | ||
113 | list); | ||
114 | list_del(&rb->list); | ||
115 | kfree(rb->buffer.data); | ||
116 | kfree(rb); | ||
117 | } | ||
118 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
119 | /* release allocated memory - pass over in_process_list */ | ||
120 | spin_lock_irqsave(&cl->in_process_spinlock, flags); | ||
121 | while (!list_empty(&cl->in_process_list.list)) { | ||
122 | rb = list_entry(cl->in_process_list.list.next, | ||
123 | struct ishtp_cl_rb, list); | ||
124 | list_del(&rb->list); | ||
125 | kfree(rb->buffer.data); | ||
126 | kfree(rb); | ||
127 | } | ||
128 | spin_unlock_irqrestore(&cl->in_process_spinlock, flags); | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * ishtp_cl_free_tx_ring() - Free TX ring buffers | ||
133 | * @cl: client device instance | ||
134 | * | ||
135 | * Free TX ring buffers | ||
136 | */ | ||
137 | void ishtp_cl_free_tx_ring(struct ishtp_cl *cl) | ||
138 | { | ||
139 | struct ishtp_cl_tx_ring *tx_buf; | ||
140 | unsigned long flags; | ||
141 | |||
142 | spin_lock_irqsave(&cl->tx_free_list_spinlock, flags); | ||
143 | /* release allocated memory - pass over tx_free_list */ | ||
144 | while (!list_empty(&cl->tx_free_list.list)) { | ||
145 | tx_buf = list_entry(cl->tx_free_list.list.next, | ||
146 | struct ishtp_cl_tx_ring, list); | ||
147 | list_del(&tx_buf->list); | ||
148 | kfree(tx_buf->send_buf.data); | ||
149 | kfree(tx_buf); | ||
150 | } | ||
151 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, flags); | ||
152 | |||
153 | spin_lock_irqsave(&cl->tx_list_spinlock, flags); | ||
154 | /* release allocated memory - pass over tx_list */ | ||
155 | while (!list_empty(&cl->tx_list.list)) { | ||
156 | tx_buf = list_entry(cl->tx_list.list.next, | ||
157 | struct ishtp_cl_tx_ring, list); | ||
158 | list_del(&tx_buf->list); | ||
159 | kfree(tx_buf->send_buf.data); | ||
160 | kfree(tx_buf); | ||
161 | } | ||
162 | spin_unlock_irqrestore(&cl->tx_list_spinlock, flags); | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * ishtp_io_rb_free() - Free IO request block | ||
167 | * @rb: IO request block | ||
168 | * | ||
169 | * Free io request block memory | ||
170 | */ | ||
171 | void ishtp_io_rb_free(struct ishtp_cl_rb *rb) | ||
172 | { | ||
173 | if (rb == NULL) | ||
174 | return; | ||
175 | |||
176 | kfree(rb->buffer.data); | ||
177 | kfree(rb); | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * ishtp_io_rb_init() - Allocate and init IO request block | ||
182 | * @cl: client device instance | ||
183 | * | ||
184 | * Allocate and initialize request block | ||
185 | * | ||
186 | * Return: Allocted IO request block pointer | ||
187 | */ | ||
188 | struct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl) | ||
189 | { | ||
190 | struct ishtp_cl_rb *rb; | ||
191 | |||
192 | rb = kzalloc(sizeof(struct ishtp_cl_rb), GFP_KERNEL); | ||
193 | if (!rb) | ||
194 | return NULL; | ||
195 | |||
196 | INIT_LIST_HEAD(&rb->list); | ||
197 | rb->cl = cl; | ||
198 | rb->buf_idx = 0; | ||
199 | return rb; | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * ishtp_io_rb_alloc_buf() - Allocate and init response buffer | ||
204 | * @rb: IO request block | ||
205 | * @length: length of response buffer | ||
206 | * | ||
207 | * Allocate respose buffer | ||
208 | * | ||
209 | * Return: 0 on success else -ENOMEM | ||
210 | */ | ||
211 | int ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length) | ||
212 | { | ||
213 | if (!rb) | ||
214 | return -EINVAL; | ||
215 | |||
216 | if (length == 0) | ||
217 | return 0; | ||
218 | |||
219 | rb->buffer.data = kmalloc(length, GFP_KERNEL); | ||
220 | if (!rb->buffer.data) | ||
221 | return -ENOMEM; | ||
222 | |||
223 | rb->buffer.size = length; | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * ishtp_cl_io_rb_recycle() - Recycle IO request blocks | ||
229 | * @rb: IO request block | ||
230 | * | ||
231 | * Re-append rb to its client's free list and send flow control if needed | ||
232 | * | ||
233 | * Return: 0 on success else -EFAULT | ||
234 | */ | ||
235 | int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb) | ||
236 | { | ||
237 | struct ishtp_cl *cl; | ||
238 | int rets = 0; | ||
239 | unsigned long flags; | ||
240 | |||
241 | if (!rb || !rb->cl) | ||
242 | return -EFAULT; | ||
243 | |||
244 | cl = rb->cl; | ||
245 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
246 | list_add_tail(&rb->list, &cl->free_rb_list.list); | ||
247 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
248 | |||
249 | /* | ||
250 | * If we returned the first buffer to empty 'free' list, | ||
251 | * send flow control | ||
252 | */ | ||
253 | if (!cl->out_flow_ctrl_creds) | ||
254 | rets = ishtp_cl_read_start(cl); | ||
255 | |||
256 | return rets; | ||
257 | } | ||
258 | EXPORT_SYMBOL(ishtp_cl_io_rb_recycle); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c new file mode 100644 index 000000000000..aad61328f282 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/client.c | |||
@@ -0,0 +1,1054 @@ | |||
1 | /* | ||
2 | * ISHTP client logic | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/slab.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/wait.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | #include "hbm.h" | ||
23 | #include "client.h" | ||
24 | |||
25 | /** | ||
26 | * ishtp_read_list_flush() - Flush read queue | ||
27 | * @cl: ishtp client instance | ||
28 | * | ||
29 | * Used to remove all entries from read queue for a client | ||
30 | */ | ||
31 | static void ishtp_read_list_flush(struct ishtp_cl *cl) | ||
32 | { | ||
33 | struct ishtp_cl_rb *rb; | ||
34 | struct ishtp_cl_rb *next; | ||
35 | unsigned long flags; | ||
36 | |||
37 | spin_lock_irqsave(&cl->dev->read_list_spinlock, flags); | ||
38 | list_for_each_entry_safe(rb, next, &cl->dev->read_list.list, list) | ||
39 | if (rb->cl && ishtp_cl_cmp_id(cl, rb->cl)) { | ||
40 | list_del(&rb->list); | ||
41 | ishtp_io_rb_free(rb); | ||
42 | } | ||
43 | spin_unlock_irqrestore(&cl->dev->read_list_spinlock, flags); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * ishtp_cl_flush_queues() - Flush all queues for a client | ||
48 | * @cl: ishtp client instance | ||
49 | * | ||
50 | * Used to remove all queues for a client. This is called when a client device | ||
51 | * needs reset due to error, S3 resume or during module removal | ||
52 | * | ||
53 | * Return: 0 on success else -EINVAL if device is NULL | ||
54 | */ | ||
55 | int ishtp_cl_flush_queues(struct ishtp_cl *cl) | ||
56 | { | ||
57 | if (WARN_ON(!cl || !cl->dev)) | ||
58 | return -EINVAL; | ||
59 | |||
60 | ishtp_read_list_flush(cl); | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | EXPORT_SYMBOL(ishtp_cl_flush_queues); | ||
65 | |||
66 | /** | ||
67 | * ishtp_cl_init() - Initialize all fields of a client device | ||
68 | * @cl: ishtp client instance | ||
69 | * @dev: ishtp device | ||
70 | * | ||
71 | * Initializes a client device fields: Init spinlocks, init queues etc. | ||
72 | * This function is called during new client creation | ||
73 | */ | ||
74 | static void ishtp_cl_init(struct ishtp_cl *cl, struct ishtp_device *dev) | ||
75 | { | ||
76 | memset(cl, 0, sizeof(struct ishtp_cl)); | ||
77 | init_waitqueue_head(&cl->wait_ctrl_res); | ||
78 | spin_lock_init(&cl->free_list_spinlock); | ||
79 | spin_lock_init(&cl->in_process_spinlock); | ||
80 | spin_lock_init(&cl->tx_list_spinlock); | ||
81 | spin_lock_init(&cl->tx_free_list_spinlock); | ||
82 | spin_lock_init(&cl->fc_spinlock); | ||
83 | INIT_LIST_HEAD(&cl->link); | ||
84 | cl->dev = dev; | ||
85 | |||
86 | INIT_LIST_HEAD(&cl->free_rb_list.list); | ||
87 | INIT_LIST_HEAD(&cl->tx_list.list); | ||
88 | INIT_LIST_HEAD(&cl->tx_free_list.list); | ||
89 | INIT_LIST_HEAD(&cl->in_process_list.list); | ||
90 | |||
91 | cl->rx_ring_size = CL_DEF_RX_RING_SIZE; | ||
92 | cl->tx_ring_size = CL_DEF_TX_RING_SIZE; | ||
93 | |||
94 | /* dma */ | ||
95 | cl->last_tx_path = CL_TX_PATH_IPC; | ||
96 | cl->last_dma_acked = 1; | ||
97 | cl->last_dma_addr = NULL; | ||
98 | cl->last_ipc_acked = 1; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * ishtp_cl_allocate() - allocates client structure and sets it up. | ||
103 | * @dev: ishtp device | ||
104 | * | ||
105 | * Allocate memory for new client device and call to initialize each field. | ||
106 | * | ||
107 | * Return: The allocated client instance or NULL on failure | ||
108 | */ | ||
109 | struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev) | ||
110 | { | ||
111 | struct ishtp_cl *cl; | ||
112 | |||
113 | cl = kmalloc(sizeof(struct ishtp_cl), GFP_KERNEL); | ||
114 | if (!cl) | ||
115 | return NULL; | ||
116 | |||
117 | ishtp_cl_init(cl, dev); | ||
118 | return cl; | ||
119 | } | ||
120 | EXPORT_SYMBOL(ishtp_cl_allocate); | ||
121 | |||
122 | /** | ||
123 | * ishtp_cl_free() - Frees a client device | ||
124 | * @cl: client device instance | ||
125 | * | ||
126 | * Frees a client device | ||
127 | */ | ||
128 | void ishtp_cl_free(struct ishtp_cl *cl) | ||
129 | { | ||
130 | struct ishtp_device *dev; | ||
131 | unsigned long flags; | ||
132 | |||
133 | if (!cl) | ||
134 | return; | ||
135 | |||
136 | dev = cl->dev; | ||
137 | if (!dev) | ||
138 | return; | ||
139 | |||
140 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
141 | ishtp_cl_free_rx_ring(cl); | ||
142 | ishtp_cl_free_tx_ring(cl); | ||
143 | kfree(cl); | ||
144 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
145 | } | ||
146 | EXPORT_SYMBOL(ishtp_cl_free); | ||
147 | |||
148 | /** | ||
149 | * ishtp_cl_link() - Reserve a host id and link the client instance | ||
150 | * @cl: client device instance | ||
151 | * @id: host client id to use. It can be ISHTP_HOST_CLIENT_ID_ANY if any | ||
152 | * id from the available can be used | ||
153 | * | ||
154 | * | ||
155 | * This allocates a single bit in the hostmap. This function will make sure | ||
156 | * that not many client sessions are opened at the same time. Once allocated | ||
157 | * the client device instance is added to the ishtp device in the current | ||
158 | * client list | ||
159 | * | ||
160 | * Return: 0 or error code on failure | ||
161 | */ | ||
162 | int ishtp_cl_link(struct ishtp_cl *cl, int id) | ||
163 | { | ||
164 | struct ishtp_device *dev; | ||
165 | unsigned long flags, flags_cl; | ||
166 | int ret = 0; | ||
167 | |||
168 | if (WARN_ON(!cl || !cl->dev)) | ||
169 | return -EINVAL; | ||
170 | |||
171 | dev = cl->dev; | ||
172 | |||
173 | spin_lock_irqsave(&dev->device_lock, flags); | ||
174 | |||
175 | if (dev->open_handle_count >= ISHTP_MAX_OPEN_HANDLE_COUNT) { | ||
176 | ret = -EMFILE; | ||
177 | goto unlock_dev; | ||
178 | } | ||
179 | |||
180 | /* If Id is not assigned get one*/ | ||
181 | if (id == ISHTP_HOST_CLIENT_ID_ANY) | ||
182 | id = find_first_zero_bit(dev->host_clients_map, | ||
183 | ISHTP_CLIENTS_MAX); | ||
184 | |||
185 | if (id >= ISHTP_CLIENTS_MAX) { | ||
186 | spin_unlock_irqrestore(&dev->device_lock, flags); | ||
187 | dev_err(&cl->device->dev, "id exceeded %d", ISHTP_CLIENTS_MAX); | ||
188 | return -ENOENT; | ||
189 | } | ||
190 | |||
191 | dev->open_handle_count++; | ||
192 | cl->host_client_id = id; | ||
193 | spin_lock_irqsave(&dev->cl_list_lock, flags_cl); | ||
194 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
195 | ret = -ENODEV; | ||
196 | goto unlock_cl; | ||
197 | } | ||
198 | list_add_tail(&cl->link, &dev->cl_list); | ||
199 | set_bit(id, dev->host_clients_map); | ||
200 | cl->state = ISHTP_CL_INITIALIZING; | ||
201 | |||
202 | unlock_cl: | ||
203 | spin_unlock_irqrestore(&dev->cl_list_lock, flags_cl); | ||
204 | unlock_dev: | ||
205 | spin_unlock_irqrestore(&dev->device_lock, flags); | ||
206 | return ret; | ||
207 | } | ||
208 | EXPORT_SYMBOL(ishtp_cl_link); | ||
209 | |||
210 | /** | ||
211 | * ishtp_cl_unlink() - remove fw_cl from the client device list | ||
212 | * @cl: client device instance | ||
213 | * | ||
214 | * Remove a previously linked device to a ishtp device | ||
215 | */ | ||
216 | void ishtp_cl_unlink(struct ishtp_cl *cl) | ||
217 | { | ||
218 | struct ishtp_device *dev; | ||
219 | struct ishtp_cl *pos; | ||
220 | unsigned long flags; | ||
221 | |||
222 | /* don't shout on error exit path */ | ||
223 | if (!cl || !cl->dev) | ||
224 | return; | ||
225 | |||
226 | dev = cl->dev; | ||
227 | |||
228 | spin_lock_irqsave(&dev->device_lock, flags); | ||
229 | if (dev->open_handle_count > 0) { | ||
230 | clear_bit(cl->host_client_id, dev->host_clients_map); | ||
231 | dev->open_handle_count--; | ||
232 | } | ||
233 | spin_unlock_irqrestore(&dev->device_lock, flags); | ||
234 | |||
235 | /* | ||
236 | * This checks that 'cl' is actually linked into device's structure, | ||
237 | * before attempting 'list_del' | ||
238 | */ | ||
239 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
240 | list_for_each_entry(pos, &dev->cl_list, link) | ||
241 | if (cl->host_client_id == pos->host_client_id) { | ||
242 | list_del_init(&pos->link); | ||
243 | break; | ||
244 | } | ||
245 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
246 | } | ||
247 | EXPORT_SYMBOL(ishtp_cl_unlink); | ||
248 | |||
249 | /** | ||
250 | * ishtp_cl_disconnect() - Send disconnect request to firmware | ||
251 | * @cl: client device instance | ||
252 | * | ||
253 | * Send a disconnect request for a client to firmware. | ||
254 | * | ||
255 | * Return: 0 if successful disconnect response from the firmware or error | ||
256 | * code on failure | ||
257 | */ | ||
258 | int ishtp_cl_disconnect(struct ishtp_cl *cl) | ||
259 | { | ||
260 | struct ishtp_device *dev; | ||
261 | int err; | ||
262 | |||
263 | if (WARN_ON(!cl || !cl->dev)) | ||
264 | return -ENODEV; | ||
265 | |||
266 | dev = cl->dev; | ||
267 | |||
268 | dev->print_log(dev, "%s() state %d\n", __func__, cl->state); | ||
269 | |||
270 | if (cl->state != ISHTP_CL_DISCONNECTING) { | ||
271 | dev->print_log(dev, "%s() Disconnect in progress\n", __func__); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | if (ishtp_hbm_cl_disconnect_req(dev, cl)) { | ||
276 | dev->print_log(dev, "%s() Failed to disconnect\n", __func__); | ||
277 | dev_err(&cl->device->dev, "failed to disconnect.\n"); | ||
278 | return -ENODEV; | ||
279 | } | ||
280 | |||
281 | err = wait_event_interruptible_timeout(cl->wait_ctrl_res, | ||
282 | (dev->dev_state != ISHTP_DEV_ENABLED || | ||
283 | cl->state == ISHTP_CL_DISCONNECTED), | ||
284 | ishtp_secs_to_jiffies(ISHTP_CL_CONNECT_TIMEOUT)); | ||
285 | |||
286 | /* | ||
287 | * If FW reset arrived, this will happen. Don't check cl->, | ||
288 | * as 'cl' may be freed already | ||
289 | */ | ||
290 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
291 | dev->print_log(dev, "%s() dev_state != ISHTP_DEV_ENABLED\n", | ||
292 | __func__); | ||
293 | return -ENODEV; | ||
294 | } | ||
295 | |||
296 | if (cl->state == ISHTP_CL_DISCONNECTED) { | ||
297 | dev->print_log(dev, "%s() successful\n", __func__); | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | return -ENODEV; | ||
302 | } | ||
303 | EXPORT_SYMBOL(ishtp_cl_disconnect); | ||
304 | |||
305 | /** | ||
306 | * ishtp_cl_is_other_connecting() - Check other client is connecting | ||
307 | * @cl: client device instance | ||
308 | * | ||
309 | * Checks if other client with the same fw client id is connecting | ||
310 | * | ||
311 | * Return: true if other client is connected else false | ||
312 | */ | ||
313 | static bool ishtp_cl_is_other_connecting(struct ishtp_cl *cl) | ||
314 | { | ||
315 | struct ishtp_device *dev; | ||
316 | struct ishtp_cl *pos; | ||
317 | unsigned long flags; | ||
318 | |||
319 | if (WARN_ON(!cl || !cl->dev)) | ||
320 | return false; | ||
321 | |||
322 | dev = cl->dev; | ||
323 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
324 | list_for_each_entry(pos, &dev->cl_list, link) { | ||
325 | if ((pos->state == ISHTP_CL_CONNECTING) && (pos != cl) && | ||
326 | cl->fw_client_id == pos->fw_client_id) { | ||
327 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
328 | return true; | ||
329 | } | ||
330 | } | ||
331 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
332 | |||
333 | return false; | ||
334 | } | ||
335 | |||
336 | /** | ||
337 | * ishtp_cl_connect() - Send connect request to firmware | ||
338 | * @cl: client device instance | ||
339 | * | ||
340 | * Send a connect request for a client to firmware. If successful it will | ||
341 | * RX and TX ring buffers | ||
342 | * | ||
343 | * Return: 0 if successful connect response from the firmware and able | ||
344 | * to bind and allocate ring buffers or error code on failure | ||
345 | */ | ||
346 | int ishtp_cl_connect(struct ishtp_cl *cl) | ||
347 | { | ||
348 | struct ishtp_device *dev; | ||
349 | int rets; | ||
350 | |||
351 | if (WARN_ON(!cl || !cl->dev)) | ||
352 | return -ENODEV; | ||
353 | |||
354 | dev = cl->dev; | ||
355 | |||
356 | dev->print_log(dev, "%s() current_state = %d\n", __func__, cl->state); | ||
357 | |||
358 | if (ishtp_cl_is_other_connecting(cl)) { | ||
359 | dev->print_log(dev, "%s() Busy\n", __func__); | ||
360 | return -EBUSY; | ||
361 | } | ||
362 | |||
363 | if (ishtp_hbm_cl_connect_req(dev, cl)) { | ||
364 | dev->print_log(dev, "%s() HBM connect req fail\n", __func__); | ||
365 | return -ENODEV; | ||
366 | } | ||
367 | |||
368 | rets = wait_event_interruptible_timeout(cl->wait_ctrl_res, | ||
369 | (dev->dev_state == ISHTP_DEV_ENABLED && | ||
370 | (cl->state == ISHTP_CL_CONNECTED || | ||
371 | cl->state == ISHTP_CL_DISCONNECTED)), | ||
372 | ishtp_secs_to_jiffies( | ||
373 | ISHTP_CL_CONNECT_TIMEOUT)); | ||
374 | /* | ||
375 | * If FW reset arrived, this will happen. Don't check cl->, | ||
376 | * as 'cl' may be freed already | ||
377 | */ | ||
378 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
379 | dev->print_log(dev, "%s() dev_state != ISHTP_DEV_ENABLED\n", | ||
380 | __func__); | ||
381 | return -EFAULT; | ||
382 | } | ||
383 | |||
384 | if (cl->state != ISHTP_CL_CONNECTED) { | ||
385 | dev->print_log(dev, "%s() state != ISHTP_CL_CONNECTED\n", | ||
386 | __func__); | ||
387 | return -EFAULT; | ||
388 | } | ||
389 | |||
390 | rets = cl->status; | ||
391 | if (rets) { | ||
392 | dev->print_log(dev, "%s() Invalid status\n", __func__); | ||
393 | return rets; | ||
394 | } | ||
395 | |||
396 | rets = ishtp_cl_device_bind(cl); | ||
397 | if (rets) { | ||
398 | dev->print_log(dev, "%s() Bind error\n", __func__); | ||
399 | ishtp_cl_disconnect(cl); | ||
400 | return rets; | ||
401 | } | ||
402 | |||
403 | rets = ishtp_cl_alloc_rx_ring(cl); | ||
404 | if (rets) { | ||
405 | dev->print_log(dev, "%s() Alloc RX ring failed\n", __func__); | ||
406 | /* if failed allocation, disconnect */ | ||
407 | ishtp_cl_disconnect(cl); | ||
408 | return rets; | ||
409 | } | ||
410 | |||
411 | rets = ishtp_cl_alloc_tx_ring(cl); | ||
412 | if (rets) { | ||
413 | dev->print_log(dev, "%s() Alloc TX ring failed\n", __func__); | ||
414 | /* if failed allocation, disconnect */ | ||
415 | ishtp_cl_free_rx_ring(cl); | ||
416 | ishtp_cl_disconnect(cl); | ||
417 | return rets; | ||
418 | } | ||
419 | |||
420 | /* Upon successful connection and allocation, emit flow-control */ | ||
421 | rets = ishtp_cl_read_start(cl); | ||
422 | |||
423 | dev->print_log(dev, "%s() successful\n", __func__); | ||
424 | |||
425 | return rets; | ||
426 | } | ||
427 | EXPORT_SYMBOL(ishtp_cl_connect); | ||
428 | |||
429 | /** | ||
430 | * ishtp_cl_read_start() - Prepare to read client message | ||
431 | * @cl: client device instance | ||
432 | * | ||
433 | * Get a free buffer from pool of free read buffers and add to read buffer | ||
434 | * pool to add contents. Send a flow control request to firmware to be able | ||
435 | * send next message. | ||
436 | * | ||
437 | * Return: 0 if successful or error code on failure | ||
438 | */ | ||
439 | int ishtp_cl_read_start(struct ishtp_cl *cl) | ||
440 | { | ||
441 | struct ishtp_device *dev; | ||
442 | struct ishtp_cl_rb *rb; | ||
443 | int rets; | ||
444 | int i; | ||
445 | unsigned long flags; | ||
446 | unsigned long dev_flags; | ||
447 | |||
448 | if (WARN_ON(!cl || !cl->dev)) | ||
449 | return -ENODEV; | ||
450 | |||
451 | dev = cl->dev; | ||
452 | |||
453 | if (cl->state != ISHTP_CL_CONNECTED) | ||
454 | return -ENODEV; | ||
455 | |||
456 | if (dev->dev_state != ISHTP_DEV_ENABLED) | ||
457 | return -ENODEV; | ||
458 | |||
459 | i = ishtp_fw_cl_by_id(dev, cl->fw_client_id); | ||
460 | if (i < 0) { | ||
461 | dev_err(&cl->device->dev, "no such fw client %d\n", | ||
462 | cl->fw_client_id); | ||
463 | return -ENODEV; | ||
464 | } | ||
465 | |||
466 | /* The current rb is the head of the free rb list */ | ||
467 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
468 | if (list_empty(&cl->free_rb_list.list)) { | ||
469 | dev_warn(&cl->device->dev, | ||
470 | "[ishtp-ish] Rx buffers pool is empty\n"); | ||
471 | rets = -ENOMEM; | ||
472 | rb = NULL; | ||
473 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
474 | goto out; | ||
475 | } | ||
476 | rb = list_entry(cl->free_rb_list.list.next, struct ishtp_cl_rb, list); | ||
477 | list_del_init(&rb->list); | ||
478 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
479 | |||
480 | rb->cl = cl; | ||
481 | rb->buf_idx = 0; | ||
482 | |||
483 | INIT_LIST_HEAD(&rb->list); | ||
484 | rets = 0; | ||
485 | |||
486 | /* | ||
487 | * This must be BEFORE sending flow control - | ||
488 | * response in ISR may come too fast... | ||
489 | */ | ||
490 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
491 | list_add_tail(&rb->list, &dev->read_list.list); | ||
492 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
493 | if (ishtp_hbm_cl_flow_control_req(dev, cl)) { | ||
494 | rets = -ENODEV; | ||
495 | goto out; | ||
496 | } | ||
497 | out: | ||
498 | /* if ishtp_hbm_cl_flow_control_req failed, return rb to free list */ | ||
499 | if (rets && rb) { | ||
500 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
501 | list_del(&rb->list); | ||
502 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
503 | |||
504 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
505 | list_add_tail(&rb->list, &cl->free_rb_list.list); | ||
506 | spin_unlock_irqrestore(&cl->free_list_spinlock, flags); | ||
507 | } | ||
508 | return rets; | ||
509 | } | ||
510 | |||
511 | /** | ||
512 | * ishtp_cl_send() - Send a message to firmware | ||
513 | * @cl: client device instance | ||
514 | * @buf: message buffer | ||
515 | * @length: length of message | ||
516 | * | ||
517 | * If the client is correct state to send message, this function gets a buffer | ||
518 | * from tx ring buffers, copy the message data and call to send the message | ||
519 | * using ishtp_cl_send_msg() | ||
520 | * | ||
521 | * Return: 0 if successful or error code on failure | ||
522 | */ | ||
523 | int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length) | ||
524 | { | ||
525 | struct ishtp_device *dev; | ||
526 | int id; | ||
527 | struct ishtp_cl_tx_ring *cl_msg; | ||
528 | int have_msg_to_send = 0; | ||
529 | unsigned long tx_flags, tx_free_flags; | ||
530 | |||
531 | if (WARN_ON(!cl || !cl->dev)) | ||
532 | return -ENODEV; | ||
533 | |||
534 | dev = cl->dev; | ||
535 | |||
536 | if (cl->state != ISHTP_CL_CONNECTED) { | ||
537 | ++cl->err_send_msg; | ||
538 | return -EPIPE; | ||
539 | } | ||
540 | |||
541 | if (dev->dev_state != ISHTP_DEV_ENABLED) { | ||
542 | ++cl->err_send_msg; | ||
543 | return -ENODEV; | ||
544 | } | ||
545 | |||
546 | /* Check if we have fw client device */ | ||
547 | id = ishtp_fw_cl_by_id(dev, cl->fw_client_id); | ||
548 | if (id < 0) { | ||
549 | ++cl->err_send_msg; | ||
550 | return -ENOENT; | ||
551 | } | ||
552 | |||
553 | if (length > dev->fw_clients[id].props.max_msg_length) { | ||
554 | ++cl->err_send_msg; | ||
555 | return -EMSGSIZE; | ||
556 | } | ||
557 | |||
558 | /* No free bufs */ | ||
559 | spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags); | ||
560 | if (list_empty(&cl->tx_free_list.list)) { | ||
561 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, | ||
562 | tx_free_flags); | ||
563 | ++cl->err_send_msg; | ||
564 | return -ENOMEM; | ||
565 | } | ||
566 | |||
567 | cl_msg = list_first_entry(&cl->tx_free_list.list, | ||
568 | struct ishtp_cl_tx_ring, list); | ||
569 | if (!cl_msg->send_buf.data) { | ||
570 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, | ||
571 | tx_free_flags); | ||
572 | return -EIO; | ||
573 | /* Should not happen, as free list is pre-allocated */ | ||
574 | } | ||
575 | /* | ||
576 | * This is safe, as 'length' is already checked for not exceeding | ||
577 | * max ISHTP message size per client | ||
578 | */ | ||
579 | list_del_init(&cl_msg->list); | ||
580 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags); | ||
581 | memcpy(cl_msg->send_buf.data, buf, length); | ||
582 | cl_msg->send_buf.size = length; | ||
583 | spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); | ||
584 | have_msg_to_send = !list_empty(&cl->tx_list.list); | ||
585 | list_add_tail(&cl_msg->list, &cl->tx_list.list); | ||
586 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
587 | |||
588 | if (!have_msg_to_send && cl->ishtp_flow_ctrl_creds > 0) | ||
589 | ishtp_cl_send_msg(dev, cl); | ||
590 | |||
591 | return 0; | ||
592 | } | ||
593 | EXPORT_SYMBOL(ishtp_cl_send); | ||
594 | |||
595 | /** | ||
596 | * ishtp_cl_read_complete() - read complete | ||
597 | * @rb: Pointer to client request block | ||
598 | * | ||
599 | * If the message is completely received call ishtp_cl_bus_rx_event() | ||
600 | * to process message | ||
601 | */ | ||
602 | static void ishtp_cl_read_complete(struct ishtp_cl_rb *rb) | ||
603 | { | ||
604 | unsigned long flags; | ||
605 | int schedule_work_flag = 0; | ||
606 | struct ishtp_cl *cl = rb->cl; | ||
607 | |||
608 | spin_lock_irqsave(&cl->in_process_spinlock, flags); | ||
609 | /* | ||
610 | * if in-process list is empty, then need to schedule | ||
611 | * the processing thread | ||
612 | */ | ||
613 | schedule_work_flag = list_empty(&cl->in_process_list.list); | ||
614 | list_add_tail(&rb->list, &cl->in_process_list.list); | ||
615 | spin_unlock_irqrestore(&cl->in_process_spinlock, flags); | ||
616 | |||
617 | if (schedule_work_flag) | ||
618 | ishtp_cl_bus_rx_event(cl->device); | ||
619 | } | ||
620 | |||
621 | /** | ||
622 | * ipc_tx_callback() - IPC tx callback function | ||
623 | * @prm: Pointer to client device instance | ||
624 | * | ||
625 | * Send message over IPC either first time or on callback on previous message | ||
626 | * completion | ||
627 | */ | ||
628 | static void ipc_tx_callback(void *prm) | ||
629 | { | ||
630 | struct ishtp_cl *cl = prm; | ||
631 | struct ishtp_cl_tx_ring *cl_msg; | ||
632 | size_t rem; | ||
633 | struct ishtp_device *dev = (cl ? cl->dev : NULL); | ||
634 | struct ishtp_msg_hdr ishtp_hdr; | ||
635 | unsigned long tx_flags, tx_free_flags; | ||
636 | unsigned char *pmsg; | ||
637 | |||
638 | if (!dev) | ||
639 | return; | ||
640 | |||
641 | /* | ||
642 | * Other conditions if some critical error has | ||
643 | * occurred before this callback is called | ||
644 | */ | ||
645 | if (dev->dev_state != ISHTP_DEV_ENABLED) | ||
646 | return; | ||
647 | |||
648 | if (cl->state != ISHTP_CL_CONNECTED) | ||
649 | return; | ||
650 | |||
651 | spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); | ||
652 | if (list_empty(&cl->tx_list.list)) { | ||
653 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
654 | return; | ||
655 | } | ||
656 | |||
657 | if (cl->ishtp_flow_ctrl_creds != 1 && !cl->sending) { | ||
658 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
659 | return; | ||
660 | } | ||
661 | |||
662 | if (!cl->sending) { | ||
663 | --cl->ishtp_flow_ctrl_creds; | ||
664 | cl->last_ipc_acked = 0; | ||
665 | cl->last_tx_path = CL_TX_PATH_IPC; | ||
666 | cl->sending = 1; | ||
667 | } | ||
668 | |||
669 | cl_msg = list_entry(cl->tx_list.list.next, struct ishtp_cl_tx_ring, | ||
670 | list); | ||
671 | rem = cl_msg->send_buf.size - cl->tx_offs; | ||
672 | |||
673 | ishtp_hdr.host_addr = cl->host_client_id; | ||
674 | ishtp_hdr.fw_addr = cl->fw_client_id; | ||
675 | ishtp_hdr.reserved = 0; | ||
676 | pmsg = cl_msg->send_buf.data + cl->tx_offs; | ||
677 | |||
678 | if (rem <= dev->mtu) { | ||
679 | ishtp_hdr.length = rem; | ||
680 | ishtp_hdr.msg_complete = 1; | ||
681 | cl->sending = 0; | ||
682 | list_del_init(&cl_msg->list); /* Must be before write */ | ||
683 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
684 | /* Submit to IPC queue with no callback */ | ||
685 | ishtp_write_message(dev, &ishtp_hdr, pmsg); | ||
686 | spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags); | ||
687 | list_add_tail(&cl_msg->list, &cl->tx_free_list.list); | ||
688 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, | ||
689 | tx_free_flags); | ||
690 | } else { | ||
691 | /* Send IPC fragment */ | ||
692 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
693 | cl->tx_offs += dev->mtu; | ||
694 | ishtp_hdr.length = dev->mtu; | ||
695 | ishtp_hdr.msg_complete = 0; | ||
696 | ishtp_send_msg(dev, &ishtp_hdr, pmsg, ipc_tx_callback, cl); | ||
697 | } | ||
698 | } | ||
699 | |||
700 | /** | ||
701 | * ishtp_cl_send_msg_ipc() -Send message using IPC | ||
702 | * @dev: ISHTP device instance | ||
703 | * @cl: Pointer to client device instance | ||
704 | * | ||
705 | * Send message over IPC not using DMA | ||
706 | */ | ||
707 | static void ishtp_cl_send_msg_ipc(struct ishtp_device *dev, | ||
708 | struct ishtp_cl *cl) | ||
709 | { | ||
710 | /* If last DMA message wasn't acked yet, leave this one in Tx queue */ | ||
711 | if (cl->last_tx_path == CL_TX_PATH_DMA && cl->last_dma_acked == 0) | ||
712 | return; | ||
713 | |||
714 | cl->tx_offs = 0; | ||
715 | ipc_tx_callback(cl); | ||
716 | ++cl->send_msg_cnt_ipc; | ||
717 | } | ||
718 | |||
719 | /** | ||
720 | * ishtp_cl_send_msg_dma() -Send message using DMA | ||
721 | * @dev: ISHTP device instance | ||
722 | * @cl: Pointer to client device instance | ||
723 | * | ||
724 | * Send message using DMA | ||
725 | */ | ||
726 | static void ishtp_cl_send_msg_dma(struct ishtp_device *dev, | ||
727 | struct ishtp_cl *cl) | ||
728 | { | ||
729 | struct ishtp_msg_hdr hdr; | ||
730 | struct dma_xfer_hbm dma_xfer; | ||
731 | unsigned char *msg_addr; | ||
732 | int off; | ||
733 | struct ishtp_cl_tx_ring *cl_msg; | ||
734 | unsigned long tx_flags, tx_free_flags; | ||
735 | |||
736 | /* If last IPC message wasn't acked yet, leave this one in Tx queue */ | ||
737 | if (cl->last_tx_path == CL_TX_PATH_IPC && cl->last_ipc_acked == 0) | ||
738 | return; | ||
739 | |||
740 | spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); | ||
741 | if (list_empty(&cl->tx_list.list)) { | ||
742 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
743 | return; | ||
744 | } | ||
745 | |||
746 | cl_msg = list_entry(cl->tx_list.list.next, struct ishtp_cl_tx_ring, | ||
747 | list); | ||
748 | |||
749 | msg_addr = ishtp_cl_get_dma_send_buf(dev, cl_msg->send_buf.size); | ||
750 | if (!msg_addr) { | ||
751 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
752 | if (dev->transfer_path == CL_TX_PATH_DEFAULT) | ||
753 | ishtp_cl_send_msg_ipc(dev, cl); | ||
754 | return; | ||
755 | } | ||
756 | |||
757 | list_del_init(&cl_msg->list); /* Must be before write */ | ||
758 | spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); | ||
759 | |||
760 | --cl->ishtp_flow_ctrl_creds; | ||
761 | cl->last_dma_acked = 0; | ||
762 | cl->last_dma_addr = msg_addr; | ||
763 | cl->last_tx_path = CL_TX_PATH_DMA; | ||
764 | |||
765 | /* write msg to dma buf */ | ||
766 | memcpy(msg_addr, cl_msg->send_buf.data, cl_msg->send_buf.size); | ||
767 | |||
768 | /* send dma_xfer hbm msg */ | ||
769 | off = msg_addr - (unsigned char *)dev->ishtp_host_dma_tx_buf; | ||
770 | ishtp_hbm_hdr(&hdr, sizeof(struct dma_xfer_hbm)); | ||
771 | dma_xfer.hbm = DMA_XFER; | ||
772 | dma_xfer.fw_client_id = cl->fw_client_id; | ||
773 | dma_xfer.host_client_id = cl->host_client_id; | ||
774 | dma_xfer.reserved = 0; | ||
775 | dma_xfer.msg_addr = dev->ishtp_host_dma_tx_buf_phys + off; | ||
776 | dma_xfer.msg_length = cl_msg->send_buf.size; | ||
777 | dma_xfer.reserved2 = 0; | ||
778 | ishtp_write_message(dev, &hdr, (unsigned char *)&dma_xfer); | ||
779 | spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags); | ||
780 | list_add_tail(&cl_msg->list, &cl->tx_free_list.list); | ||
781 | spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags); | ||
782 | ++cl->send_msg_cnt_dma; | ||
783 | } | ||
784 | |||
785 | /** | ||
786 | * ishtp_cl_send_msg() -Send message using DMA or IPC | ||
787 | * @dev: ISHTP device instance | ||
788 | * @cl: Pointer to client device instance | ||
789 | * | ||
790 | * Send message using DMA or IPC based on transfer_path | ||
791 | */ | ||
792 | void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl) | ||
793 | { | ||
794 | if (dev->transfer_path == CL_TX_PATH_DMA) | ||
795 | ishtp_cl_send_msg_dma(dev, cl); | ||
796 | else | ||
797 | ishtp_cl_send_msg_ipc(dev, cl); | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * recv_ishtp_cl_msg() -Receive client message | ||
802 | * @dev: ISHTP device instance | ||
803 | * @ishtp_hdr: Pointer to message header | ||
804 | * | ||
805 | * Receive and dispatch ISHTP client messages. This function executes in ISR | ||
806 | * context | ||
807 | */ | ||
808 | void recv_ishtp_cl_msg(struct ishtp_device *dev, | ||
809 | struct ishtp_msg_hdr *ishtp_hdr) | ||
810 | { | ||
811 | struct ishtp_cl *cl; | ||
812 | struct ishtp_cl_rb *rb; | ||
813 | struct ishtp_cl_rb *new_rb; | ||
814 | unsigned char *buffer = NULL; | ||
815 | struct ishtp_cl_rb *complete_rb = NULL; | ||
816 | unsigned long dev_flags; | ||
817 | unsigned long flags; | ||
818 | int rb_count; | ||
819 | |||
820 | if (ishtp_hdr->reserved) { | ||
821 | dev_err(dev->devc, "corrupted message header.\n"); | ||
822 | goto eoi; | ||
823 | } | ||
824 | |||
825 | if (ishtp_hdr->length > IPC_PAYLOAD_SIZE) { | ||
826 | dev_err(dev->devc, | ||
827 | "ISHTP message length in hdr exceeds IPC MTU\n"); | ||
828 | goto eoi; | ||
829 | } | ||
830 | |||
831 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
832 | rb_count = -1; | ||
833 | list_for_each_entry(rb, &dev->read_list.list, list) { | ||
834 | ++rb_count; | ||
835 | cl = rb->cl; | ||
836 | if (!cl || !(cl->host_client_id == ishtp_hdr->host_addr && | ||
837 | cl->fw_client_id == ishtp_hdr->fw_addr) || | ||
838 | !(cl->state == ISHTP_CL_CONNECTED)) | ||
839 | continue; | ||
840 | |||
841 | /* If no Rx buffer is allocated, disband the rb */ | ||
842 | if (rb->buffer.size == 0 || rb->buffer.data == NULL) { | ||
843 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
844 | dev_flags); | ||
845 | dev_err(&cl->device->dev, | ||
846 | "Rx buffer is not allocated.\n"); | ||
847 | list_del(&rb->list); | ||
848 | ishtp_io_rb_free(rb); | ||
849 | cl->status = -ENOMEM; | ||
850 | goto eoi; | ||
851 | } | ||
852 | |||
853 | /* | ||
854 | * If message buffer overflown (exceeds max. client msg | ||
855 | * size, drop message and return to free buffer. | ||
856 | * Do we need to disconnect such a client? (We don't send | ||
857 | * back FC, so communication will be stuck anyway) | ||
858 | */ | ||
859 | if (rb->buffer.size < ishtp_hdr->length + rb->buf_idx) { | ||
860 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
861 | dev_flags); | ||
862 | dev_err(&cl->device->dev, | ||
863 | "message overflow. size %d len %d idx %ld\n", | ||
864 | rb->buffer.size, ishtp_hdr->length, | ||
865 | rb->buf_idx); | ||
866 | list_del(&rb->list); | ||
867 | ishtp_cl_io_rb_recycle(rb); | ||
868 | cl->status = -EIO; | ||
869 | goto eoi; | ||
870 | } | ||
871 | |||
872 | buffer = rb->buffer.data + rb->buf_idx; | ||
873 | dev->ops->ishtp_read(dev, buffer, ishtp_hdr->length); | ||
874 | |||
875 | rb->buf_idx += ishtp_hdr->length; | ||
876 | if (ishtp_hdr->msg_complete) { | ||
877 | /* Last fragment in message - it's complete */ | ||
878 | cl->status = 0; | ||
879 | list_del(&rb->list); | ||
880 | complete_rb = rb; | ||
881 | |||
882 | --cl->out_flow_ctrl_creds; | ||
883 | /* | ||
884 | * the whole msg arrived, send a new FC, and add a new | ||
885 | * rb buffer for the next coming msg | ||
886 | */ | ||
887 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
888 | |||
889 | if (!list_empty(&cl->free_rb_list.list)) { | ||
890 | new_rb = list_entry(cl->free_rb_list.list.next, | ||
891 | struct ishtp_cl_rb, list); | ||
892 | list_del_init(&new_rb->list); | ||
893 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
894 | flags); | ||
895 | new_rb->cl = cl; | ||
896 | new_rb->buf_idx = 0; | ||
897 | INIT_LIST_HEAD(&new_rb->list); | ||
898 | list_add_tail(&new_rb->list, | ||
899 | &dev->read_list.list); | ||
900 | |||
901 | ishtp_hbm_cl_flow_control_req(dev, cl); | ||
902 | } else { | ||
903 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
904 | flags); | ||
905 | } | ||
906 | } | ||
907 | /* One more fragment in message (even if this was last) */ | ||
908 | ++cl->recv_msg_num_frags; | ||
909 | |||
910 | /* | ||
911 | * We can safely break here (and in BH too), | ||
912 | * a single input message can go only to a single request! | ||
913 | */ | ||
914 | break; | ||
915 | } | ||
916 | |||
917 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
918 | /* If it's nobody's message, just read and discard it */ | ||
919 | if (!buffer) { | ||
920 | uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; | ||
921 | |||
922 | dev_err(dev->devc, "Dropped Rx msg - no request\n"); | ||
923 | dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length); | ||
924 | goto eoi; | ||
925 | } | ||
926 | |||
927 | if (complete_rb) { | ||
928 | getnstimeofday(&cl->ts_rx); | ||
929 | ++cl->recv_msg_cnt_ipc; | ||
930 | ishtp_cl_read_complete(complete_rb); | ||
931 | } | ||
932 | eoi: | ||
933 | return; | ||
934 | } | ||
935 | |||
936 | /** | ||
937 | * recv_ishtp_cl_msg_dma() -Receive client message | ||
938 | * @dev: ISHTP device instance | ||
939 | * @msg: message pointer | ||
940 | * @hbm: hbm buffer | ||
941 | * | ||
942 | * Receive and dispatch ISHTP client messages using DMA. This function executes | ||
943 | * in ISR context | ||
944 | */ | ||
945 | void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, | ||
946 | struct dma_xfer_hbm *hbm) | ||
947 | { | ||
948 | struct ishtp_cl *cl; | ||
949 | struct ishtp_cl_rb *rb; | ||
950 | struct ishtp_cl_rb *new_rb; | ||
951 | unsigned char *buffer = NULL; | ||
952 | struct ishtp_cl_rb *complete_rb = NULL; | ||
953 | unsigned long dev_flags; | ||
954 | unsigned long flags; | ||
955 | |||
956 | spin_lock_irqsave(&dev->read_list_spinlock, dev_flags); | ||
957 | list_for_each_entry(rb, &dev->read_list.list, list) { | ||
958 | cl = rb->cl; | ||
959 | if (!cl || !(cl->host_client_id == hbm->host_client_id && | ||
960 | cl->fw_client_id == hbm->fw_client_id) || | ||
961 | !(cl->state == ISHTP_CL_CONNECTED)) | ||
962 | continue; | ||
963 | |||
964 | /* | ||
965 | * If no Rx buffer is allocated, disband the rb | ||
966 | */ | ||
967 | if (rb->buffer.size == 0 || rb->buffer.data == NULL) { | ||
968 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
969 | dev_flags); | ||
970 | dev_err(&cl->device->dev, | ||
971 | "response buffer is not allocated.\n"); | ||
972 | list_del(&rb->list); | ||
973 | ishtp_io_rb_free(rb); | ||
974 | cl->status = -ENOMEM; | ||
975 | goto eoi; | ||
976 | } | ||
977 | |||
978 | /* | ||
979 | * If message buffer overflown (exceeds max. client msg | ||
980 | * size, drop message and return to free buffer. | ||
981 | * Do we need to disconnect such a client? (We don't send | ||
982 | * back FC, so communication will be stuck anyway) | ||
983 | */ | ||
984 | if (rb->buffer.size < hbm->msg_length) { | ||
985 | spin_unlock_irqrestore(&dev->read_list_spinlock, | ||
986 | dev_flags); | ||
987 | dev_err(&cl->device->dev, | ||
988 | "message overflow. size %d len %d idx %ld\n", | ||
989 | rb->buffer.size, hbm->msg_length, rb->buf_idx); | ||
990 | list_del(&rb->list); | ||
991 | ishtp_cl_io_rb_recycle(rb); | ||
992 | cl->status = -EIO; | ||
993 | goto eoi; | ||
994 | } | ||
995 | |||
996 | buffer = rb->buffer.data; | ||
997 | memcpy(buffer, msg, hbm->msg_length); | ||
998 | rb->buf_idx = hbm->msg_length; | ||
999 | |||
1000 | /* Last fragment in message - it's complete */ | ||
1001 | cl->status = 0; | ||
1002 | list_del(&rb->list); | ||
1003 | complete_rb = rb; | ||
1004 | |||
1005 | --cl->out_flow_ctrl_creds; | ||
1006 | /* | ||
1007 | * the whole msg arrived, send a new FC, and add a new | ||
1008 | * rb buffer for the next coming msg | ||
1009 | */ | ||
1010 | spin_lock_irqsave(&cl->free_list_spinlock, flags); | ||
1011 | |||
1012 | if (!list_empty(&cl->free_rb_list.list)) { | ||
1013 | new_rb = list_entry(cl->free_rb_list.list.next, | ||
1014 | struct ishtp_cl_rb, list); | ||
1015 | list_del_init(&new_rb->list); | ||
1016 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
1017 | flags); | ||
1018 | new_rb->cl = cl; | ||
1019 | new_rb->buf_idx = 0; | ||
1020 | INIT_LIST_HEAD(&new_rb->list); | ||
1021 | list_add_tail(&new_rb->list, | ||
1022 | &dev->read_list.list); | ||
1023 | |||
1024 | ishtp_hbm_cl_flow_control_req(dev, cl); | ||
1025 | } else { | ||
1026 | spin_unlock_irqrestore(&cl->free_list_spinlock, | ||
1027 | flags); | ||
1028 | } | ||
1029 | |||
1030 | /* One more fragment in message (this is always last) */ | ||
1031 | ++cl->recv_msg_num_frags; | ||
1032 | |||
1033 | /* | ||
1034 | * We can safely break here (and in BH too), | ||
1035 | * a single input message can go only to a single request! | ||
1036 | */ | ||
1037 | break; | ||
1038 | } | ||
1039 | |||
1040 | spin_unlock_irqrestore(&dev->read_list_spinlock, dev_flags); | ||
1041 | /* If it's nobody's message, just read and discard it */ | ||
1042 | if (!buffer) { | ||
1043 | dev_err(dev->devc, "Dropped Rx (DMA) msg - no request\n"); | ||
1044 | goto eoi; | ||
1045 | } | ||
1046 | |||
1047 | if (complete_rb) { | ||
1048 | getnstimeofday(&cl->ts_rx); | ||
1049 | ++cl->recv_msg_cnt_dma; | ||
1050 | ishtp_cl_read_complete(complete_rb); | ||
1051 | } | ||
1052 | eoi: | ||
1053 | return; | ||
1054 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.h b/drivers/hid/intel-ish-hid/ishtp/client.h new file mode 100644 index 000000000000..444d069c2ed4 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/client.h | |||
@@ -0,0 +1,182 @@ | |||
1 | /* | ||
2 | * ISHTP client logic | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #ifndef _ISHTP_CLIENT_H_ | ||
17 | #define _ISHTP_CLIENT_H_ | ||
18 | |||
19 | #include <linux/types.h> | ||
20 | #include "ishtp-dev.h" | ||
21 | |||
22 | /* Client state */ | ||
23 | enum cl_state { | ||
24 | ISHTP_CL_INITIALIZING = 0, | ||
25 | ISHTP_CL_CONNECTING, | ||
26 | ISHTP_CL_CONNECTED, | ||
27 | ISHTP_CL_DISCONNECTING, | ||
28 | ISHTP_CL_DISCONNECTED | ||
29 | }; | ||
30 | |||
31 | /* Tx and Rx ring size */ | ||
32 | #define CL_DEF_RX_RING_SIZE 2 | ||
33 | #define CL_DEF_TX_RING_SIZE 2 | ||
34 | #define CL_MAX_RX_RING_SIZE 32 | ||
35 | #define CL_MAX_TX_RING_SIZE 32 | ||
36 | |||
37 | #define DMA_SLOT_SIZE 4096 | ||
38 | /* Number of IPC fragments after which it's worth sending via DMA */ | ||
39 | #define DMA_WORTH_THRESHOLD 3 | ||
40 | |||
41 | /* DMA/IPC Tx paths. Other the default means enforcement */ | ||
42 | #define CL_TX_PATH_DEFAULT 0 | ||
43 | #define CL_TX_PATH_IPC 1 | ||
44 | #define CL_TX_PATH_DMA 2 | ||
45 | |||
46 | /* Client Tx buffer list entry */ | ||
47 | struct ishtp_cl_tx_ring { | ||
48 | struct list_head list; | ||
49 | struct ishtp_msg_data send_buf; | ||
50 | }; | ||
51 | |||
52 | /* ISHTP client instance */ | ||
53 | struct ishtp_cl { | ||
54 | struct list_head link; | ||
55 | struct ishtp_device *dev; | ||
56 | enum cl_state state; | ||
57 | int status; | ||
58 | |||
59 | /* Link to ISHTP bus device */ | ||
60 | struct ishtp_cl_device *device; | ||
61 | |||
62 | /* ID of client connected */ | ||
63 | uint8_t host_client_id; | ||
64 | uint8_t fw_client_id; | ||
65 | uint8_t ishtp_flow_ctrl_creds; | ||
66 | uint8_t out_flow_ctrl_creds; | ||
67 | |||
68 | /* dma */ | ||
69 | int last_tx_path; | ||
70 | /* 0: ack wasn't received,1:ack was received */ | ||
71 | int last_dma_acked; | ||
72 | unsigned char *last_dma_addr; | ||
73 | /* 0: ack wasn't received,1:ack was received */ | ||
74 | int last_ipc_acked; | ||
75 | |||
76 | /* Rx ring buffer pool */ | ||
77 | unsigned int rx_ring_size; | ||
78 | struct ishtp_cl_rb free_rb_list; | ||
79 | spinlock_t free_list_spinlock; | ||
80 | /* Rx in-process list */ | ||
81 | struct ishtp_cl_rb in_process_list; | ||
82 | spinlock_t in_process_spinlock; | ||
83 | |||
84 | /* Client Tx buffers list */ | ||
85 | unsigned int tx_ring_size; | ||
86 | struct ishtp_cl_tx_ring tx_list, tx_free_list; | ||
87 | spinlock_t tx_list_spinlock; | ||
88 | spinlock_t tx_free_list_spinlock; | ||
89 | size_t tx_offs; /* Offset in buffer at head of 'tx_list' */ | ||
90 | |||
91 | /** | ||
92 | * if we get a FC, and the list is not empty, we must know whether we | ||
93 | * are at the middle of sending. | ||
94 | * if so -need to increase FC counter, otherwise, need to start sending | ||
95 | * the first msg in list | ||
96 | * (!)This is for counting-FC implementation only. Within single-FC the | ||
97 | * other party may NOT send FC until it receives complete message | ||
98 | */ | ||
99 | int sending; | ||
100 | |||
101 | /* Send FC spinlock */ | ||
102 | spinlock_t fc_spinlock; | ||
103 | |||
104 | /* wait queue for connect and disconnect response from FW */ | ||
105 | wait_queue_head_t wait_ctrl_res; | ||
106 | |||
107 | /* Error stats */ | ||
108 | unsigned int err_send_msg; | ||
109 | unsigned int err_send_fc; | ||
110 | |||
111 | /* Send/recv stats */ | ||
112 | unsigned int send_msg_cnt_ipc; | ||
113 | unsigned int send_msg_cnt_dma; | ||
114 | unsigned int recv_msg_cnt_ipc; | ||
115 | unsigned int recv_msg_cnt_dma; | ||
116 | unsigned int recv_msg_num_frags; | ||
117 | unsigned int ishtp_flow_ctrl_cnt; | ||
118 | unsigned int out_flow_ctrl_cnt; | ||
119 | |||
120 | /* Rx msg ... out FC timing */ | ||
121 | struct timespec ts_rx; | ||
122 | struct timespec ts_out_fc; | ||
123 | struct timespec ts_max_fc_delay; | ||
124 | void *client_data; | ||
125 | }; | ||
126 | |||
127 | /* Client connection managenment internal functions */ | ||
128 | int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, uuid_le *uuid); | ||
129 | int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id); | ||
130 | void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl); | ||
131 | void recv_ishtp_cl_msg(struct ishtp_device *dev, | ||
132 | struct ishtp_msg_hdr *ishtp_hdr); | ||
133 | int ishtp_cl_read_start(struct ishtp_cl *cl); | ||
134 | |||
135 | /* Ring Buffer I/F */ | ||
136 | int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl); | ||
137 | int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl); | ||
138 | void ishtp_cl_free_rx_ring(struct ishtp_cl *cl); | ||
139 | void ishtp_cl_free_tx_ring(struct ishtp_cl *cl); | ||
140 | |||
141 | /* DMA I/F functions */ | ||
142 | void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, | ||
143 | struct dma_xfer_hbm *hbm); | ||
144 | void ishtp_cl_alloc_dma_buf(struct ishtp_device *dev); | ||
145 | void ishtp_cl_free_dma_buf(struct ishtp_device *dev); | ||
146 | void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev, | ||
147 | uint32_t size); | ||
148 | void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev, | ||
149 | void *msg_addr, | ||
150 | uint8_t size); | ||
151 | |||
152 | /* Request blocks alloc/free I/F */ | ||
153 | struct ishtp_cl_rb *ishtp_io_rb_init(struct ishtp_cl *cl); | ||
154 | void ishtp_io_rb_free(struct ishtp_cl_rb *priv_rb); | ||
155 | int ishtp_io_rb_alloc_buf(struct ishtp_cl_rb *rb, size_t length); | ||
156 | |||
157 | /** | ||
158 | * ishtp_cl_cmp_id - tells if file private data have same id | ||
159 | * returns true - if ids are the same and not NULL | ||
160 | */ | ||
161 | static inline bool ishtp_cl_cmp_id(const struct ishtp_cl *cl1, | ||
162 | const struct ishtp_cl *cl2) | ||
163 | { | ||
164 | return cl1 && cl2 && | ||
165 | (cl1->host_client_id == cl2->host_client_id) && | ||
166 | (cl1->fw_client_id == cl2->fw_client_id); | ||
167 | } | ||
168 | |||
169 | /* exported functions from ISHTP under client management scope */ | ||
170 | struct ishtp_cl *ishtp_cl_allocate(struct ishtp_device *dev); | ||
171 | void ishtp_cl_free(struct ishtp_cl *cl); | ||
172 | int ishtp_cl_link(struct ishtp_cl *cl, int id); | ||
173 | void ishtp_cl_unlink(struct ishtp_cl *cl); | ||
174 | int ishtp_cl_disconnect(struct ishtp_cl *cl); | ||
175 | int ishtp_cl_connect(struct ishtp_cl *cl); | ||
176 | int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length); | ||
177 | int ishtp_cl_flush_queues(struct ishtp_cl *cl); | ||
178 | |||
179 | /* exported functions from ISHTP client buffer management scope */ | ||
180 | int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb); | ||
181 | |||
182 | #endif /* _ISHTP_CLIENT_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/dma-if.c b/drivers/hid/intel-ish-hid/ishtp/dma-if.c new file mode 100644 index 000000000000..2783f3666114 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/dma-if.c | |||
@@ -0,0 +1,175 @@ | |||
1 | /* | ||
2 | * ISHTP DMA I/F functions | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/slab.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/wait.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | #include "ishtp-dev.h" | ||
23 | #include "client.h" | ||
24 | |||
25 | /** | ||
26 | * ishtp_cl_alloc_dma_buf() - Allocate DMA RX and TX buffer | ||
27 | * @dev: ishtp device | ||
28 | * | ||
29 | * Allocate RX and TX DMA buffer once during bus setup. | ||
30 | * It allocates 1MB, RX and TX DMA buffer, which are divided | ||
31 | * into slots. | ||
32 | */ | ||
33 | void ishtp_cl_alloc_dma_buf(struct ishtp_device *dev) | ||
34 | { | ||
35 | dma_addr_t h; | ||
36 | |||
37 | if (dev->ishtp_host_dma_tx_buf) | ||
38 | return; | ||
39 | |||
40 | dev->ishtp_host_dma_tx_buf_size = 1024*1024; | ||
41 | dev->ishtp_host_dma_rx_buf_size = 1024*1024; | ||
42 | |||
43 | /* Allocate Tx buffer and init usage bitmap */ | ||
44 | dev->ishtp_host_dma_tx_buf = dma_alloc_coherent(dev->devc, | ||
45 | dev->ishtp_host_dma_tx_buf_size, | ||
46 | &h, GFP_KERNEL); | ||
47 | if (dev->ishtp_host_dma_tx_buf) | ||
48 | dev->ishtp_host_dma_tx_buf_phys = h; | ||
49 | |||
50 | dev->ishtp_dma_num_slots = dev->ishtp_host_dma_tx_buf_size / | ||
51 | DMA_SLOT_SIZE; | ||
52 | |||
53 | dev->ishtp_dma_tx_map = kcalloc(dev->ishtp_dma_num_slots, | ||
54 | sizeof(uint8_t), | ||
55 | GFP_KERNEL); | ||
56 | spin_lock_init(&dev->ishtp_dma_tx_lock); | ||
57 | |||
58 | /* Allocate Rx buffer */ | ||
59 | dev->ishtp_host_dma_rx_buf = dma_alloc_coherent(dev->devc, | ||
60 | dev->ishtp_host_dma_rx_buf_size, | ||
61 | &h, GFP_KERNEL); | ||
62 | |||
63 | if (dev->ishtp_host_dma_rx_buf) | ||
64 | dev->ishtp_host_dma_rx_buf_phys = h; | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * ishtp_cl_free_dma_buf() - Free DMA RX and TX buffer | ||
69 | * @dev: ishtp device | ||
70 | * | ||
71 | * Free DMA buffer when all clients are released. This is | ||
72 | * only happens during error path in ISH built in driver | ||
73 | * model | ||
74 | */ | ||
75 | void ishtp_cl_free_dma_buf(struct ishtp_device *dev) | ||
76 | { | ||
77 | dma_addr_t h; | ||
78 | |||
79 | if (dev->ishtp_host_dma_tx_buf) { | ||
80 | h = dev->ishtp_host_dma_tx_buf_phys; | ||
81 | dma_free_coherent(dev->devc, dev->ishtp_host_dma_tx_buf_size, | ||
82 | dev->ishtp_host_dma_tx_buf, h); | ||
83 | } | ||
84 | |||
85 | if (dev->ishtp_host_dma_rx_buf) { | ||
86 | h = dev->ishtp_host_dma_rx_buf_phys; | ||
87 | dma_free_coherent(dev->devc, dev->ishtp_host_dma_rx_buf_size, | ||
88 | dev->ishtp_host_dma_rx_buf, h); | ||
89 | } | ||
90 | |||
91 | kfree(dev->ishtp_dma_tx_map); | ||
92 | dev->ishtp_host_dma_tx_buf = NULL; | ||
93 | dev->ishtp_host_dma_rx_buf = NULL; | ||
94 | dev->ishtp_dma_tx_map = NULL; | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * ishtp_cl_get_dma_send_buf() - Get a DMA memory slot | ||
99 | * @dev: ishtp device | ||
100 | * @size: Size of memory to get | ||
101 | * | ||
102 | * Find and return free address of "size" bytes in dma tx buffer. | ||
103 | * the function will mark this address as "in-used" memory. | ||
104 | * | ||
105 | * Return: NULL when no free buffer else a buffer to copy | ||
106 | */ | ||
107 | void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev, | ||
108 | uint32_t size) | ||
109 | { | ||
110 | unsigned long flags; | ||
111 | int i, j, free; | ||
112 | /* additional slot is needed if there is rem */ | ||
113 | int required_slots = (size / DMA_SLOT_SIZE) | ||
114 | + 1 * (size % DMA_SLOT_SIZE != 0); | ||
115 | |||
116 | spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); | ||
117 | for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) { | ||
118 | free = 1; | ||
119 | for (j = 0; j < required_slots; j++) | ||
120 | if (dev->ishtp_dma_tx_map[i+j]) { | ||
121 | free = 0; | ||
122 | i += j; | ||
123 | break; | ||
124 | } | ||
125 | if (free) { | ||
126 | /* mark memory as "caught" */ | ||
127 | for (j = 0; j < required_slots; j++) | ||
128 | dev->ishtp_dma_tx_map[i+j] = 1; | ||
129 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
130 | return (i * DMA_SLOT_SIZE) + | ||
131 | (unsigned char *)dev->ishtp_host_dma_tx_buf; | ||
132 | } | ||
133 | } | ||
134 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
135 | dev_err(dev->devc, "No free DMA buffer to send msg\n"); | ||
136 | return NULL; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * ishtp_cl_release_dma_acked_mem() - Release DMA memory slot | ||
141 | * @dev: ishtp device | ||
142 | * @msg_addr: message address of slot | ||
143 | * @size: Size of memory to get | ||
144 | * | ||
145 | * Release_dma_acked_mem - returnes the acked memory to free list. | ||
146 | * (from msg_addr, size bytes long) | ||
147 | */ | ||
148 | void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev, | ||
149 | void *msg_addr, | ||
150 | uint8_t size) | ||
151 | { | ||
152 | unsigned long flags; | ||
153 | int acked_slots = (size / DMA_SLOT_SIZE) | ||
154 | + 1 * (size % DMA_SLOT_SIZE != 0); | ||
155 | int i, j; | ||
156 | |||
157 | if ((msg_addr - dev->ishtp_host_dma_tx_buf) % DMA_SLOT_SIZE) { | ||
158 | dev_err(dev->devc, "Bad DMA Tx ack address\n"); | ||
159 | return; | ||
160 | } | ||
161 | |||
162 | i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE; | ||
163 | spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags); | ||
164 | for (j = 0; j < acked_slots; j++) { | ||
165 | if ((i + j) >= dev->ishtp_dma_num_slots || | ||
166 | !dev->ishtp_dma_tx_map[i+j]) { | ||
167 | /* no such slot, or memory is already free */ | ||
168 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
169 | dev_err(dev->devc, "Bad DMA Tx ack address\n"); | ||
170 | return; | ||
171 | } | ||
172 | dev->ishtp_dma_tx_map[i+j] = 0; | ||
173 | } | ||
174 | spin_unlock_irqrestore(&dev->ishtp_dma_tx_lock, flags); | ||
175 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c new file mode 100644 index 000000000000..74bffee60774 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c | |||
@@ -0,0 +1,1032 @@ | |||
1 | /* | ||
2 | * ISHTP bus layer messages handling | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/export.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/wait.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/miscdevice.h> | ||
23 | #include "ishtp-dev.h" | ||
24 | #include "hbm.h" | ||
25 | #include "client.h" | ||
26 | |||
27 | /** | ||
28 | * ishtp_hbm_fw_cl_allocate() - Allocate FW clients | ||
29 | * @dev: ISHTP device instance | ||
30 | * | ||
31 | * Allocates storage for fw clients | ||
32 | */ | ||
33 | static void ishtp_hbm_fw_cl_allocate(struct ishtp_device *dev) | ||
34 | { | ||
35 | struct ishtp_fw_client *clients; | ||
36 | int b; | ||
37 | |||
38 | /* count how many ISH clients we have */ | ||
39 | for_each_set_bit(b, dev->fw_clients_map, ISHTP_CLIENTS_MAX) | ||
40 | dev->fw_clients_num++; | ||
41 | |||
42 | if (dev->fw_clients_num <= 0) | ||
43 | return; | ||
44 | |||
45 | /* allocate storage for fw clients representation */ | ||
46 | clients = kcalloc(dev->fw_clients_num, sizeof(struct ishtp_fw_client), | ||
47 | GFP_KERNEL); | ||
48 | if (!clients) { | ||
49 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
50 | ish_hw_reset(dev); | ||
51 | return; | ||
52 | } | ||
53 | dev->fw_clients = clients; | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * ishtp_hbm_cl_hdr() - construct client hbm header | ||
58 | * @cl: client | ||
59 | * @hbm_cmd: host bus message command | ||
60 | * @buf: buffer for cl header | ||
61 | * @len: buffer length | ||
62 | * | ||
63 | * Initialize HBM buffer | ||
64 | */ | ||
65 | static inline void ishtp_hbm_cl_hdr(struct ishtp_cl *cl, uint8_t hbm_cmd, | ||
66 | void *buf, size_t len) | ||
67 | { | ||
68 | struct ishtp_hbm_cl_cmd *cmd = buf; | ||
69 | |||
70 | memset(cmd, 0, len); | ||
71 | |||
72 | cmd->hbm_cmd = hbm_cmd; | ||
73 | cmd->host_addr = cl->host_client_id; | ||
74 | cmd->fw_addr = cl->fw_client_id; | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * ishtp_hbm_cl_addr_equal() - Compare client address | ||
79 | * @cl: client | ||
80 | * @buf: Client command buffer | ||
81 | * | ||
82 | * Compare client address with the address in command buffer | ||
83 | * | ||
84 | * Return: True if they have the same address | ||
85 | */ | ||
86 | static inline bool ishtp_hbm_cl_addr_equal(struct ishtp_cl *cl, void *buf) | ||
87 | { | ||
88 | struct ishtp_hbm_cl_cmd *cmd = buf; | ||
89 | |||
90 | return cl->host_client_id == cmd->host_addr && | ||
91 | cl->fw_client_id == cmd->fw_addr; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * ishtp_hbm_start_wait() - Wait for HBM start message | ||
96 | * @dev: ISHTP device instance | ||
97 | * | ||
98 | * Wait for HBM start message from firmware | ||
99 | * | ||
100 | * Return: 0 if HBM start is/was received else timeout error | ||
101 | */ | ||
102 | int ishtp_hbm_start_wait(struct ishtp_device *dev) | ||
103 | { | ||
104 | int ret; | ||
105 | |||
106 | if (dev->hbm_state > ISHTP_HBM_START) | ||
107 | return 0; | ||
108 | |||
109 | dev_dbg(dev->devc, "Going to wait for ishtp start. hbm_state=%08X\n", | ||
110 | dev->hbm_state); | ||
111 | ret = wait_event_interruptible_timeout(dev->wait_hbm_recvd_msg, | ||
112 | dev->hbm_state >= ISHTP_HBM_STARTED, | ||
113 | (ISHTP_INTEROP_TIMEOUT * HZ)); | ||
114 | |||
115 | dev_dbg(dev->devc, | ||
116 | "Woke up from waiting for ishtp start. hbm_state=%08X\n", | ||
117 | dev->hbm_state); | ||
118 | |||
119 | if (ret <= 0 && (dev->hbm_state <= ISHTP_HBM_START)) { | ||
120 | dev->hbm_state = ISHTP_HBM_IDLE; | ||
121 | dev_err(dev->devc, | ||
122 | "waiting for ishtp start failed. ret=%d hbm_state=%08X\n", | ||
123 | ret, dev->hbm_state); | ||
124 | return -ETIMEDOUT; | ||
125 | } | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * ishtp_hbm_start_req() - Send HBM start message | ||
131 | * @dev: ISHTP device instance | ||
132 | * | ||
133 | * Send HBM start message to firmware | ||
134 | * | ||
135 | * Return: 0 if success else error code | ||
136 | */ | ||
137 | int ishtp_hbm_start_req(struct ishtp_device *dev) | ||
138 | { | ||
139 | struct ishtp_msg_hdr hdr; | ||
140 | unsigned char data[128]; | ||
141 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
142 | struct hbm_host_version_request *start_req; | ||
143 | const size_t len = sizeof(struct hbm_host_version_request); | ||
144 | |||
145 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
146 | |||
147 | /* host start message */ | ||
148 | start_req = (struct hbm_host_version_request *)data; | ||
149 | memset(start_req, 0, len); | ||
150 | start_req->hbm_cmd = HOST_START_REQ_CMD; | ||
151 | start_req->host_version.major_version = HBM_MAJOR_VERSION; | ||
152 | start_req->host_version.minor_version = HBM_MINOR_VERSION; | ||
153 | |||
154 | /* | ||
155 | * (!) Response to HBM start may be so quick that this thread would get | ||
156 | * preempted BEFORE managing to set hbm_state = ISHTP_HBM_START. | ||
157 | * So set it at first, change back to ISHTP_HBM_IDLE upon failure | ||
158 | */ | ||
159 | dev->hbm_state = ISHTP_HBM_START; | ||
160 | if (ishtp_write_message(dev, ishtp_hdr, data)) { | ||
161 | dev_err(dev->devc, "version message send failed\n"); | ||
162 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
163 | dev->hbm_state = ISHTP_HBM_IDLE; | ||
164 | ish_hw_reset(dev); | ||
165 | return -ENODEV; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * ishtp_hbm_enum_clients_req() - Send client enum req | ||
173 | * @dev: ISHTP device instance | ||
174 | * | ||
175 | * Send enumeration client request message | ||
176 | * | ||
177 | * Return: 0 if success else error code | ||
178 | */ | ||
179 | void ishtp_hbm_enum_clients_req(struct ishtp_device *dev) | ||
180 | { | ||
181 | struct ishtp_msg_hdr hdr; | ||
182 | unsigned char data[128]; | ||
183 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
184 | struct hbm_host_enum_request *enum_req; | ||
185 | const size_t len = sizeof(struct hbm_host_enum_request); | ||
186 | |||
187 | /* enumerate clients */ | ||
188 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
189 | |||
190 | enum_req = (struct hbm_host_enum_request *)data; | ||
191 | memset(enum_req, 0, len); | ||
192 | enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; | ||
193 | |||
194 | if (ishtp_write_message(dev, ishtp_hdr, data)) { | ||
195 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
196 | dev_err(dev->devc, "enumeration request send failed\n"); | ||
197 | ish_hw_reset(dev); | ||
198 | } | ||
199 | dev->hbm_state = ISHTP_HBM_ENUM_CLIENTS; | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * ishtp_hbm_prop_req() - Request property | ||
204 | * @dev: ISHTP device instance | ||
205 | * | ||
206 | * Request property for a single client | ||
207 | * | ||
208 | * Return: 0 if success else error code | ||
209 | */ | ||
210 | static int ishtp_hbm_prop_req(struct ishtp_device *dev) | ||
211 | { | ||
212 | |||
213 | struct ishtp_msg_hdr hdr; | ||
214 | unsigned char data[128]; | ||
215 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
216 | struct hbm_props_request *prop_req; | ||
217 | const size_t len = sizeof(struct hbm_props_request); | ||
218 | unsigned long next_client_index; | ||
219 | uint8_t client_num; | ||
220 | |||
221 | client_num = dev->fw_client_presentation_num; | ||
222 | |||
223 | next_client_index = find_next_bit(dev->fw_clients_map, | ||
224 | ISHTP_CLIENTS_MAX, dev->fw_client_index); | ||
225 | |||
226 | /* We got all client properties */ | ||
227 | if (next_client_index == ISHTP_CLIENTS_MAX) { | ||
228 | dev->hbm_state = ISHTP_HBM_WORKING; | ||
229 | dev->dev_state = ISHTP_DEV_ENABLED; | ||
230 | |||
231 | for (dev->fw_client_presentation_num = 1; | ||
232 | dev->fw_client_presentation_num < client_num + 1; | ||
233 | ++dev->fw_client_presentation_num) | ||
234 | /* Add new client device */ | ||
235 | ishtp_bus_new_client(dev); | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | dev->fw_clients[client_num].client_id = next_client_index; | ||
240 | |||
241 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
242 | prop_req = (struct hbm_props_request *)data; | ||
243 | |||
244 | memset(prop_req, 0, sizeof(struct hbm_props_request)); | ||
245 | |||
246 | prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; | ||
247 | prop_req->address = next_client_index; | ||
248 | |||
249 | if (ishtp_write_message(dev, ishtp_hdr, data)) { | ||
250 | dev->dev_state = ISHTP_DEV_RESETTING; | ||
251 | dev_err(dev->devc, "properties request send failed\n"); | ||
252 | ish_hw_reset(dev); | ||
253 | return -EIO; | ||
254 | } | ||
255 | |||
256 | dev->fw_client_index = next_client_index; | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * ishtp_hbm_stop_req() - Send HBM stop | ||
263 | * @dev: ISHTP device instance | ||
264 | * | ||
265 | * Send stop request message | ||
266 | */ | ||
267 | static void ishtp_hbm_stop_req(struct ishtp_device *dev) | ||
268 | { | ||
269 | struct ishtp_msg_hdr hdr; | ||
270 | unsigned char data[128]; | ||
271 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
272 | struct hbm_host_stop_request *req; | ||
273 | const size_t len = sizeof(struct hbm_host_stop_request); | ||
274 | |||
275 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
276 | req = (struct hbm_host_stop_request *)data; | ||
277 | |||
278 | memset(req, 0, sizeof(struct hbm_host_stop_request)); | ||
279 | req->hbm_cmd = HOST_STOP_REQ_CMD; | ||
280 | req->reason = DRIVER_STOP_REQUEST; | ||
281 | |||
282 | ishtp_write_message(dev, ishtp_hdr, data); | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * ishtp_hbm_cl_flow_control_req() - Send flow control request | ||
287 | * @dev: ISHTP device instance | ||
288 | * @cl: ISHTP client instance | ||
289 | * | ||
290 | * Send flow control request | ||
291 | * | ||
292 | * Return: 0 if success else error code | ||
293 | */ | ||
294 | int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, | ||
295 | struct ishtp_cl *cl) | ||
296 | { | ||
297 | struct ishtp_msg_hdr hdr; | ||
298 | unsigned char data[128]; | ||
299 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
300 | const size_t len = sizeof(struct hbm_flow_control); | ||
301 | int rv; | ||
302 | unsigned int num_frags; | ||
303 | unsigned long flags; | ||
304 | |||
305 | spin_lock_irqsave(&cl->fc_spinlock, flags); | ||
306 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
307 | ishtp_hbm_cl_hdr(cl, ISHTP_FLOW_CONTROL_CMD, data, len); | ||
308 | |||
309 | /* | ||
310 | * Sync possible race when RB recycle and packet receive paths | ||
311 | * both try to send an out FC | ||
312 | */ | ||
313 | if (cl->out_flow_ctrl_creds) { | ||
314 | spin_unlock_irqrestore(&cl->fc_spinlock, flags); | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | num_frags = cl->recv_msg_num_frags; | ||
319 | cl->recv_msg_num_frags = 0; | ||
320 | |||
321 | rv = ishtp_write_message(dev, ishtp_hdr, data); | ||
322 | if (!rv) { | ||
323 | ++cl->out_flow_ctrl_creds; | ||
324 | ++cl->out_flow_ctrl_cnt; | ||
325 | getnstimeofday(&cl->ts_out_fc); | ||
326 | if (cl->ts_rx.tv_sec && cl->ts_rx.tv_nsec) { | ||
327 | struct timespec ts_diff; | ||
328 | |||
329 | ts_diff = timespec_sub(cl->ts_out_fc, cl->ts_rx); | ||
330 | if (timespec_compare(&ts_diff, &cl->ts_max_fc_delay) | ||
331 | > 0) | ||
332 | cl->ts_max_fc_delay = ts_diff; | ||
333 | } | ||
334 | } else { | ||
335 | ++cl->err_send_fc; | ||
336 | } | ||
337 | |||
338 | spin_unlock_irqrestore(&cl->fc_spinlock, flags); | ||
339 | return rv; | ||
340 | } | ||
341 | |||
342 | /** | ||
343 | * ishtp_hbm_cl_disconnect_req() - Send disconnect request | ||
344 | * @dev: ISHTP device instance | ||
345 | * @cl: ISHTP client instance | ||
346 | * | ||
347 | * Send disconnect message to fw | ||
348 | * | ||
349 | * Return: 0 if success else error code | ||
350 | */ | ||
351 | int ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl) | ||
352 | { | ||
353 | struct ishtp_msg_hdr hdr; | ||
354 | unsigned char data[128]; | ||
355 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
356 | const size_t len = sizeof(struct hbm_client_connect_request); | ||
357 | |||
358 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
359 | ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, data, len); | ||
360 | |||
361 | return ishtp_write_message(dev, ishtp_hdr, data); | ||
362 | } | ||
363 | |||
364 | /** | ||
365 | * ishtp_hbm_cl_disconnect_res() - Get disconnect response | ||
366 | * @dev: ISHTP device instance | ||
367 | * @rs: Response message | ||
368 | * | ||
369 | * Received disconnect response from fw | ||
370 | */ | ||
371 | static void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev, | ||
372 | struct hbm_client_connect_response *rs) | ||
373 | { | ||
374 | struct ishtp_cl *cl = NULL; | ||
375 | unsigned long flags; | ||
376 | |||
377 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
378 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
379 | if (!rs->status && ishtp_hbm_cl_addr_equal(cl, rs)) { | ||
380 | cl->state = ISHTP_CL_DISCONNECTED; | ||
381 | break; | ||
382 | } | ||
383 | } | ||
384 | if (cl) | ||
385 | wake_up_interruptible(&cl->wait_ctrl_res); | ||
386 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * ishtp_hbm_cl_connect_req() - Send connect request | ||
391 | * @dev: ISHTP device instance | ||
392 | * @cl: client device instance | ||
393 | * | ||
394 | * Send connection request to specific fw client | ||
395 | * | ||
396 | * Return: 0 if success else error code | ||
397 | */ | ||
398 | int ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl) | ||
399 | { | ||
400 | struct ishtp_msg_hdr hdr; | ||
401 | unsigned char data[128]; | ||
402 | struct ishtp_msg_hdr *ishtp_hdr = &hdr; | ||
403 | const size_t len = sizeof(struct hbm_client_connect_request); | ||
404 | |||
405 | ishtp_hbm_hdr(ishtp_hdr, len); | ||
406 | ishtp_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, data, len); | ||
407 | |||
408 | return ishtp_write_message(dev, ishtp_hdr, data); | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * ishtp_hbm_cl_connect_res() - Get connect response | ||
413 | * @dev: ISHTP device instance | ||
414 | * @rs: Response message | ||
415 | * | ||
416 | * Received connect response from fw | ||
417 | */ | ||
418 | static void ishtp_hbm_cl_connect_res(struct ishtp_device *dev, | ||
419 | struct hbm_client_connect_response *rs) | ||
420 | { | ||
421 | struct ishtp_cl *cl = NULL; | ||
422 | unsigned long flags; | ||
423 | |||
424 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
425 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
426 | if (ishtp_hbm_cl_addr_equal(cl, rs)) { | ||
427 | if (!rs->status) { | ||
428 | cl->state = ISHTP_CL_CONNECTED; | ||
429 | cl->status = 0; | ||
430 | } else { | ||
431 | cl->state = ISHTP_CL_DISCONNECTED; | ||
432 | cl->status = -ENODEV; | ||
433 | } | ||
434 | break; | ||
435 | } | ||
436 | } | ||
437 | if (cl) | ||
438 | wake_up_interruptible(&cl->wait_ctrl_res); | ||
439 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
440 | } | ||
441 | |||
442 | /** | ||
443 | * ishtp_client_disconnect_request() - Receive disconnect request | ||
444 | * @dev: ISHTP device instance | ||
445 | * @disconnect_req: disconnect request structure | ||
446 | * | ||
447 | * Disconnect request bus message from the fw. Send diconnect response. | ||
448 | */ | ||
449 | static void ishtp_hbm_fw_disconnect_req(struct ishtp_device *dev, | ||
450 | struct hbm_client_connect_request *disconnect_req) | ||
451 | { | ||
452 | struct ishtp_cl *cl; | ||
453 | const size_t len = sizeof(struct hbm_client_connect_response); | ||
454 | unsigned long flags; | ||
455 | struct ishtp_msg_hdr hdr; | ||
456 | unsigned char data[4]; /* All HBM messages are 4 bytes */ | ||
457 | |||
458 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
459 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
460 | if (ishtp_hbm_cl_addr_equal(cl, disconnect_req)) { | ||
461 | cl->state = ISHTP_CL_DISCONNECTED; | ||
462 | |||
463 | /* send disconnect response */ | ||
464 | ishtp_hbm_hdr(&hdr, len); | ||
465 | ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD, data, | ||
466 | len); | ||
467 | ishtp_write_message(dev, &hdr, data); | ||
468 | break; | ||
469 | } | ||
470 | } | ||
471 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
472 | } | ||
473 | |||
474 | /** | ||
475 | * ishtp_hbm_dma_xfer_ack(() - Receive transfer ACK | ||
476 | * @dev: ISHTP device instance | ||
477 | * @dma_xfer: HBM transfer message | ||
478 | * | ||
479 | * Receive ack for ISHTP-over-DMA client message | ||
480 | */ | ||
481 | static void ishtp_hbm_dma_xfer_ack(struct ishtp_device *dev, | ||
482 | struct dma_xfer_hbm *dma_xfer) | ||
483 | { | ||
484 | void *msg; | ||
485 | uint64_t offs; | ||
486 | struct ishtp_msg_hdr *ishtp_hdr = | ||
487 | (struct ishtp_msg_hdr *)&dev->ishtp_msg_hdr; | ||
488 | unsigned int msg_offs; | ||
489 | struct ishtp_cl *cl; | ||
490 | |||
491 | for (msg_offs = 0; msg_offs < ishtp_hdr->length; | ||
492 | msg_offs += sizeof(struct dma_xfer_hbm)) { | ||
493 | offs = dma_xfer->msg_addr - dev->ishtp_host_dma_tx_buf_phys; | ||
494 | if (offs > dev->ishtp_host_dma_tx_buf_size) { | ||
495 | dev_err(dev->devc, "Bad DMA Tx ack message address\n"); | ||
496 | return; | ||
497 | } | ||
498 | if (dma_xfer->msg_length > | ||
499 | dev->ishtp_host_dma_tx_buf_size - offs) { | ||
500 | dev_err(dev->devc, "Bad DMA Tx ack message size\n"); | ||
501 | return; | ||
502 | } | ||
503 | |||
504 | /* logical address of the acked mem */ | ||
505 | msg = (unsigned char *)dev->ishtp_host_dma_tx_buf + offs; | ||
506 | ishtp_cl_release_dma_acked_mem(dev, msg, dma_xfer->msg_length); | ||
507 | |||
508 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
509 | if (cl->fw_client_id == dma_xfer->fw_client_id && | ||
510 | cl->host_client_id == dma_xfer->host_client_id) | ||
511 | /* | ||
512 | * in case that a single ack may be sent | ||
513 | * over several dma transfers, and the last msg | ||
514 | * addr was inside the acked memory, but not in | ||
515 | * its start | ||
516 | */ | ||
517 | if (cl->last_dma_addr >= | ||
518 | (unsigned char *)msg && | ||
519 | cl->last_dma_addr < | ||
520 | (unsigned char *)msg + | ||
521 | dma_xfer->msg_length) { | ||
522 | cl->last_dma_acked = 1; | ||
523 | |||
524 | if (!list_empty(&cl->tx_list.list) && | ||
525 | cl->ishtp_flow_ctrl_creds) { | ||
526 | /* | ||
527 | * start sending the first msg | ||
528 | */ | ||
529 | ishtp_cl_send_msg(dev, cl); | ||
530 | } | ||
531 | } | ||
532 | } | ||
533 | ++dma_xfer; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | /** | ||
538 | * ishtp_hbm_dma_xfer() - Receive DMA transfer message | ||
539 | * @dev: ISHTP device instance | ||
540 | * @dma_xfer: HBM transfer message | ||
541 | * | ||
542 | * Receive ISHTP-over-DMA client message | ||
543 | */ | ||
544 | static void ishtp_hbm_dma_xfer(struct ishtp_device *dev, | ||
545 | struct dma_xfer_hbm *dma_xfer) | ||
546 | { | ||
547 | void *msg; | ||
548 | uint64_t offs; | ||
549 | struct ishtp_msg_hdr hdr; | ||
550 | struct ishtp_msg_hdr *ishtp_hdr = | ||
551 | (struct ishtp_msg_hdr *) &dev->ishtp_msg_hdr; | ||
552 | struct dma_xfer_hbm *prm = dma_xfer; | ||
553 | unsigned int msg_offs; | ||
554 | |||
555 | for (msg_offs = 0; msg_offs < ishtp_hdr->length; | ||
556 | msg_offs += sizeof(struct dma_xfer_hbm)) { | ||
557 | |||
558 | offs = dma_xfer->msg_addr - dev->ishtp_host_dma_rx_buf_phys; | ||
559 | if (offs > dev->ishtp_host_dma_rx_buf_size) { | ||
560 | dev_err(dev->devc, "Bad DMA Rx message address\n"); | ||
561 | return; | ||
562 | } | ||
563 | if (dma_xfer->msg_length > | ||
564 | dev->ishtp_host_dma_rx_buf_size - offs) { | ||
565 | dev_err(dev->devc, "Bad DMA Rx message size\n"); | ||
566 | return; | ||
567 | } | ||
568 | msg = dev->ishtp_host_dma_rx_buf + offs; | ||
569 | recv_ishtp_cl_msg_dma(dev, msg, dma_xfer); | ||
570 | dma_xfer->hbm = DMA_XFER_ACK; /* Prepare for response */ | ||
571 | ++dma_xfer; | ||
572 | } | ||
573 | |||
574 | /* Send DMA_XFER_ACK [...] */ | ||
575 | ishtp_hbm_hdr(&hdr, ishtp_hdr->length); | ||
576 | ishtp_write_message(dev, &hdr, (unsigned char *)prm); | ||
577 | } | ||
578 | |||
579 | /** | ||
580 | * ishtp_hbm_dispatch() - HBM dispatch function | ||
581 | * @dev: ISHTP device instance | ||
582 | * @hdr: bus message | ||
583 | * | ||
584 | * Bottom half read routine after ISR to handle the read bus message cmd | ||
585 | * processing | ||
586 | */ | ||
587 | void ishtp_hbm_dispatch(struct ishtp_device *dev, | ||
588 | struct ishtp_bus_message *hdr) | ||
589 | { | ||
590 | struct ishtp_bus_message *ishtp_msg; | ||
591 | struct ishtp_fw_client *fw_client; | ||
592 | struct hbm_host_version_response *version_res; | ||
593 | struct hbm_client_connect_response *connect_res; | ||
594 | struct hbm_client_connect_response *disconnect_res; | ||
595 | struct hbm_client_connect_request *disconnect_req; | ||
596 | struct hbm_props_response *props_res; | ||
597 | struct hbm_host_enum_response *enum_res; | ||
598 | struct ishtp_msg_hdr ishtp_hdr; | ||
599 | struct dma_alloc_notify dma_alloc_notify; | ||
600 | struct dma_xfer_hbm *dma_xfer; | ||
601 | |||
602 | ishtp_msg = hdr; | ||
603 | |||
604 | switch (ishtp_msg->hbm_cmd) { | ||
605 | case HOST_START_RES_CMD: | ||
606 | version_res = (struct hbm_host_version_response *)ishtp_msg; | ||
607 | if (!version_res->host_version_supported) { | ||
608 | dev->version = version_res->fw_max_version; | ||
609 | |||
610 | dev->hbm_state = ISHTP_HBM_STOPPED; | ||
611 | ishtp_hbm_stop_req(dev); | ||
612 | return; | ||
613 | } | ||
614 | |||
615 | dev->version.major_version = HBM_MAJOR_VERSION; | ||
616 | dev->version.minor_version = HBM_MINOR_VERSION; | ||
617 | if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS && | ||
618 | dev->hbm_state == ISHTP_HBM_START) { | ||
619 | dev->hbm_state = ISHTP_HBM_STARTED; | ||
620 | ishtp_hbm_enum_clients_req(dev); | ||
621 | } else { | ||
622 | dev_err(dev->devc, | ||
623 | "reset: wrong host start response\n"); | ||
624 | /* BUG: why do we arrive here? */ | ||
625 | ish_hw_reset(dev); | ||
626 | return; | ||
627 | } | ||
628 | |||
629 | wake_up_interruptible(&dev->wait_hbm_recvd_msg); | ||
630 | break; | ||
631 | |||
632 | case CLIENT_CONNECT_RES_CMD: | ||
633 | connect_res = (struct hbm_client_connect_response *)ishtp_msg; | ||
634 | ishtp_hbm_cl_connect_res(dev, connect_res); | ||
635 | break; | ||
636 | |||
637 | case CLIENT_DISCONNECT_RES_CMD: | ||
638 | disconnect_res = | ||
639 | (struct hbm_client_connect_response *)ishtp_msg; | ||
640 | ishtp_hbm_cl_disconnect_res(dev, disconnect_res); | ||
641 | break; | ||
642 | |||
643 | case HOST_CLIENT_PROPERTIES_RES_CMD: | ||
644 | props_res = (struct hbm_props_response *)ishtp_msg; | ||
645 | fw_client = &dev->fw_clients[dev->fw_client_presentation_num]; | ||
646 | |||
647 | if (props_res->status || !dev->fw_clients) { | ||
648 | dev_err(dev->devc, | ||
649 | "reset: properties response hbm wrong status\n"); | ||
650 | ish_hw_reset(dev); | ||
651 | return; | ||
652 | } | ||
653 | |||
654 | if (fw_client->client_id != props_res->address) { | ||
655 | dev_err(dev->devc, | ||
656 | "reset: host properties response address mismatch [%02X %02X]\n", | ||
657 | fw_client->client_id, props_res->address); | ||
658 | ish_hw_reset(dev); | ||
659 | return; | ||
660 | } | ||
661 | |||
662 | if (dev->dev_state != ISHTP_DEV_INIT_CLIENTS || | ||
663 | dev->hbm_state != ISHTP_HBM_CLIENT_PROPERTIES) { | ||
664 | dev_err(dev->devc, | ||
665 | "reset: unexpected properties response\n"); | ||
666 | ish_hw_reset(dev); | ||
667 | return; | ||
668 | } | ||
669 | |||
670 | fw_client->props = props_res->client_properties; | ||
671 | dev->fw_client_index++; | ||
672 | dev->fw_client_presentation_num++; | ||
673 | |||
674 | /* request property for the next client */ | ||
675 | ishtp_hbm_prop_req(dev); | ||
676 | |||
677 | if (dev->dev_state != ISHTP_DEV_ENABLED) | ||
678 | break; | ||
679 | |||
680 | if (!ishtp_use_dma_transfer()) | ||
681 | break; | ||
682 | |||
683 | dev_dbg(dev->devc, "Requesting to use DMA\n"); | ||
684 | ishtp_cl_alloc_dma_buf(dev); | ||
685 | if (dev->ishtp_host_dma_rx_buf) { | ||
686 | const size_t len = sizeof(dma_alloc_notify); | ||
687 | |||
688 | memset(&dma_alloc_notify, 0, sizeof(dma_alloc_notify)); | ||
689 | dma_alloc_notify.hbm = DMA_BUFFER_ALLOC_NOTIFY; | ||
690 | dma_alloc_notify.buf_size = | ||
691 | dev->ishtp_host_dma_rx_buf_size; | ||
692 | dma_alloc_notify.buf_address = | ||
693 | dev->ishtp_host_dma_rx_buf_phys; | ||
694 | ishtp_hbm_hdr(&ishtp_hdr, len); | ||
695 | ishtp_write_message(dev, &ishtp_hdr, | ||
696 | (unsigned char *)&dma_alloc_notify); | ||
697 | } | ||
698 | |||
699 | break; | ||
700 | |||
701 | case HOST_ENUM_RES_CMD: | ||
702 | enum_res = (struct hbm_host_enum_response *) ishtp_msg; | ||
703 | memcpy(dev->fw_clients_map, enum_res->valid_addresses, 32); | ||
704 | if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS && | ||
705 | dev->hbm_state == ISHTP_HBM_ENUM_CLIENTS) { | ||
706 | dev->fw_client_presentation_num = 0; | ||
707 | dev->fw_client_index = 0; | ||
708 | |||
709 | ishtp_hbm_fw_cl_allocate(dev); | ||
710 | dev->hbm_state = ISHTP_HBM_CLIENT_PROPERTIES; | ||
711 | |||
712 | /* first property request */ | ||
713 | ishtp_hbm_prop_req(dev); | ||
714 | } else { | ||
715 | dev_err(dev->devc, | ||
716 | "reset: unexpected enumeration response hbm\n"); | ||
717 | ish_hw_reset(dev); | ||
718 | return; | ||
719 | } | ||
720 | break; | ||
721 | |||
722 | case HOST_STOP_RES_CMD: | ||
723 | if (dev->hbm_state != ISHTP_HBM_STOPPED) | ||
724 | dev_err(dev->devc, "unexpected stop response\n"); | ||
725 | |||
726 | dev->dev_state = ISHTP_DEV_DISABLED; | ||
727 | dev_info(dev->devc, "reset: FW stop response\n"); | ||
728 | ish_hw_reset(dev); | ||
729 | break; | ||
730 | |||
731 | case CLIENT_DISCONNECT_REQ_CMD: | ||
732 | /* search for client */ | ||
733 | disconnect_req = | ||
734 | (struct hbm_client_connect_request *)ishtp_msg; | ||
735 | ishtp_hbm_fw_disconnect_req(dev, disconnect_req); | ||
736 | break; | ||
737 | |||
738 | case FW_STOP_REQ_CMD: | ||
739 | dev->hbm_state = ISHTP_HBM_STOPPED; | ||
740 | break; | ||
741 | |||
742 | case DMA_BUFFER_ALLOC_RESPONSE: | ||
743 | dev->ishtp_host_dma_enabled = 1; | ||
744 | break; | ||
745 | |||
746 | case DMA_XFER: | ||
747 | dma_xfer = (struct dma_xfer_hbm *)ishtp_msg; | ||
748 | if (!dev->ishtp_host_dma_enabled) { | ||
749 | dev_err(dev->devc, | ||
750 | "DMA XFER requested but DMA is not enabled\n"); | ||
751 | break; | ||
752 | } | ||
753 | ishtp_hbm_dma_xfer(dev, dma_xfer); | ||
754 | break; | ||
755 | |||
756 | case DMA_XFER_ACK: | ||
757 | dma_xfer = (struct dma_xfer_hbm *)ishtp_msg; | ||
758 | if (!dev->ishtp_host_dma_enabled || | ||
759 | !dev->ishtp_host_dma_tx_buf) { | ||
760 | dev_err(dev->devc, | ||
761 | "DMA XFER acked but DMA Tx is not enabled\n"); | ||
762 | break; | ||
763 | } | ||
764 | ishtp_hbm_dma_xfer_ack(dev, dma_xfer); | ||
765 | break; | ||
766 | |||
767 | default: | ||
768 | dev_err(dev->devc, "unknown HBM: %u\n", | ||
769 | (unsigned int)ishtp_msg->hbm_cmd); | ||
770 | |||
771 | break; | ||
772 | } | ||
773 | } | ||
774 | |||
775 | /** | ||
776 | * bh_hbm_work_fn() - HBM work function | ||
777 | * @work: work struct | ||
778 | * | ||
779 | * Bottom half processing work function (instead of thread handler) | ||
780 | * for processing hbm messages | ||
781 | */ | ||
782 | void bh_hbm_work_fn(struct work_struct *work) | ||
783 | { | ||
784 | unsigned long flags; | ||
785 | struct ishtp_device *dev; | ||
786 | unsigned char hbm[IPC_PAYLOAD_SIZE]; | ||
787 | |||
788 | dev = container_of(work, struct ishtp_device, bh_hbm_work); | ||
789 | spin_lock_irqsave(&dev->rd_msg_spinlock, flags); | ||
790 | if (dev->rd_msg_fifo_head != dev->rd_msg_fifo_tail) { | ||
791 | memcpy(hbm, dev->rd_msg_fifo + dev->rd_msg_fifo_head, | ||
792 | IPC_PAYLOAD_SIZE); | ||
793 | dev->rd_msg_fifo_head = | ||
794 | (dev->rd_msg_fifo_head + IPC_PAYLOAD_SIZE) % | ||
795 | (RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE); | ||
796 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
797 | ishtp_hbm_dispatch(dev, (struct ishtp_bus_message *)hbm); | ||
798 | } else { | ||
799 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
800 | } | ||
801 | } | ||
802 | |||
803 | /** | ||
804 | * recv_hbm() - Receive HBM message | ||
805 | * @dev: ISHTP device instance | ||
806 | * @ishtp_hdr: received bus message | ||
807 | * | ||
808 | * Receive and process ISHTP bus messages in ISR context. This will schedule | ||
809 | * work function to process message | ||
810 | */ | ||
811 | void recv_hbm(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr) | ||
812 | { | ||
813 | uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; | ||
814 | struct ishtp_bus_message *ishtp_msg = | ||
815 | (struct ishtp_bus_message *)rd_msg_buf; | ||
816 | unsigned long flags; | ||
817 | |||
818 | dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length); | ||
819 | |||
820 | /* Flow control - handle in place */ | ||
821 | if (ishtp_msg->hbm_cmd == ISHTP_FLOW_CONTROL_CMD) { | ||
822 | struct hbm_flow_control *flow_control = | ||
823 | (struct hbm_flow_control *)ishtp_msg; | ||
824 | struct ishtp_cl *cl = NULL; | ||
825 | unsigned long flags, tx_flags; | ||
826 | |||
827 | spin_lock_irqsave(&dev->cl_list_lock, flags); | ||
828 | list_for_each_entry(cl, &dev->cl_list, link) { | ||
829 | if (cl->host_client_id == flow_control->host_addr && | ||
830 | cl->fw_client_id == | ||
831 | flow_control->fw_addr) { | ||
832 | /* | ||
833 | * NOTE: It's valid only for counting | ||
834 | * flow-control implementation to receive a | ||
835 | * FC in the middle of sending. Meanwhile not | ||
836 | * supported | ||
837 | */ | ||
838 | if (cl->ishtp_flow_ctrl_creds) | ||
839 | dev_err(dev->devc, | ||
840 | "recv extra FC from FW client %u (host client %u) (FC count was %d)\n", | ||
841 | (unsigned int)cl->fw_client_id, | ||
842 | (unsigned int)cl->host_client_id, | ||
843 | cl->ishtp_flow_ctrl_creds); | ||
844 | else { | ||
845 | ++cl->ishtp_flow_ctrl_creds; | ||
846 | ++cl->ishtp_flow_ctrl_cnt; | ||
847 | cl->last_ipc_acked = 1; | ||
848 | spin_lock_irqsave( | ||
849 | &cl->tx_list_spinlock, | ||
850 | tx_flags); | ||
851 | if (!list_empty(&cl->tx_list.list)) { | ||
852 | /* | ||
853 | * start sending the first msg | ||
854 | * = the callback function | ||
855 | */ | ||
856 | spin_unlock_irqrestore( | ||
857 | &cl->tx_list_spinlock, | ||
858 | tx_flags); | ||
859 | ishtp_cl_send_msg(dev, cl); | ||
860 | } else { | ||
861 | spin_unlock_irqrestore( | ||
862 | &cl->tx_list_spinlock, | ||
863 | tx_flags); | ||
864 | } | ||
865 | } | ||
866 | break; | ||
867 | } | ||
868 | } | ||
869 | spin_unlock_irqrestore(&dev->cl_list_lock, flags); | ||
870 | goto eoi; | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * Some messages that are safe for ISR processing and important | ||
875 | * to be done "quickly" and in-order, go here | ||
876 | */ | ||
877 | if (ishtp_msg->hbm_cmd == CLIENT_CONNECT_RES_CMD || | ||
878 | ishtp_msg->hbm_cmd == CLIENT_DISCONNECT_RES_CMD || | ||
879 | ishtp_msg->hbm_cmd == CLIENT_DISCONNECT_REQ_CMD || | ||
880 | ishtp_msg->hbm_cmd == DMA_XFER) { | ||
881 | ishtp_hbm_dispatch(dev, ishtp_msg); | ||
882 | goto eoi; | ||
883 | } | ||
884 | |||
885 | /* | ||
886 | * All other HBMs go here. | ||
887 | * We schedule HBMs for processing serially by using system wq, | ||
888 | * possibly there will be multiple HBMs scheduled at the same time. | ||
889 | */ | ||
890 | spin_lock_irqsave(&dev->rd_msg_spinlock, flags); | ||
891 | if ((dev->rd_msg_fifo_tail + IPC_PAYLOAD_SIZE) % | ||
892 | (RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE) == | ||
893 | dev->rd_msg_fifo_head) { | ||
894 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
895 | dev_err(dev->devc, "BH buffer overflow, dropping HBM %u\n", | ||
896 | (unsigned int)ishtp_msg->hbm_cmd); | ||
897 | goto eoi; | ||
898 | } | ||
899 | memcpy(dev->rd_msg_fifo + dev->rd_msg_fifo_tail, ishtp_msg, | ||
900 | ishtp_hdr->length); | ||
901 | dev->rd_msg_fifo_tail = (dev->rd_msg_fifo_tail + IPC_PAYLOAD_SIZE) % | ||
902 | (RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE); | ||
903 | spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags); | ||
904 | schedule_work(&dev->bh_hbm_work); | ||
905 | eoi: | ||
906 | return; | ||
907 | } | ||
908 | |||
909 | /** | ||
910 | * recv_fixed_cl_msg() - Receive fixed client message | ||
911 | * @dev: ISHTP device instance | ||
912 | * @ishtp_hdr: received bus message | ||
913 | * | ||
914 | * Receive and process ISHTP fixed client messages (address == 0) | ||
915 | * in ISR context | ||
916 | */ | ||
917 | void recv_fixed_cl_msg(struct ishtp_device *dev, | ||
918 | struct ishtp_msg_hdr *ishtp_hdr) | ||
919 | { | ||
920 | uint8_t rd_msg_buf[ISHTP_RD_MSG_BUF_SIZE]; | ||
921 | |||
922 | dev->print_log(dev, | ||
923 | "%s() got fixed client msg from client #%d\n", | ||
924 | __func__, ishtp_hdr->fw_addr); | ||
925 | dev->ops->ishtp_read(dev, rd_msg_buf, ishtp_hdr->length); | ||
926 | if (ishtp_hdr->fw_addr == ISHTP_SYSTEM_STATE_CLIENT_ADDR) { | ||
927 | struct ish_system_states_header *msg_hdr = | ||
928 | (struct ish_system_states_header *)rd_msg_buf; | ||
929 | if (msg_hdr->cmd == SYSTEM_STATE_SUBSCRIBE) | ||
930 | ishtp_send_resume(dev); | ||
931 | /* if FW request arrived here, the system is not suspended */ | ||
932 | else | ||
933 | dev_err(dev->devc, "unknown fixed client msg [%02X]\n", | ||
934 | msg_hdr->cmd); | ||
935 | } | ||
936 | } | ||
937 | |||
938 | /** | ||
939 | * fix_cl_hdr() - Initialize fixed client header | ||
940 | * @hdr: message header | ||
941 | * @length: length of message | ||
942 | * @cl_addr: Client address | ||
943 | * | ||
944 | * Initialize message header for fixed client | ||
945 | */ | ||
946 | static inline void fix_cl_hdr(struct ishtp_msg_hdr *hdr, size_t length, | ||
947 | uint8_t cl_addr) | ||
948 | { | ||
949 | hdr->host_addr = 0; | ||
950 | hdr->fw_addr = cl_addr; | ||
951 | hdr->length = length; | ||
952 | hdr->msg_complete = 1; | ||
953 | hdr->reserved = 0; | ||
954 | } | ||
955 | |||
956 | /*** Suspend and resume notification ***/ | ||
957 | |||
958 | static uint32_t current_state; | ||
959 | static uint32_t supported_states = 0 | SUSPEND_STATE_BIT; | ||
960 | |||
961 | /** | ||
962 | * ishtp_send_suspend() - Send suspend message to FW | ||
963 | * @dev: ISHTP device instance | ||
964 | * | ||
965 | * Send suspend message to FW. This is useful for system freeze (non S3) case | ||
966 | */ | ||
967 | void ishtp_send_suspend(struct ishtp_device *dev) | ||
968 | { | ||
969 | struct ishtp_msg_hdr ishtp_hdr; | ||
970 | struct ish_system_states_status state_status_msg; | ||
971 | const size_t len = sizeof(struct ish_system_states_status); | ||
972 | |||
973 | fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR); | ||
974 | |||
975 | memset(&state_status_msg, 0, len); | ||
976 | state_status_msg.hdr.cmd = SYSTEM_STATE_STATUS; | ||
977 | state_status_msg.supported_states = supported_states; | ||
978 | current_state |= SUSPEND_STATE_BIT; | ||
979 | dev->print_log(dev, "%s() sends SUSPEND notification\n", __func__); | ||
980 | state_status_msg.states_status = current_state; | ||
981 | |||
982 | ishtp_write_message(dev, &ishtp_hdr, | ||
983 | (unsigned char *)&state_status_msg); | ||
984 | } | ||
985 | EXPORT_SYMBOL(ishtp_send_suspend); | ||
986 | |||
987 | /** | ||
988 | * ishtp_send_resume() - Send resume message to FW | ||
989 | * @dev: ISHTP device instance | ||
990 | * | ||
991 | * Send resume message to FW. This is useful for system freeze (non S3) case | ||
992 | */ | ||
993 | void ishtp_send_resume(struct ishtp_device *dev) | ||
994 | { | ||
995 | struct ishtp_msg_hdr ishtp_hdr; | ||
996 | struct ish_system_states_status state_status_msg; | ||
997 | const size_t len = sizeof(struct ish_system_states_status); | ||
998 | |||
999 | fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR); | ||
1000 | |||
1001 | memset(&state_status_msg, 0, len); | ||
1002 | state_status_msg.hdr.cmd = SYSTEM_STATE_STATUS; | ||
1003 | state_status_msg.supported_states = supported_states; | ||
1004 | current_state &= ~SUSPEND_STATE_BIT; | ||
1005 | dev->print_log(dev, "%s() sends RESUME notification\n", __func__); | ||
1006 | state_status_msg.states_status = current_state; | ||
1007 | |||
1008 | ishtp_write_message(dev, &ishtp_hdr, | ||
1009 | (unsigned char *)&state_status_msg); | ||
1010 | } | ||
1011 | EXPORT_SYMBOL(ishtp_send_resume); | ||
1012 | |||
1013 | /** | ||
1014 | * ishtp_query_subscribers() - Send query subscribers message | ||
1015 | * @dev: ISHTP device instance | ||
1016 | * | ||
1017 | * Send message to query subscribers | ||
1018 | */ | ||
1019 | void ishtp_query_subscribers(struct ishtp_device *dev) | ||
1020 | { | ||
1021 | struct ishtp_msg_hdr ishtp_hdr; | ||
1022 | struct ish_system_states_query_subscribers query_subscribers_msg; | ||
1023 | const size_t len = sizeof(struct ish_system_states_query_subscribers); | ||
1024 | |||
1025 | fix_cl_hdr(&ishtp_hdr, len, ISHTP_SYSTEM_STATE_CLIENT_ADDR); | ||
1026 | |||
1027 | memset(&query_subscribers_msg, 0, len); | ||
1028 | query_subscribers_msg.hdr.cmd = SYSTEM_STATE_QUERY_SUBSCRIBERS; | ||
1029 | |||
1030 | ishtp_write_message(dev, &ishtp_hdr, | ||
1031 | (unsigned char *)&query_subscribers_msg); | ||
1032 | } | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.h b/drivers/hid/intel-ish-hid/ishtp/hbm.h new file mode 100644 index 000000000000..d96111cef7f8 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.h | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * ISHTP bus layer messages handling | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #ifndef _ISHTP_HBM_H_ | ||
17 | #define _ISHTP_HBM_H_ | ||
18 | |||
19 | #include <linux/uuid.h> | ||
20 | |||
21 | struct ishtp_device; | ||
22 | struct ishtp_msg_hdr; | ||
23 | struct ishtp_cl; | ||
24 | |||
25 | /* | ||
26 | * Timeouts in Seconds | ||
27 | */ | ||
28 | #define ISHTP_INTEROP_TIMEOUT 7 /* Timeout on ready message */ | ||
29 | |||
30 | #define ISHTP_CL_CONNECT_TIMEOUT 15 /* HPS: Client Connect Timeout */ | ||
31 | |||
32 | /* | ||
33 | * ISHTP Version | ||
34 | */ | ||
35 | #define HBM_MINOR_VERSION 0 | ||
36 | #define HBM_MAJOR_VERSION 1 | ||
37 | |||
38 | /* Host bus message command opcode */ | ||
39 | #define ISHTP_HBM_CMD_OP_MSK 0x7f | ||
40 | /* Host bus message command RESPONSE */ | ||
41 | #define ISHTP_HBM_CMD_RES_MSK 0x80 | ||
42 | |||
43 | /* | ||
44 | * ISHTP Bus Message Command IDs | ||
45 | */ | ||
46 | #define HOST_START_REQ_CMD 0x01 | ||
47 | #define HOST_START_RES_CMD 0x81 | ||
48 | |||
49 | #define HOST_STOP_REQ_CMD 0x02 | ||
50 | #define HOST_STOP_RES_CMD 0x82 | ||
51 | |||
52 | #define FW_STOP_REQ_CMD 0x03 | ||
53 | |||
54 | #define HOST_ENUM_REQ_CMD 0x04 | ||
55 | #define HOST_ENUM_RES_CMD 0x84 | ||
56 | |||
57 | #define HOST_CLIENT_PROPERTIES_REQ_CMD 0x05 | ||
58 | #define HOST_CLIENT_PROPERTIES_RES_CMD 0x85 | ||
59 | |||
60 | #define CLIENT_CONNECT_REQ_CMD 0x06 | ||
61 | #define CLIENT_CONNECT_RES_CMD 0x86 | ||
62 | |||
63 | #define CLIENT_DISCONNECT_REQ_CMD 0x07 | ||
64 | #define CLIENT_DISCONNECT_RES_CMD 0x87 | ||
65 | |||
66 | #define ISHTP_FLOW_CONTROL_CMD 0x08 | ||
67 | |||
68 | #define DMA_BUFFER_ALLOC_NOTIFY 0x11 | ||
69 | #define DMA_BUFFER_ALLOC_RESPONSE 0x91 | ||
70 | |||
71 | #define DMA_XFER 0x12 | ||
72 | #define DMA_XFER_ACK 0x92 | ||
73 | |||
74 | /* | ||
75 | * ISHTP Stop Reason | ||
76 | * used by hbm_host_stop_request.reason | ||
77 | */ | ||
78 | #define DRIVER_STOP_REQUEST 0x00 | ||
79 | |||
80 | /* | ||
81 | * ISHTP BUS Interface Section | ||
82 | */ | ||
83 | struct ishtp_msg_hdr { | ||
84 | uint32_t fw_addr:8; | ||
85 | uint32_t host_addr:8; | ||
86 | uint32_t length:9; | ||
87 | uint32_t reserved:6; | ||
88 | uint32_t msg_complete:1; | ||
89 | } __packed; | ||
90 | |||
91 | struct ishtp_bus_message { | ||
92 | uint8_t hbm_cmd; | ||
93 | uint8_t data[0]; | ||
94 | } __packed; | ||
95 | |||
96 | /** | ||
97 | * struct hbm_cl_cmd - client specific host bus command | ||
98 | * CONNECT, DISCONNECT, and FlOW CONTROL | ||
99 | * | ||
100 | * @hbm_cmd - bus message command header | ||
101 | * @fw_addr - address of the fw client | ||
102 | * @host_addr - address of the client in the driver | ||
103 | * @data | ||
104 | */ | ||
105 | struct ishtp_hbm_cl_cmd { | ||
106 | uint8_t hbm_cmd; | ||
107 | uint8_t fw_addr; | ||
108 | uint8_t host_addr; | ||
109 | uint8_t data; | ||
110 | }; | ||
111 | |||
112 | struct hbm_version { | ||
113 | uint8_t minor_version; | ||
114 | uint8_t major_version; | ||
115 | } __packed; | ||
116 | |||
117 | struct hbm_host_version_request { | ||
118 | uint8_t hbm_cmd; | ||
119 | uint8_t reserved; | ||
120 | struct hbm_version host_version; | ||
121 | } __packed; | ||
122 | |||
123 | struct hbm_host_version_response { | ||
124 | uint8_t hbm_cmd; | ||
125 | uint8_t host_version_supported; | ||
126 | struct hbm_version fw_max_version; | ||
127 | } __packed; | ||
128 | |||
129 | struct hbm_host_stop_request { | ||
130 | uint8_t hbm_cmd; | ||
131 | uint8_t reason; | ||
132 | uint8_t reserved[2]; | ||
133 | } __packed; | ||
134 | |||
135 | struct hbm_host_stop_response { | ||
136 | uint8_t hbm_cmd; | ||
137 | uint8_t reserved[3]; | ||
138 | } __packed; | ||
139 | |||
140 | struct hbm_host_enum_request { | ||
141 | uint8_t hbm_cmd; | ||
142 | uint8_t reserved[3]; | ||
143 | } __packed; | ||
144 | |||
145 | struct hbm_host_enum_response { | ||
146 | uint8_t hbm_cmd; | ||
147 | uint8_t reserved[3]; | ||
148 | uint8_t valid_addresses[32]; | ||
149 | } __packed; | ||
150 | |||
151 | struct ishtp_client_properties { | ||
152 | uuid_le protocol_name; | ||
153 | uint8_t protocol_version; | ||
154 | uint8_t max_number_of_connections; | ||
155 | uint8_t fixed_address; | ||
156 | uint8_t single_recv_buf; | ||
157 | uint32_t max_msg_length; | ||
158 | uint8_t dma_hdr_len; | ||
159 | #define ISHTP_CLIENT_DMA_ENABLED 0x80 | ||
160 | uint8_t reserved4; | ||
161 | uint8_t reserved5; | ||
162 | uint8_t reserved6; | ||
163 | } __packed; | ||
164 | |||
165 | struct hbm_props_request { | ||
166 | uint8_t hbm_cmd; | ||
167 | uint8_t address; | ||
168 | uint8_t reserved[2]; | ||
169 | } __packed; | ||
170 | |||
171 | struct hbm_props_response { | ||
172 | uint8_t hbm_cmd; | ||
173 | uint8_t address; | ||
174 | uint8_t status; | ||
175 | uint8_t reserved[1]; | ||
176 | struct ishtp_client_properties client_properties; | ||
177 | } __packed; | ||
178 | |||
179 | /** | ||
180 | * struct hbm_client_connect_request - connect/disconnect request | ||
181 | * | ||
182 | * @hbm_cmd - bus message command header | ||
183 | * @fw_addr - address of the fw client | ||
184 | * @host_addr - address of the client in the driver | ||
185 | * @reserved | ||
186 | */ | ||
187 | struct hbm_client_connect_request { | ||
188 | uint8_t hbm_cmd; | ||
189 | uint8_t fw_addr; | ||
190 | uint8_t host_addr; | ||
191 | uint8_t reserved; | ||
192 | } __packed; | ||
193 | |||
194 | /** | ||
195 | * struct hbm_client_connect_response - connect/disconnect response | ||
196 | * | ||
197 | * @hbm_cmd - bus message command header | ||
198 | * @fw_addr - address of the fw client | ||
199 | * @host_addr - address of the client in the driver | ||
200 | * @status - status of the request | ||
201 | */ | ||
202 | struct hbm_client_connect_response { | ||
203 | uint8_t hbm_cmd; | ||
204 | uint8_t fw_addr; | ||
205 | uint8_t host_addr; | ||
206 | uint8_t status; | ||
207 | } __packed; | ||
208 | |||
209 | |||
210 | #define ISHTP_FC_MESSAGE_RESERVED_LENGTH 5 | ||
211 | |||
212 | struct hbm_flow_control { | ||
213 | uint8_t hbm_cmd; | ||
214 | uint8_t fw_addr; | ||
215 | uint8_t host_addr; | ||
216 | uint8_t reserved[ISHTP_FC_MESSAGE_RESERVED_LENGTH]; | ||
217 | } __packed; | ||
218 | |||
219 | struct dma_alloc_notify { | ||
220 | uint8_t hbm; | ||
221 | uint8_t status; | ||
222 | uint8_t reserved[2]; | ||
223 | uint32_t buf_size; | ||
224 | uint64_t buf_address; | ||
225 | /* [...] May come more size/address pairs */ | ||
226 | } __packed; | ||
227 | |||
228 | struct dma_xfer_hbm { | ||
229 | uint8_t hbm; | ||
230 | uint8_t fw_client_id; | ||
231 | uint8_t host_client_id; | ||
232 | uint8_t reserved; | ||
233 | uint64_t msg_addr; | ||
234 | uint32_t msg_length; | ||
235 | uint32_t reserved2; | ||
236 | } __packed; | ||
237 | |||
238 | /* System state */ | ||
239 | #define ISHTP_SYSTEM_STATE_CLIENT_ADDR 13 | ||
240 | |||
241 | #define SYSTEM_STATE_SUBSCRIBE 0x1 | ||
242 | #define SYSTEM_STATE_STATUS 0x2 | ||
243 | #define SYSTEM_STATE_QUERY_SUBSCRIBERS 0x3 | ||
244 | #define SYSTEM_STATE_STATE_CHANGE_REQ 0x4 | ||
245 | /*indicates suspend and resume states*/ | ||
246 | #define SUSPEND_STATE_BIT (1<<1) | ||
247 | |||
248 | struct ish_system_states_header { | ||
249 | uint32_t cmd; | ||
250 | uint32_t cmd_status; /*responses will have this set*/ | ||
251 | } __packed; | ||
252 | |||
253 | struct ish_system_states_subscribe { | ||
254 | struct ish_system_states_header hdr; | ||
255 | uint32_t states; | ||
256 | } __packed; | ||
257 | |||
258 | struct ish_system_states_status { | ||
259 | struct ish_system_states_header hdr; | ||
260 | uint32_t supported_states; | ||
261 | uint32_t states_status; | ||
262 | } __packed; | ||
263 | |||
264 | struct ish_system_states_query_subscribers { | ||
265 | struct ish_system_states_header hdr; | ||
266 | } __packed; | ||
267 | |||
268 | struct ish_system_states_state_change_req { | ||
269 | struct ish_system_states_header hdr; | ||
270 | uint32_t requested_states; | ||
271 | uint32_t states_status; | ||
272 | } __packed; | ||
273 | |||
274 | /** | ||
275 | * enum ishtp_hbm_state - host bus message protocol state | ||
276 | * | ||
277 | * @ISHTP_HBM_IDLE : protocol not started | ||
278 | * @ISHTP_HBM_START : start request message was sent | ||
279 | * @ISHTP_HBM_ENUM_CLIENTS : enumeration request was sent | ||
280 | * @ISHTP_HBM_CLIENT_PROPERTIES : acquiring clients properties | ||
281 | */ | ||
282 | enum ishtp_hbm_state { | ||
283 | ISHTP_HBM_IDLE = 0, | ||
284 | ISHTP_HBM_START, | ||
285 | ISHTP_HBM_STARTED, | ||
286 | ISHTP_HBM_ENUM_CLIENTS, | ||
287 | ISHTP_HBM_CLIENT_PROPERTIES, | ||
288 | ISHTP_HBM_WORKING, | ||
289 | ISHTP_HBM_STOPPED, | ||
290 | }; | ||
291 | |||
292 | static inline void ishtp_hbm_hdr(struct ishtp_msg_hdr *hdr, size_t length) | ||
293 | { | ||
294 | hdr->host_addr = 0; | ||
295 | hdr->fw_addr = 0; | ||
296 | hdr->length = length; | ||
297 | hdr->msg_complete = 1; | ||
298 | hdr->reserved = 0; | ||
299 | } | ||
300 | |||
301 | int ishtp_hbm_start_req(struct ishtp_device *dev); | ||
302 | int ishtp_hbm_start_wait(struct ishtp_device *dev); | ||
303 | int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, | ||
304 | struct ishtp_cl *cl); | ||
305 | int ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl); | ||
306 | int ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl); | ||
307 | void ishtp_hbm_enum_clients_req(struct ishtp_device *dev); | ||
308 | void bh_hbm_work_fn(struct work_struct *work); | ||
309 | void recv_hbm(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr); | ||
310 | void recv_fixed_cl_msg(struct ishtp_device *dev, | ||
311 | struct ishtp_msg_hdr *ishtp_hdr); | ||
312 | void ishtp_hbm_dispatch(struct ishtp_device *dev, | ||
313 | struct ishtp_bus_message *hdr); | ||
314 | |||
315 | void ishtp_query_subscribers(struct ishtp_device *dev); | ||
316 | |||
317 | /* Exported I/F */ | ||
318 | void ishtp_send_suspend(struct ishtp_device *dev); | ||
319 | void ishtp_send_resume(struct ishtp_device *dev); | ||
320 | |||
321 | #endif /* _ISHTP_HBM_H_ */ | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/init.c b/drivers/hid/intel-ish-hid/ishtp/init.c new file mode 100644 index 000000000000..ac364418e17c --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/init.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * Initialization protocol for ISHTP driver | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/export.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/miscdevice.h> | ||
20 | #include "ishtp-dev.h" | ||
21 | #include "hbm.h" | ||
22 | #include "client.h" | ||
23 | |||
24 | /** | ||
25 | * ishtp_dev_state_str() -Convert to string format | ||
26 | * @state: state to convert | ||
27 | * | ||
28 | * Convert state to string for prints | ||
29 | * | ||
30 | * Return: character pointer to converted string | ||
31 | */ | ||
32 | const char *ishtp_dev_state_str(int state) | ||
33 | { | ||
34 | switch (state) { | ||
35 | case ISHTP_DEV_INITIALIZING: | ||
36 | return "INITIALIZING"; | ||
37 | case ISHTP_DEV_INIT_CLIENTS: | ||
38 | return "INIT_CLIENTS"; | ||
39 | case ISHTP_DEV_ENABLED: | ||
40 | return "ENABLED"; | ||
41 | case ISHTP_DEV_RESETTING: | ||
42 | return "RESETTING"; | ||
43 | case ISHTP_DEV_DISABLED: | ||
44 | return "DISABLED"; | ||
45 | case ISHTP_DEV_POWER_DOWN: | ||
46 | return "POWER_DOWN"; | ||
47 | case ISHTP_DEV_POWER_UP: | ||
48 | return "POWER_UP"; | ||
49 | default: | ||
50 | return "unknown"; | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * ishtp_device_init() - ishtp device init | ||
56 | * @dev: ISHTP device instance | ||
57 | * | ||
58 | * After ISHTP device is alloacted, this function is used to initialize | ||
59 | * each field which includes spin lock, work struct and lists | ||
60 | */ | ||
61 | void ishtp_device_init(struct ishtp_device *dev) | ||
62 | { | ||
63 | dev->dev_state = ISHTP_DEV_INITIALIZING; | ||
64 | INIT_LIST_HEAD(&dev->cl_list); | ||
65 | INIT_LIST_HEAD(&dev->device_list); | ||
66 | dev->rd_msg_fifo_head = 0; | ||
67 | dev->rd_msg_fifo_tail = 0; | ||
68 | spin_lock_init(&dev->rd_msg_spinlock); | ||
69 | |||
70 | init_waitqueue_head(&dev->wait_hbm_recvd_msg); | ||
71 | spin_lock_init(&dev->read_list_spinlock); | ||
72 | spin_lock_init(&dev->device_lock); | ||
73 | spin_lock_init(&dev->device_list_lock); | ||
74 | spin_lock_init(&dev->cl_list_lock); | ||
75 | spin_lock_init(&dev->fw_clients_lock); | ||
76 | INIT_WORK(&dev->bh_hbm_work, bh_hbm_work_fn); | ||
77 | |||
78 | bitmap_zero(dev->host_clients_map, ISHTP_CLIENTS_MAX); | ||
79 | dev->open_handle_count = 0; | ||
80 | |||
81 | /* | ||
82 | * Reserving client ID 0 for ISHTP Bus Message communications | ||
83 | */ | ||
84 | bitmap_set(dev->host_clients_map, 0, 1); | ||
85 | |||
86 | INIT_LIST_HEAD(&dev->read_list.list); | ||
87 | |||
88 | } | ||
89 | EXPORT_SYMBOL(ishtp_device_init); | ||
90 | |||
91 | /** | ||
92 | * ishtp_start() - Start ISH processing | ||
93 | * @dev: ISHTP device instance | ||
94 | * | ||
95 | * Start ISHTP processing by sending query subscriber message | ||
96 | * | ||
97 | * Return: 0 on success else -ENODEV | ||
98 | */ | ||
99 | int ishtp_start(struct ishtp_device *dev) | ||
100 | { | ||
101 | if (ishtp_hbm_start_wait(dev)) { | ||
102 | dev_err(dev->devc, "HBM haven't started"); | ||
103 | goto err; | ||
104 | } | ||
105 | |||
106 | /* suspend & resume notification - send QUERY_SUBSCRIBERS msg */ | ||
107 | ishtp_query_subscribers(dev); | ||
108 | |||
109 | return 0; | ||
110 | err: | ||
111 | dev_err(dev->devc, "link layer initialization failed.\n"); | ||
112 | dev->dev_state = ISHTP_DEV_DISABLED; | ||
113 | return -ENODEV; | ||
114 | } | ||
115 | EXPORT_SYMBOL(ishtp_start); | ||
diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h new file mode 100644 index 000000000000..a94f9a8a96a0 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * Most ISHTP provider device and ISHTP logic declarations | ||
3 | * | ||
4 | * Copyright (c) 2003-2016, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | |||
16 | #ifndef _ISHTP_DEV_H_ | ||
17 | #define _ISHTP_DEV_H_ | ||
18 | |||
19 | #include <linux/types.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include "bus.h" | ||
22 | #include "hbm.h" | ||
23 | |||
24 | #define IPC_PAYLOAD_SIZE 128 | ||
25 | #define ISHTP_RD_MSG_BUF_SIZE IPC_PAYLOAD_SIZE | ||
26 | #define IPC_FULL_MSG_SIZE 132 | ||
27 | |||
28 | /* Number of messages to be held in ISR->BH FIFO */ | ||
29 | #define RD_INT_FIFO_SIZE 64 | ||
30 | |||
31 | /* | ||
32 | * Number of IPC messages to be held in Tx FIFO, to be sent by ISR - | ||
33 | * Tx complete interrupt or RX_COMPLETE handler | ||
34 | */ | ||
35 | #define IPC_TX_FIFO_SIZE 512 | ||
36 | |||
37 | /* | ||
38 | * Number of Maximum ISHTP Clients | ||
39 | */ | ||
40 | #define ISHTP_CLIENTS_MAX 256 | ||
41 | |||
42 | /* | ||
43 | * Number of File descriptors/handles | ||
44 | * that can be opened to the driver. | ||
45 | * | ||
46 | * Limit to 255: 256 Total Clients | ||
47 | * minus internal client for ISHTP Bus Messages | ||
48 | */ | ||
49 | #define ISHTP_MAX_OPEN_HANDLE_COUNT (ISHTP_CLIENTS_MAX - 1) | ||
50 | |||
51 | /* Internal Clients Number */ | ||
52 | #define ISHTP_HOST_CLIENT_ID_ANY (-1) | ||
53 | #define ISHTP_HBM_HOST_CLIENT_ID 0 | ||
54 | |||
55 | #define MAX_DMA_DELAY 20 | ||
56 | |||
57 | /* ISHTP device states */ | ||
58 | enum ishtp_dev_state { | ||
59 | ISHTP_DEV_INITIALIZING = 0, | ||
60 | ISHTP_DEV_INIT_CLIENTS, | ||
61 | ISHTP_DEV_ENABLED, | ||
62 | ISHTP_DEV_RESETTING, | ||
63 | ISHTP_DEV_DISABLED, | ||
64 | ISHTP_DEV_POWER_DOWN, | ||
65 | ISHTP_DEV_POWER_UP | ||
66 | }; | ||
67 | const char *ishtp_dev_state_str(int state); | ||
68 | |||
69 | struct ishtp_cl; | ||
70 | |||
71 | /** | ||
72 | * struct ishtp_fw_client - representation of fw client | ||
73 | * | ||
74 | * @props - client properties | ||
75 | * @client_id - fw client id | ||
76 | */ | ||
77 | struct ishtp_fw_client { | ||
78 | struct ishtp_client_properties props; | ||
79 | uint8_t client_id; | ||
80 | }; | ||
81 | |||
82 | /** | ||
83 | * struct ishtp_msg_data - ISHTP message data struct | ||
84 | * @size: Size of data in the *data | ||
85 | * @data: Pointer to data | ||
86 | */ | ||
87 | struct ishtp_msg_data { | ||
88 | uint32_t size; | ||
89 | unsigned char *data; | ||
90 | }; | ||
91 | |||
92 | /* | ||
93 | * struct ishtp_cl_rb - request block structure | ||
94 | * @list: Link to list members | ||
95 | * @cl: ISHTP client instance | ||
96 | * @buffer: message header | ||
97 | * @buf_idx: Index into buffer | ||
98 | * @read_time: unused at this time | ||
99 | */ | ||
100 | struct ishtp_cl_rb { | ||
101 | struct list_head list; | ||
102 | struct ishtp_cl *cl; | ||
103 | struct ishtp_msg_data buffer; | ||
104 | unsigned long buf_idx; | ||
105 | unsigned long read_time; | ||
106 | }; | ||
107 | |||
108 | /* | ||
109 | * Control info for IPC messages ISHTP/IPC sending FIFO - | ||
110 | * list with inline data buffer | ||
111 | * This structure will be filled with parameters submitted | ||
112 | * by the caller glue layer | ||
113 | * 'buf' may be pointing to the external buffer or to 'inline_data' | ||
114 | * 'offset' will be initialized to 0 by submitting | ||
115 | * | ||
116 | * 'ipc_send_compl' is intended for use by clients that send fragmented | ||
117 | * messages. When a fragment is sent down to IPC msg regs, | ||
118 | * it will be called. | ||
119 | * If it has more fragments to send, it will do it. With last fragment | ||
120 | * it will send appropriate ISHTP "message-complete" flag. | ||
121 | * It will remove the outstanding message | ||
122 | * (mark outstanding buffer as available). | ||
123 | * If counting flow control is in work and there are more flow control | ||
124 | * credits, it can put the next client message queued in cl. | ||
125 | * structure for IPC processing. | ||
126 | * | ||
127 | */ | ||
128 | struct wr_msg_ctl_info { | ||
129 | /* Will be called with 'ipc_send_compl_prm' as parameter */ | ||
130 | void (*ipc_send_compl)(void *); | ||
131 | |||
132 | void *ipc_send_compl_prm; | ||
133 | size_t length; | ||
134 | struct list_head link; | ||
135 | unsigned char inline_data[IPC_FULL_MSG_SIZE]; | ||
136 | }; | ||
137 | |||
138 | /* | ||
139 | * The ISHTP layer talks to hardware IPC message using the following | ||
140 | * callbacks | ||
141 | */ | ||
142 | struct ishtp_hw_ops { | ||
143 | int (*hw_reset)(struct ishtp_device *dev); | ||
144 | int (*ipc_reset)(struct ishtp_device *dev); | ||
145 | uint32_t (*ipc_get_header)(struct ishtp_device *dev, int length, | ||
146 | int busy); | ||
147 | int (*write)(struct ishtp_device *dev, | ||
148 | void (*ipc_send_compl)(void *), void *ipc_send_compl_prm, | ||
149 | unsigned char *msg, int length); | ||
150 | uint32_t (*ishtp_read_hdr)(const struct ishtp_device *dev); | ||
151 | int (*ishtp_read)(struct ishtp_device *dev, unsigned char *buffer, | ||
152 | unsigned long buffer_length); | ||
153 | uint32_t (*get_fw_status)(struct ishtp_device *dev); | ||
154 | void (*sync_fw_clock)(struct ishtp_device *dev); | ||
155 | }; | ||
156 | |||
157 | /** | ||
158 | * struct ishtp_device - ISHTP private device struct | ||
159 | */ | ||
160 | struct ishtp_device { | ||
161 | struct device *devc; /* pointer to lowest device */ | ||
162 | struct pci_dev *pdev; /* PCI device to get device ids */ | ||
163 | |||
164 | /* waitq for waiting for suspend response */ | ||
165 | wait_queue_head_t suspend_wait; | ||
166 | bool suspend_flag; /* Suspend is active */ | ||
167 | |||
168 | /* waitq for waiting for resume response */ | ||
169 | wait_queue_head_t resume_wait; | ||
170 | bool resume_flag; /*Resume is active */ | ||
171 | |||
172 | /* | ||
173 | * lock for the device, for everything that doesn't have | ||
174 | * a dedicated spinlock | ||
175 | */ | ||
176 | spinlock_t device_lock; | ||
177 | |||
178 | bool recvd_hw_ready; | ||
179 | struct hbm_version version; | ||
180 | int transfer_path; /* Choice of transfer path: IPC or DMA */ | ||
181 | |||
182 | /* ishtp device states */ | ||
183 | enum ishtp_dev_state dev_state; | ||
184 | enum ishtp_hbm_state hbm_state; | ||
185 | |||
186 | /* driver read queue */ | ||
187 | struct ishtp_cl_rb read_list; | ||
188 | spinlock_t read_list_spinlock; | ||
189 | |||
190 | /* list of ishtp_cl's */ | ||
191 | struct list_head cl_list; | ||
192 | spinlock_t cl_list_lock; | ||
193 | long open_handle_count; | ||
194 | |||
195 | /* List of bus devices */ | ||
196 | struct list_head device_list; | ||
197 | spinlock_t device_list_lock; | ||
198 | |||
199 | /* waiting queues for receive message from FW */ | ||
200 | wait_queue_head_t wait_hw_ready; | ||
201 | wait_queue_head_t wait_hbm_recvd_msg; | ||
202 | |||
203 | /* FIFO for input messages for BH processing */ | ||
204 | unsigned char rd_msg_fifo[RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE]; | ||
205 | unsigned int rd_msg_fifo_head, rd_msg_fifo_tail; | ||
206 | spinlock_t rd_msg_spinlock; | ||
207 | struct work_struct bh_hbm_work; | ||
208 | |||
209 | /* IPC write queue */ | ||
210 | struct wr_msg_ctl_info wr_processing_list_head, wr_free_list_head; | ||
211 | /* For both processing list and free list */ | ||
212 | spinlock_t wr_processing_spinlock; | ||
213 | |||
214 | spinlock_t out_ipc_spinlock; | ||
215 | |||
216 | struct ishtp_fw_client *fw_clients; /*Note:memory has to be allocated*/ | ||
217 | DECLARE_BITMAP(fw_clients_map, ISHTP_CLIENTS_MAX); | ||
218 | DECLARE_BITMAP(host_clients_map, ISHTP_CLIENTS_MAX); | ||
219 | uint8_t fw_clients_num; | ||
220 | uint8_t fw_client_presentation_num; | ||
221 | uint8_t fw_client_index; | ||
222 | spinlock_t fw_clients_lock; | ||
223 | |||
224 | /* TX DMA buffers and slots */ | ||
225 | int ishtp_host_dma_enabled; | ||
226 | void *ishtp_host_dma_tx_buf; | ||
227 | unsigned int ishtp_host_dma_tx_buf_size; | ||
228 | uint64_t ishtp_host_dma_tx_buf_phys; | ||
229 | int ishtp_dma_num_slots; | ||
230 | |||
231 | /* map of 4k blocks in Tx dma buf: 0-free, 1-used */ | ||
232 | uint8_t *ishtp_dma_tx_map; | ||
233 | spinlock_t ishtp_dma_tx_lock; | ||
234 | |||
235 | /* RX DMA buffers and slots */ | ||
236 | void *ishtp_host_dma_rx_buf; | ||
237 | unsigned int ishtp_host_dma_rx_buf_size; | ||
238 | uint64_t ishtp_host_dma_rx_buf_phys; | ||
239 | |||
240 | /* Dump to trace buffers if enabled*/ | ||
241 | void (*print_log)(struct ishtp_device *dev, char *format, ...); | ||
242 | |||
243 | /* Debug stats */ | ||
244 | unsigned int ipc_rx_cnt; | ||
245 | unsigned long long ipc_rx_bytes_cnt; | ||
246 | unsigned int ipc_tx_cnt; | ||
247 | unsigned long long ipc_tx_bytes_cnt; | ||
248 | |||
249 | const struct ishtp_hw_ops *ops; | ||
250 | size_t mtu; | ||
251 | uint32_t ishtp_msg_hdr; | ||
252 | char hw[0] __aligned(sizeof(void *)); | ||
253 | }; | ||
254 | |||
255 | static inline unsigned long ishtp_secs_to_jiffies(unsigned long sec) | ||
256 | { | ||
257 | return msecs_to_jiffies(sec * MSEC_PER_SEC); | ||
258 | } | ||
259 | |||
260 | /* | ||
261 | * Register Access Function | ||
262 | */ | ||
263 | static inline int ish_ipc_reset(struct ishtp_device *dev) | ||
264 | { | ||
265 | return dev->ops->ipc_reset(dev); | ||
266 | } | ||
267 | |||
268 | static inline int ish_hw_reset(struct ishtp_device *dev) | ||
269 | { | ||
270 | return dev->ops->hw_reset(dev); | ||
271 | } | ||
272 | |||
273 | /* Exported function */ | ||
274 | void ishtp_device_init(struct ishtp_device *dev); | ||
275 | int ishtp_start(struct ishtp_device *dev); | ||
276 | |||
277 | #endif /*_ISHTP_DEV_H_*/ | ||