Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HSI: hsi: Introducing HSI framework

Adds HSI framework in to the linux kernel.

High Speed Synchronous Serial Interface (HSI) is a
serial interface mainly used for connecting application
engines (APE) with cellular modem engines (CMT) in cellular
handsets.

HSI provides multiplexing for up to 16 logical channels,
low-latency and full duplex communication.

Signed-off-by: Carlos Chinea <carlos.chinea@nokia.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>

+1026
+2
drivers/Kconfig
··· 52 52 53 53 source "drivers/spi/Kconfig" 54 54 55 + source "drivers/hsi/Kconfig" 56 + 55 57 source "drivers/pps/Kconfig" 56 58 57 59 source "drivers/ptp/Kconfig"
+1
drivers/Makefile
··· 53 53 obj-$(CONFIG_TARGET_CORE) += target/ 54 54 obj-$(CONFIG_MTD) += mtd/ 55 55 obj-$(CONFIG_SPI) += spi/ 56 + obj-y += hsi/ 56 57 obj-y += net/ 57 58 obj-$(CONFIG_ATM) += atm/ 58 59 obj-$(CONFIG_FUSION) += message/
+17
drivers/hsi/Kconfig
··· 1 + # 2 + # HSI driver configuration 3 + # 4 + menuconfig HSI 5 + tristate "HSI support" 6 + ---help--- 7 + The "High speed synchronous Serial Interface" is 8 + synchronous serial interface used mainly to connect 9 + application engines and cellular modems. 10 + 11 + if HSI 12 + 13 + config HSI_BOARDINFO 14 + bool 15 + default y 16 + 17 + endif # HSI
+5
drivers/hsi/Makefile
··· 1 + # 2 + # Makefile for HSI 3 + # 4 + obj-$(CONFIG_HSI_BOARDINFO) += hsi_boardinfo.o 5 + obj-$(CONFIG_HSI) += hsi.o
+494
drivers/hsi/hsi.c
··· 1 + /* 2 + * HSI core. 3 + * 4 + * Copyright (C) 2010 Nokia Corporation. All rights reserved. 5 + * 6 + * Contact: Carlos Chinea <carlos.chinea@nokia.com> 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * version 2 as published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 + * General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 + * 02110-1301 USA 21 + */ 22 + #include <linux/hsi/hsi.h> 23 + #include <linux/compiler.h> 24 + #include <linux/rwsem.h> 25 + #include <linux/list.h> 26 + #include <linux/spinlock.h> 27 + #include <linux/kobject.h> 28 + #include <linux/slab.h> 29 + #include <linux/string.h> 30 + #include "hsi_core.h" 31 + 32 + static struct device_type hsi_ctrl = { 33 + .name = "hsi_controller", 34 + }; 35 + 36 + static struct device_type hsi_cl = { 37 + .name = "hsi_client", 38 + }; 39 + 40 + static struct device_type hsi_port = { 41 + .name = "hsi_port", 42 + }; 43 + 44 + static ssize_t modalias_show(struct device *dev, 45 + struct device_attribute *a __maybe_unused, char *buf) 46 + { 47 + return sprintf(buf, "hsi:%s\n", dev_name(dev)); 48 + } 49 + 50 + static struct device_attribute hsi_bus_dev_attrs[] = { 51 + __ATTR_RO(modalias), 52 + __ATTR_NULL, 53 + }; 54 + 55 + static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) 56 + { 57 + if (dev->type == &hsi_cl) 58 + add_uevent_var(env, "MODALIAS=hsi:%s", dev_name(dev)); 59 + 60 + return 0; 61 + } 62 + 63 + static int hsi_bus_match(struct device *dev, struct device_driver *driver) 64 + { 65 + return strcmp(dev_name(dev), driver->name) == 0; 66 + } 67 + 68 + static struct bus_type hsi_bus_type = { 69 + .name = "hsi", 70 + .dev_attrs = hsi_bus_dev_attrs, 71 + .match = hsi_bus_match, 72 + .uevent = hsi_bus_uevent, 73 + }; 74 + 75 + static void hsi_client_release(struct device *dev) 76 + { 77 + kfree(to_hsi_client(dev)); 78 + } 79 + 80 + static void hsi_new_client(struct hsi_port *port, struct hsi_board_info *info) 81 + { 82 + struct hsi_client *cl; 83 + unsigned long flags; 84 + 85 + cl = kzalloc(sizeof(*cl), GFP_KERNEL); 86 + if (!cl) 87 + return; 88 + cl->device.type = &hsi_cl; 89 + cl->tx_cfg = info->tx_cfg; 90 + cl->rx_cfg = info->rx_cfg; 91 + cl->device.bus = &hsi_bus_type; 92 + cl->device.parent = &port->device; 93 + cl->device.release = hsi_client_release; 94 + dev_set_name(&cl->device, info->name); 95 + cl->device.platform_data = info->platform_data; 96 + spin_lock_irqsave(&port->clock, flags); 97 + list_add_tail(&cl->link, &port->clients); 98 + spin_unlock_irqrestore(&port->clock, flags); 99 + if (info->archdata) 100 + cl->device.archdata = *info->archdata; 101 + if (device_register(&cl->device) < 0) { 102 + pr_err("hsi: failed to register client: %s\n", info->name); 103 + kfree(cl); 104 + } 105 + } 106 + 107 + static void hsi_scan_board_info(struct hsi_controller *hsi) 108 + { 109 + struct hsi_cl_info *cl_info; 110 + struct hsi_port *p; 111 + 112 + list_for_each_entry(cl_info, &hsi_board_list, list) 113 + if (cl_info->info.hsi_id == hsi->id) { 114 + p = hsi_find_port_num(hsi, cl_info->info.port); 115 + if (!p) 116 + continue; 117 + hsi_new_client(p, &cl_info->info); 118 + } 119 + } 120 + 121 + static int hsi_remove_client(struct device *dev, void *data __maybe_unused) 122 + { 123 + struct hsi_client *cl = to_hsi_client(dev); 124 + struct hsi_port *port = to_hsi_port(dev->parent); 125 + unsigned long flags; 126 + 127 + spin_lock_irqsave(&port->clock, flags); 128 + list_del(&cl->link); 129 + spin_unlock_irqrestore(&port->clock, flags); 130 + device_unregister(dev); 131 + 132 + return 0; 133 + } 134 + 135 + static int hsi_remove_port(struct device *dev, void *data __maybe_unused) 136 + { 137 + device_for_each_child(dev, NULL, hsi_remove_client); 138 + device_unregister(dev); 139 + 140 + return 0; 141 + } 142 + 143 + static void hsi_controller_release(struct device *dev __maybe_unused) 144 + { 145 + } 146 + 147 + static void hsi_port_release(struct device *dev __maybe_unused) 148 + { 149 + } 150 + 151 + /** 152 + * hsi_unregister_controller - Unregister an HSI controller 153 + * @hsi: The HSI controller to register 154 + */ 155 + void hsi_unregister_controller(struct hsi_controller *hsi) 156 + { 157 + device_for_each_child(&hsi->device, NULL, hsi_remove_port); 158 + device_unregister(&hsi->device); 159 + } 160 + EXPORT_SYMBOL_GPL(hsi_unregister_controller); 161 + 162 + /** 163 + * hsi_register_controller - Register an HSI controller and its ports 164 + * @hsi: The HSI controller to register 165 + * 166 + * Returns -errno on failure, 0 on success. 167 + */ 168 + int hsi_register_controller(struct hsi_controller *hsi) 169 + { 170 + unsigned int i; 171 + int err; 172 + 173 + hsi->device.type = &hsi_ctrl; 174 + hsi->device.bus = &hsi_bus_type; 175 + hsi->device.release = hsi_controller_release; 176 + err = device_register(&hsi->device); 177 + if (err < 0) 178 + return err; 179 + for (i = 0; i < hsi->num_ports; i++) { 180 + hsi->port[i].device.parent = &hsi->device; 181 + hsi->port[i].device.bus = &hsi_bus_type; 182 + hsi->port[i].device.release = hsi_port_release; 183 + hsi->port[i].device.type = &hsi_port; 184 + INIT_LIST_HEAD(&hsi->port[i].clients); 185 + spin_lock_init(&hsi->port[i].clock); 186 + err = device_register(&hsi->port[i].device); 187 + if (err < 0) 188 + goto out; 189 + } 190 + /* Populate HSI bus with HSI clients */ 191 + hsi_scan_board_info(hsi); 192 + 193 + return 0; 194 + out: 195 + hsi_unregister_controller(hsi); 196 + 197 + return err; 198 + } 199 + EXPORT_SYMBOL_GPL(hsi_register_controller); 200 + 201 + /** 202 + * hsi_register_client_driver - Register an HSI client to the HSI bus 203 + * @drv: HSI client driver to register 204 + * 205 + * Returns -errno on failure, 0 on success. 206 + */ 207 + int hsi_register_client_driver(struct hsi_client_driver *drv) 208 + { 209 + drv->driver.bus = &hsi_bus_type; 210 + 211 + return driver_register(&drv->driver); 212 + } 213 + EXPORT_SYMBOL_GPL(hsi_register_client_driver); 214 + 215 + static inline int hsi_dummy_msg(struct hsi_msg *msg __maybe_unused) 216 + { 217 + return 0; 218 + } 219 + 220 + static inline int hsi_dummy_cl(struct hsi_client *cl __maybe_unused) 221 + { 222 + return 0; 223 + } 224 + 225 + /** 226 + * hsi_alloc_controller - Allocate an HSI controller and its ports 227 + * @n_ports: Number of ports on the HSI controller 228 + * @flags: Kernel allocation flags 229 + * 230 + * Return NULL on failure or a pointer to an hsi_controller on success. 231 + */ 232 + struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags) 233 + { 234 + struct hsi_controller *hsi; 235 + struct hsi_port *port; 236 + unsigned int i; 237 + 238 + if (!n_ports) 239 + return NULL; 240 + 241 + port = kzalloc(sizeof(*port)*n_ports, flags); 242 + if (!port) 243 + return NULL; 244 + hsi = kzalloc(sizeof(*hsi), flags); 245 + if (!hsi) 246 + goto out; 247 + for (i = 0; i < n_ports; i++) { 248 + dev_set_name(&port[i].device, "port%d", i); 249 + port[i].num = i; 250 + port[i].async = hsi_dummy_msg; 251 + port[i].setup = hsi_dummy_cl; 252 + port[i].flush = hsi_dummy_cl; 253 + port[i].start_tx = hsi_dummy_cl; 254 + port[i].stop_tx = hsi_dummy_cl; 255 + port[i].release = hsi_dummy_cl; 256 + mutex_init(&port[i].lock); 257 + } 258 + hsi->num_ports = n_ports; 259 + hsi->port = port; 260 + 261 + return hsi; 262 + out: 263 + kfree(port); 264 + 265 + return NULL; 266 + } 267 + EXPORT_SYMBOL_GPL(hsi_alloc_controller); 268 + 269 + /** 270 + * hsi_free_controller - Free an HSI controller 271 + * @hsi: Pointer to HSI controller 272 + */ 273 + void hsi_free_controller(struct hsi_controller *hsi) 274 + { 275 + if (!hsi) 276 + return; 277 + 278 + kfree(hsi->port); 279 + kfree(hsi); 280 + } 281 + EXPORT_SYMBOL_GPL(hsi_free_controller); 282 + 283 + /** 284 + * hsi_free_msg - Free an HSI message 285 + * @msg: Pointer to the HSI message 286 + * 287 + * Client is responsible to free the buffers pointed by the scatterlists. 288 + */ 289 + void hsi_free_msg(struct hsi_msg *msg) 290 + { 291 + if (!msg) 292 + return; 293 + sg_free_table(&msg->sgt); 294 + kfree(msg); 295 + } 296 + EXPORT_SYMBOL_GPL(hsi_free_msg); 297 + 298 + /** 299 + * hsi_alloc_msg - Allocate an HSI message 300 + * @nents: Number of memory entries 301 + * @flags: Kernel allocation flags 302 + * 303 + * nents can be 0. This mainly makes sense for read transfer. 304 + * In that case, HSI drivers will call the complete callback when 305 + * there is data to be read without consuming it. 306 + * 307 + * Return NULL on failure or a pointer to an hsi_msg on success. 308 + */ 309 + struct hsi_msg *hsi_alloc_msg(unsigned int nents, gfp_t flags) 310 + { 311 + struct hsi_msg *msg; 312 + int err; 313 + 314 + msg = kzalloc(sizeof(*msg), flags); 315 + if (!msg) 316 + return NULL; 317 + 318 + if (!nents) 319 + return msg; 320 + 321 + err = sg_alloc_table(&msg->sgt, nents, flags); 322 + if (unlikely(err)) { 323 + kfree(msg); 324 + msg = NULL; 325 + } 326 + 327 + return msg; 328 + } 329 + EXPORT_SYMBOL_GPL(hsi_alloc_msg); 330 + 331 + /** 332 + * hsi_async - Submit an HSI transfer to the controller 333 + * @cl: HSI client sending the transfer 334 + * @msg: The HSI transfer passed to controller 335 + * 336 + * The HSI message must have the channel, ttype, complete and destructor 337 + * fields set beforehand. If nents > 0 then the client has to initialize 338 + * also the scatterlists to point to the buffers to write to or read from. 339 + * 340 + * HSI controllers relay on pre-allocated buffers from their clients and they 341 + * do not allocate buffers on their own. 342 + * 343 + * Once the HSI message transfer finishes, the HSI controller calls the 344 + * complete callback with the status and actual_len fields of the HSI message 345 + * updated. The complete callback can be called before returning from 346 + * hsi_async. 347 + * 348 + * Returns -errno on failure or 0 on success 349 + */ 350 + int hsi_async(struct hsi_client *cl, struct hsi_msg *msg) 351 + { 352 + struct hsi_port *port = hsi_get_port(cl); 353 + 354 + if (!hsi_port_claimed(cl)) 355 + return -EACCES; 356 + 357 + WARN_ON_ONCE(!msg->destructor || !msg->complete); 358 + msg->cl = cl; 359 + 360 + return port->async(msg); 361 + } 362 + EXPORT_SYMBOL_GPL(hsi_async); 363 + 364 + /** 365 + * hsi_claim_port - Claim the HSI client's port 366 + * @cl: HSI client that wants to claim its port 367 + * @share: Flag to indicate if the client wants to share the port or not. 368 + * 369 + * Returns -errno on failure, 0 on success. 370 + */ 371 + int hsi_claim_port(struct hsi_client *cl, unsigned int share) 372 + { 373 + struct hsi_port *port = hsi_get_port(cl); 374 + int err = 0; 375 + 376 + mutex_lock(&port->lock); 377 + if ((port->claimed) && (!port->shared || !share)) { 378 + err = -EBUSY; 379 + goto out; 380 + } 381 + if (!try_module_get(to_hsi_controller(port->device.parent)->owner)) { 382 + err = -ENODEV; 383 + goto out; 384 + } 385 + port->claimed++; 386 + port->shared = !!share; 387 + cl->pclaimed = 1; 388 + out: 389 + mutex_unlock(&port->lock); 390 + 391 + return err; 392 + } 393 + EXPORT_SYMBOL_GPL(hsi_claim_port); 394 + 395 + /** 396 + * hsi_release_port - Release the HSI client's port 397 + * @cl: HSI client which previously claimed its port 398 + */ 399 + void hsi_release_port(struct hsi_client *cl) 400 + { 401 + struct hsi_port *port = hsi_get_port(cl); 402 + 403 + mutex_lock(&port->lock); 404 + /* Allow HW driver to do some cleanup */ 405 + port->release(cl); 406 + if (cl->pclaimed) 407 + port->claimed--; 408 + BUG_ON(port->claimed < 0); 409 + cl->pclaimed = 0; 410 + if (!port->claimed) 411 + port->shared = 0; 412 + module_put(to_hsi_controller(port->device.parent)->owner); 413 + mutex_unlock(&port->lock); 414 + } 415 + EXPORT_SYMBOL_GPL(hsi_release_port); 416 + 417 + static int hsi_start_rx(struct hsi_client *cl, void *data __maybe_unused) 418 + { 419 + if (cl->hsi_start_rx) 420 + (*cl->hsi_start_rx)(cl); 421 + 422 + return 0; 423 + } 424 + 425 + static int hsi_stop_rx(struct hsi_client *cl, void *data __maybe_unused) 426 + { 427 + if (cl->hsi_stop_rx) 428 + (*cl->hsi_stop_rx)(cl); 429 + 430 + return 0; 431 + } 432 + 433 + static int hsi_port_for_each_client(struct hsi_port *port, void *data, 434 + int (*fn)(struct hsi_client *cl, void *data)) 435 + { 436 + struct hsi_client *cl; 437 + 438 + spin_lock(&port->clock); 439 + list_for_each_entry(cl, &port->clients, link) { 440 + spin_unlock(&port->clock); 441 + (*fn)(cl, data); 442 + spin_lock(&port->clock); 443 + } 444 + spin_unlock(&port->clock); 445 + 446 + return 0; 447 + } 448 + 449 + /** 450 + * hsi_event -Notifies clients about port events 451 + * @port: Port where the event occurred 452 + * @event: The event type 453 + * 454 + * Clients should not be concerned about wake line behavior. However, due 455 + * to a race condition in HSI HW protocol, clients need to be notified 456 + * about wake line changes, so they can implement a workaround for it. 457 + * 458 + * Events: 459 + * HSI_EVENT_START_RX - Incoming wake line high 460 + * HSI_EVENT_STOP_RX - Incoming wake line down 461 + */ 462 + void hsi_event(struct hsi_port *port, unsigned int event) 463 + { 464 + int (*fn)(struct hsi_client *cl, void *data); 465 + 466 + switch (event) { 467 + case HSI_EVENT_START_RX: 468 + fn = hsi_start_rx; 469 + break; 470 + case HSI_EVENT_STOP_RX: 471 + fn = hsi_stop_rx; 472 + break; 473 + default: 474 + return; 475 + } 476 + hsi_port_for_each_client(port, NULL, fn); 477 + } 478 + EXPORT_SYMBOL_GPL(hsi_event); 479 + 480 + static int __init hsi_init(void) 481 + { 482 + return bus_register(&hsi_bus_type); 483 + } 484 + postcore_initcall(hsi_init); 485 + 486 + static void __exit hsi_exit(void) 487 + { 488 + bus_unregister(&hsi_bus_type); 489 + } 490 + module_exit(hsi_exit); 491 + 492 + MODULE_AUTHOR("Carlos Chinea <carlos.chinea@nokia.com>"); 493 + MODULE_DESCRIPTION("High-speed Synchronous Serial Interface (HSI) framework"); 494 + MODULE_LICENSE("GPL v2");
+62
drivers/hsi/hsi_boardinfo.c
··· 1 + /* 2 + * HSI clients registration interface 3 + * 4 + * Copyright (C) 2010 Nokia Corporation. All rights reserved. 5 + * 6 + * Contact: Carlos Chinea <carlos.chinea@nokia.com> 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * version 2 as published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 + * General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 + * 02110-1301 USA 21 + */ 22 + #include <linux/hsi/hsi.h> 23 + #include <linux/list.h> 24 + #include <linux/slab.h> 25 + #include "hsi_core.h" 26 + 27 + /* 28 + * hsi_board_list is only used internally by the HSI framework. 29 + * No one else is allowed to make use of it. 30 + */ 31 + LIST_HEAD(hsi_board_list); 32 + EXPORT_SYMBOL_GPL(hsi_board_list); 33 + 34 + /** 35 + * hsi_register_board_info - Register HSI clients information 36 + * @info: Array of HSI clients on the board 37 + * @len: Length of the array 38 + * 39 + * HSI clients are statically declared and registered on board files. 40 + * 41 + * HSI clients will be automatically registered to the HSI bus once the 42 + * controller and the port where the clients wishes to attach are registered 43 + * to it. 44 + * 45 + * Return -errno on failure, 0 on success. 46 + */ 47 + int __init hsi_register_board_info(struct hsi_board_info const *info, 48 + unsigned int len) 49 + { 50 + struct hsi_cl_info *cl_info; 51 + 52 + cl_info = kzalloc(sizeof(*cl_info) * len, GFP_KERNEL); 53 + if (!cl_info) 54 + return -ENOMEM; 55 + 56 + for (; len; len--, info++, cl_info++) { 57 + cl_info->info = *info; 58 + list_add_tail(&cl_info->list, &hsi_board_list); 59 + } 60 + 61 + return 0; 62 + }
+35
drivers/hsi/hsi_core.h
··· 1 + /* 2 + * HSI framework internal interfaces, 3 + * 4 + * Copyright (C) 2010 Nokia Corporation. All rights reserved. 5 + * 6 + * Contact: Carlos Chinea <carlos.chinea@nokia.com> 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * version 2 as published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 + * General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 + * 02110-1301 USA 21 + */ 22 + 23 + #ifndef __LINUX_HSI_CORE_H__ 24 + #define __LINUX_HSI_CORE_H__ 25 + 26 + #include <linux/hsi/hsi.h> 27 + 28 + struct hsi_cl_info { 29 + struct list_head list; 30 + struct hsi_board_info info; 31 + }; 32 + 33 + extern struct list_head hsi_board_list; 34 + 35 + #endif /* __LINUX_HSI_CORE_H__ */
+410
include/linux/hsi/hsi.h
··· 1 + /* 2 + * HSI core header file. 3 + * 4 + * Copyright (C) 2010 Nokia Corporation. All rights reserved. 5 + * 6 + * Contact: Carlos Chinea <carlos.chinea@nokia.com> 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * version 2 as published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 + * General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 + * 02110-1301 USA 21 + */ 22 + 23 + #ifndef __LINUX_HSI_H__ 24 + #define __LINUX_HSI_H__ 25 + 26 + #include <linux/device.h> 27 + #include <linux/mutex.h> 28 + #include <linux/scatterlist.h> 29 + #include <linux/spinlock.h> 30 + #include <linux/list.h> 31 + #include <linux/module.h> 32 + 33 + /* HSI message ttype */ 34 + #define HSI_MSG_READ 0 35 + #define HSI_MSG_WRITE 1 36 + 37 + /* HSI configuration values */ 38 + enum { 39 + HSI_MODE_STREAM = 1, 40 + HSI_MODE_FRAME, 41 + }; 42 + 43 + enum { 44 + HSI_FLOW_SYNC, /* Synchronized flow */ 45 + HSI_FLOW_PIPE, /* Pipelined flow */ 46 + }; 47 + 48 + enum { 49 + HSI_ARB_RR, /* Round-robin arbitration */ 50 + HSI_ARB_PRIO, /* Channel priority arbitration */ 51 + }; 52 + 53 + #define HSI_MAX_CHANNELS 16 54 + 55 + /* HSI message status codes */ 56 + enum { 57 + HSI_STATUS_COMPLETED, /* Message transfer is completed */ 58 + HSI_STATUS_PENDING, /* Message pending to be read/write (POLL) */ 59 + HSI_STATUS_PROCEEDING, /* Message transfer is ongoing */ 60 + HSI_STATUS_QUEUED, /* Message waiting to be served */ 61 + HSI_STATUS_ERROR, /* Error when message transfer was ongoing */ 62 + }; 63 + 64 + /* HSI port event codes */ 65 + enum { 66 + HSI_EVENT_START_RX, 67 + HSI_EVENT_STOP_RX, 68 + }; 69 + 70 + /** 71 + * struct hsi_config - Configuration for RX/TX HSI modules 72 + * @mode: Bit transmission mode (STREAM or FRAME) 73 + * @channels: Number of channels to use [1..16] 74 + * @speed: Max bit transmission speed (Kbit/s) 75 + * @flow: RX flow type (SYNCHRONIZED or PIPELINE) 76 + * @arb_mode: Arbitration mode for TX frame (Round robin, priority) 77 + */ 78 + struct hsi_config { 79 + unsigned int mode; 80 + unsigned int channels; 81 + unsigned int speed; 82 + union { 83 + unsigned int flow; /* RX only */ 84 + unsigned int arb_mode; /* TX only */ 85 + }; 86 + }; 87 + 88 + /** 89 + * struct hsi_board_info - HSI client board info 90 + * @name: Name for the HSI device 91 + * @hsi_id: HSI controller id where the client sits 92 + * @port: Port number in the controller where the client sits 93 + * @tx_cfg: HSI TX configuration 94 + * @rx_cfg: HSI RX configuration 95 + * @platform_data: Platform related data 96 + * @archdata: Architecture-dependent device data 97 + */ 98 + struct hsi_board_info { 99 + const char *name; 100 + unsigned int hsi_id; 101 + unsigned int port; 102 + struct hsi_config tx_cfg; 103 + struct hsi_config rx_cfg; 104 + void *platform_data; 105 + struct dev_archdata *archdata; 106 + }; 107 + 108 + #ifdef CONFIG_HSI_BOARDINFO 109 + extern int hsi_register_board_info(struct hsi_board_info const *info, 110 + unsigned int len); 111 + #else 112 + static inline int hsi_register_board_info(struct hsi_board_info const *info, 113 + unsigned int len) 114 + { 115 + return 0; 116 + } 117 + #endif /* CONFIG_HSI_BOARDINFO */ 118 + 119 + /** 120 + * struct hsi_client - HSI client attached to an HSI port 121 + * @device: Driver model representation of the device 122 + * @tx_cfg: HSI TX configuration 123 + * @rx_cfg: HSI RX configuration 124 + * @hsi_start_rx: Called after incoming wake line goes high 125 + * @hsi_stop_rx: Called after incoming wake line goes low 126 + */ 127 + struct hsi_client { 128 + struct device device; 129 + struct hsi_config tx_cfg; 130 + struct hsi_config rx_cfg; 131 + void (*hsi_start_rx)(struct hsi_client *cl); 132 + void (*hsi_stop_rx)(struct hsi_client *cl); 133 + /* private: */ 134 + unsigned int pclaimed:1; 135 + struct list_head link; 136 + }; 137 + 138 + #define to_hsi_client(dev) container_of(dev, struct hsi_client, device) 139 + 140 + static inline void hsi_client_set_drvdata(struct hsi_client *cl, void *data) 141 + { 142 + dev_set_drvdata(&cl->device, data); 143 + } 144 + 145 + static inline void *hsi_client_drvdata(struct hsi_client *cl) 146 + { 147 + return dev_get_drvdata(&cl->device); 148 + } 149 + 150 + /** 151 + * struct hsi_client_driver - Driver associated to an HSI client 152 + * @driver: Driver model representation of the driver 153 + */ 154 + struct hsi_client_driver { 155 + struct device_driver driver; 156 + }; 157 + 158 + #define to_hsi_client_driver(drv) container_of(drv, struct hsi_client_driver,\ 159 + driver) 160 + 161 + int hsi_register_client_driver(struct hsi_client_driver *drv); 162 + 163 + static inline void hsi_unregister_client_driver(struct hsi_client_driver *drv) 164 + { 165 + driver_unregister(&drv->driver); 166 + } 167 + 168 + /** 169 + * struct hsi_msg - HSI message descriptor 170 + * @link: Free to use by the current descriptor owner 171 + * @cl: HSI device client that issues the transfer 172 + * @sgt: Head of the scatterlist array 173 + * @context: Client context data associated to the transfer 174 + * @complete: Transfer completion callback 175 + * @destructor: Destructor to free resources when flushing 176 + * @status: Status of the transfer when completed 177 + * @actual_len: Actual length of data transfered on completion 178 + * @channel: Channel were to TX/RX the message 179 + * @ttype: Transfer type (TX if set, RX otherwise) 180 + * @break_frame: if true HSI will send/receive a break frame. Data buffers are 181 + * ignored in the request. 182 + */ 183 + struct hsi_msg { 184 + struct list_head link; 185 + struct hsi_client *cl; 186 + struct sg_table sgt; 187 + void *context; 188 + 189 + void (*complete)(struct hsi_msg *msg); 190 + void (*destructor)(struct hsi_msg *msg); 191 + 192 + int status; 193 + unsigned int actual_len; 194 + unsigned int channel; 195 + unsigned int ttype:1; 196 + unsigned int break_frame:1; 197 + }; 198 + 199 + struct hsi_msg *hsi_alloc_msg(unsigned int n_frag, gfp_t flags); 200 + void hsi_free_msg(struct hsi_msg *msg); 201 + 202 + /** 203 + * struct hsi_port - HSI port device 204 + * @device: Driver model representation of the device 205 + * @tx_cfg: Current TX path configuration 206 + * @rx_cfg: Current RX path configuration 207 + * @num: Port number 208 + * @shared: Set when port can be shared by different clients 209 + * @claimed: Reference count of clients which claimed the port 210 + * @lock: Serialize port claim 211 + * @async: Asynchronous transfer callback 212 + * @setup: Callback to set the HSI client configuration 213 + * @flush: Callback to clean the HW state and destroy all pending transfers 214 + * @start_tx: Callback to inform that a client wants to TX data 215 + * @stop_tx: Callback to inform that a client no longer wishes to TX data 216 + * @release: Callback to inform that a client no longer uses the port 217 + * @clients: List of hsi_clients using the port. 218 + * @clock: Lock to serialize access to the clients list. 219 + */ 220 + struct hsi_port { 221 + struct device device; 222 + struct hsi_config tx_cfg; 223 + struct hsi_config rx_cfg; 224 + unsigned int num; 225 + unsigned int shared:1; 226 + int claimed; 227 + struct mutex lock; 228 + int (*async)(struct hsi_msg *msg); 229 + int (*setup)(struct hsi_client *cl); 230 + int (*flush)(struct hsi_client *cl); 231 + int (*start_tx)(struct hsi_client *cl); 232 + int (*stop_tx)(struct hsi_client *cl); 233 + int (*release)(struct hsi_client *cl); 234 + struct list_head clients; 235 + spinlock_t clock; 236 + }; 237 + 238 + #define to_hsi_port(dev) container_of(dev, struct hsi_port, device) 239 + #define hsi_get_port(cl) to_hsi_port((cl)->device.parent) 240 + 241 + void hsi_event(struct hsi_port *port, unsigned int event); 242 + int hsi_claim_port(struct hsi_client *cl, unsigned int share); 243 + void hsi_release_port(struct hsi_client *cl); 244 + 245 + static inline int hsi_port_claimed(struct hsi_client *cl) 246 + { 247 + return cl->pclaimed; 248 + } 249 + 250 + static inline void hsi_port_set_drvdata(struct hsi_port *port, void *data) 251 + { 252 + dev_set_drvdata(&port->device, data); 253 + } 254 + 255 + static inline void *hsi_port_drvdata(struct hsi_port *port) 256 + { 257 + return dev_get_drvdata(&port->device); 258 + } 259 + 260 + /** 261 + * struct hsi_controller - HSI controller device 262 + * @device: Driver model representation of the device 263 + * @owner: Pointer to the module owning the controller 264 + * @id: HSI controller ID 265 + * @num_ports: Number of ports in the HSI controller 266 + * @port: Array of HSI ports 267 + */ 268 + struct hsi_controller { 269 + struct device device; 270 + struct module *owner; 271 + unsigned int id; 272 + unsigned int num_ports; 273 + struct hsi_port *port; 274 + }; 275 + 276 + #define to_hsi_controller(dev) container_of(dev, struct hsi_controller, device) 277 + 278 + struct hsi_controller *hsi_alloc_controller(unsigned int n_ports, gfp_t flags); 279 + void hsi_free_controller(struct hsi_controller *hsi); 280 + int hsi_register_controller(struct hsi_controller *hsi); 281 + void hsi_unregister_controller(struct hsi_controller *hsi); 282 + 283 + static inline void hsi_controller_set_drvdata(struct hsi_controller *hsi, 284 + void *data) 285 + { 286 + dev_set_drvdata(&hsi->device, data); 287 + } 288 + 289 + static inline void *hsi_controller_drvdata(struct hsi_controller *hsi) 290 + { 291 + return dev_get_drvdata(&hsi->device); 292 + } 293 + 294 + static inline struct hsi_port *hsi_find_port_num(struct hsi_controller *hsi, 295 + unsigned int num) 296 + { 297 + return (num < hsi->num_ports) ? &hsi->port[num] : NULL; 298 + } 299 + 300 + /* 301 + * API for HSI clients 302 + */ 303 + int hsi_async(struct hsi_client *cl, struct hsi_msg *msg); 304 + 305 + /** 306 + * hsi_id - Get HSI controller ID associated to a client 307 + * @cl: Pointer to a HSI client 308 + * 309 + * Return the controller id where the client is attached to 310 + */ 311 + static inline unsigned int hsi_id(struct hsi_client *cl) 312 + { 313 + return to_hsi_controller(cl->device.parent->parent)->id; 314 + } 315 + 316 + /** 317 + * hsi_port_id - Gets the port number a client is attached to 318 + * @cl: Pointer to HSI client 319 + * 320 + * Return the port number associated to the client 321 + */ 322 + static inline unsigned int hsi_port_id(struct hsi_client *cl) 323 + { 324 + return to_hsi_port(cl->device.parent)->num; 325 + } 326 + 327 + /** 328 + * hsi_setup - Configure the client's port 329 + * @cl: Pointer to the HSI client 330 + * 331 + * When sharing ports, clients should either relay on a single 332 + * client setup or have the same setup for all of them. 333 + * 334 + * Return -errno on failure, 0 on success 335 + */ 336 + static inline int hsi_setup(struct hsi_client *cl) 337 + { 338 + if (!hsi_port_claimed(cl)) 339 + return -EACCES; 340 + return hsi_get_port(cl)->setup(cl); 341 + } 342 + 343 + /** 344 + * hsi_flush - Flush all pending transactions on the client's port 345 + * @cl: Pointer to the HSI client 346 + * 347 + * This function will destroy all pending hsi_msg in the port and reset 348 + * the HW port so it is ready to receive and transmit from a clean state. 349 + * 350 + * Return -errno on failure, 0 on success 351 + */ 352 + static inline int hsi_flush(struct hsi_client *cl) 353 + { 354 + if (!hsi_port_claimed(cl)) 355 + return -EACCES; 356 + return hsi_get_port(cl)->flush(cl); 357 + } 358 + 359 + /** 360 + * hsi_async_read - Submit a read transfer 361 + * @cl: Pointer to the HSI client 362 + * @msg: HSI message descriptor of the transfer 363 + * 364 + * Return -errno on failure, 0 on success 365 + */ 366 + static inline int hsi_async_read(struct hsi_client *cl, struct hsi_msg *msg) 367 + { 368 + msg->ttype = HSI_MSG_READ; 369 + return hsi_async(cl, msg); 370 + } 371 + 372 + /** 373 + * hsi_async_write - Submit a write transfer 374 + * @cl: Pointer to the HSI client 375 + * @msg: HSI message descriptor of the transfer 376 + * 377 + * Return -errno on failure, 0 on success 378 + */ 379 + static inline int hsi_async_write(struct hsi_client *cl, struct hsi_msg *msg) 380 + { 381 + msg->ttype = HSI_MSG_WRITE; 382 + return hsi_async(cl, msg); 383 + } 384 + 385 + /** 386 + * hsi_start_tx - Signal the port that the client wants to start a TX 387 + * @cl: Pointer to the HSI client 388 + * 389 + * Return -errno on failure, 0 on success 390 + */ 391 + static inline int hsi_start_tx(struct hsi_client *cl) 392 + { 393 + if (!hsi_port_claimed(cl)) 394 + return -EACCES; 395 + return hsi_get_port(cl)->start_tx(cl); 396 + } 397 + 398 + /** 399 + * hsi_stop_tx - Signal the port that the client no longer wants to transmit 400 + * @cl: Pointer to the HSI client 401 + * 402 + * Return -errno on failure, 0 on success 403 + */ 404 + static inline int hsi_stop_tx(struct hsi_client *cl) 405 + { 406 + if (!hsi_port_claimed(cl)) 407 + return -EACCES; 408 + return hsi_get_port(cl)->stop_tx(cl); 409 + } 410 + #endif /* __LINUX_HSI_H__ */