diff options
| -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_*/ | ||
