Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.3 388 lines 9.3 kB view raw
1/* 2 * Intel MIC Platform Software Stack (MPSS) 3 * 4 * Copyright(c) 2014 Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License, version 2, as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * Intel SCIF driver. 16 * 17 */ 18#include <linux/module.h> 19#include <linux/idr.h> 20 21#include <linux/mic_common.h> 22#include "../common/mic_dev.h" 23#include "../bus/scif_bus.h" 24#include "scif_peer_bus.h" 25#include "scif_main.h" 26#include "scif_map.h" 27 28struct scif_info scif_info = { 29 .mdev = { 30 .minor = MISC_DYNAMIC_MINOR, 31 .name = "scif", 32 .fops = &scif_fops, 33 } 34}; 35 36struct scif_dev *scif_dev; 37static atomic_t g_loopb_cnt; 38 39/* Runs in the context of intr_wq */ 40static void scif_intr_bh_handler(struct work_struct *work) 41{ 42 struct scif_dev *scifdev = 43 container_of(work, struct scif_dev, intr_bh); 44 45 if (scifdev_self(scifdev)) 46 scif_loopb_msg_handler(scifdev, scifdev->qpairs); 47 else 48 scif_nodeqp_intrhandler(scifdev, scifdev->qpairs); 49} 50 51int scif_setup_intr_wq(struct scif_dev *scifdev) 52{ 53 if (!scifdev->intr_wq) { 54 snprintf(scifdev->intr_wqname, sizeof(scifdev->intr_wqname), 55 "SCIF INTR %d", scifdev->node); 56 scifdev->intr_wq = 57 alloc_ordered_workqueue(scifdev->intr_wqname, 0); 58 if (!scifdev->intr_wq) 59 return -ENOMEM; 60 INIT_WORK(&scifdev->intr_bh, scif_intr_bh_handler); 61 } 62 return 0; 63} 64 65void scif_destroy_intr_wq(struct scif_dev *scifdev) 66{ 67 if (scifdev->intr_wq) { 68 destroy_workqueue(scifdev->intr_wq); 69 scifdev->intr_wq = NULL; 70 } 71} 72 73irqreturn_t scif_intr_handler(int irq, void *data) 74{ 75 struct scif_dev *scifdev = data; 76 struct scif_hw_dev *sdev = scifdev->sdev; 77 78 sdev->hw_ops->ack_interrupt(sdev, scifdev->db); 79 queue_work(scifdev->intr_wq, &scifdev->intr_bh); 80 return IRQ_HANDLED; 81} 82 83static int scif_peer_probe(struct scif_peer_dev *spdev) 84{ 85 struct scif_dev *scifdev = &scif_dev[spdev->dnode]; 86 87 mutex_lock(&scif_info.conflock); 88 scif_info.total++; 89 scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid); 90 mutex_unlock(&scif_info.conflock); 91 rcu_assign_pointer(scifdev->spdev, spdev); 92 93 /* In the future SCIF kernel client devices will be added here */ 94 return 0; 95} 96 97static void scif_peer_remove(struct scif_peer_dev *spdev) 98{ 99 struct scif_dev *scifdev = &scif_dev[spdev->dnode]; 100 101 /* In the future SCIF kernel client devices will be removed here */ 102 spdev = rcu_dereference(scifdev->spdev); 103 if (spdev) 104 RCU_INIT_POINTER(scifdev->spdev, NULL); 105 synchronize_rcu(); 106 107 mutex_lock(&scif_info.conflock); 108 scif_info.total--; 109 mutex_unlock(&scif_info.conflock); 110} 111 112static void scif_qp_setup_handler(struct work_struct *work) 113{ 114 struct scif_dev *scifdev = container_of(work, struct scif_dev, 115 qp_dwork.work); 116 struct scif_hw_dev *sdev = scifdev->sdev; 117 dma_addr_t da = 0; 118 int err; 119 120 if (scif_is_mgmt_node()) { 121 struct mic_bootparam *bp = sdev->dp; 122 123 da = bp->scif_card_dma_addr; 124 scifdev->rdb = bp->h2c_scif_db; 125 } else { 126 struct mic_bootparam __iomem *bp = sdev->rdp; 127 128 da = readq(&bp->scif_host_dma_addr); 129 scifdev->rdb = ioread8(&bp->c2h_scif_db); 130 } 131 if (da) { 132 err = scif_qp_response(da, scifdev); 133 if (err) 134 dev_err(&scifdev->sdev->dev, 135 "scif_qp_response err %d\n", err); 136 } else { 137 schedule_delayed_work(&scifdev->qp_dwork, 138 msecs_to_jiffies(1000)); 139 } 140} 141 142static int scif_setup_scifdev(struct scif_hw_dev *sdev) 143{ 144 int i; 145 u8 num_nodes; 146 147 if (sdev->snode) { 148 struct mic_bootparam __iomem *bp = sdev->rdp; 149 150 num_nodes = ioread8(&bp->tot_nodes); 151 } else { 152 struct mic_bootparam *bp = sdev->dp; 153 154 num_nodes = bp->tot_nodes; 155 } 156 scif_dev = kcalloc(num_nodes, sizeof(*scif_dev), GFP_KERNEL); 157 if (!scif_dev) 158 return -ENOMEM; 159 for (i = 0; i < num_nodes; i++) { 160 struct scif_dev *scifdev = &scif_dev[i]; 161 162 scifdev->node = i; 163 scifdev->exit = OP_IDLE; 164 init_waitqueue_head(&scifdev->disconn_wq); 165 mutex_init(&scifdev->lock); 166 INIT_WORK(&scifdev->init_msg_work, scif_qp_response_ack); 167 INIT_DELAYED_WORK(&scifdev->p2p_dwork, 168 scif_poll_qp_state); 169 INIT_DELAYED_WORK(&scifdev->qp_dwork, 170 scif_qp_setup_handler); 171 INIT_LIST_HEAD(&scifdev->p2p); 172 RCU_INIT_POINTER(scifdev->spdev, NULL); 173 } 174 return 0; 175} 176 177static void scif_destroy_scifdev(void) 178{ 179 kfree(scif_dev); 180} 181 182static int scif_probe(struct scif_hw_dev *sdev) 183{ 184 struct scif_dev *scifdev; 185 int rc; 186 187 dev_set_drvdata(&sdev->dev, sdev); 188 if (1 == atomic_add_return(1, &g_loopb_cnt)) { 189 struct scif_dev *loopb_dev; 190 191 rc = scif_setup_scifdev(sdev); 192 if (rc) 193 goto exit; 194 scifdev = &scif_dev[sdev->dnode]; 195 scifdev->sdev = sdev; 196 loopb_dev = &scif_dev[sdev->snode]; 197 loopb_dev->sdev = sdev; 198 rc = scif_setup_loopback_qp(loopb_dev); 199 if (rc) 200 goto free_sdev; 201 } else { 202 scifdev = &scif_dev[sdev->dnode]; 203 scifdev->sdev = sdev; 204 } 205 rc = scif_setup_intr_wq(scifdev); 206 if (rc) 207 goto destroy_loopb; 208 rc = scif_setup_qp(scifdev); 209 if (rc) 210 goto destroy_intr; 211 scifdev->db = sdev->hw_ops->next_db(sdev); 212 scifdev->cookie = sdev->hw_ops->request_irq(sdev, scif_intr_handler, 213 "SCIF_INTR", scifdev, 214 scifdev->db); 215 if (IS_ERR(scifdev->cookie)) { 216 rc = PTR_ERR(scifdev->cookie); 217 goto free_qp; 218 } 219 if (scif_is_mgmt_node()) { 220 struct mic_bootparam *bp = sdev->dp; 221 222 bp->c2h_scif_db = scifdev->db; 223 bp->scif_host_dma_addr = scifdev->qp_dma_addr; 224 } else { 225 struct mic_bootparam __iomem *bp = sdev->rdp; 226 227 iowrite8(scifdev->db, &bp->h2c_scif_db); 228 writeq(scifdev->qp_dma_addr, &bp->scif_card_dma_addr); 229 } 230 schedule_delayed_work(&scifdev->qp_dwork, 231 msecs_to_jiffies(1000)); 232 return rc; 233free_qp: 234 scif_free_qp(scifdev); 235destroy_intr: 236 scif_destroy_intr_wq(scifdev); 237destroy_loopb: 238 if (atomic_dec_and_test(&g_loopb_cnt)) 239 scif_destroy_loopback_qp(&scif_dev[sdev->snode]); 240free_sdev: 241 scif_destroy_scifdev(); 242exit: 243 return rc; 244} 245 246void scif_stop(struct scif_dev *scifdev) 247{ 248 struct scif_dev *dev; 249 int i; 250 251 for (i = scif_info.maxid; i >= 0; i--) { 252 dev = &scif_dev[i]; 253 if (scifdev_self(dev)) 254 continue; 255 scif_handle_remove_node(i); 256 } 257} 258 259static void scif_remove(struct scif_hw_dev *sdev) 260{ 261 struct scif_dev *scifdev = &scif_dev[sdev->dnode]; 262 263 if (scif_is_mgmt_node()) { 264 struct mic_bootparam *bp = sdev->dp; 265 266 bp->c2h_scif_db = -1; 267 bp->scif_host_dma_addr = 0x0; 268 } else { 269 struct mic_bootparam __iomem *bp = sdev->rdp; 270 271 iowrite8(-1, &bp->h2c_scif_db); 272 writeq(0x0, &bp->scif_card_dma_addr); 273 } 274 if (scif_is_mgmt_node()) { 275 scif_disconnect_node(scifdev->node, true); 276 } else { 277 scif_info.card_initiated_exit = true; 278 scif_stop(scifdev); 279 } 280 if (atomic_dec_and_test(&g_loopb_cnt)) 281 scif_destroy_loopback_qp(&scif_dev[sdev->snode]); 282 if (scifdev->cookie) { 283 sdev->hw_ops->free_irq(sdev, scifdev->cookie, scifdev); 284 scifdev->cookie = NULL; 285 } 286 scif_destroy_intr_wq(scifdev); 287 cancel_delayed_work(&scifdev->qp_dwork); 288 scif_free_qp(scifdev); 289 scifdev->rdb = -1; 290 scifdev->sdev = NULL; 291} 292 293static struct scif_peer_driver scif_peer_driver = { 294 .driver.name = KBUILD_MODNAME, 295 .driver.owner = THIS_MODULE, 296 .probe = scif_peer_probe, 297 .remove = scif_peer_remove, 298}; 299 300static struct scif_hw_dev_id id_table[] = { 301 { MIC_SCIF_DEV, SCIF_DEV_ANY_ID }, 302 { 0 }, 303}; 304 305static struct scif_driver scif_driver = { 306 .driver.name = KBUILD_MODNAME, 307 .driver.owner = THIS_MODULE, 308 .id_table = id_table, 309 .probe = scif_probe, 310 .remove = scif_remove, 311}; 312 313static int _scif_init(void) 314{ 315 spin_lock_init(&scif_info.eplock); 316 spin_lock_init(&scif_info.nb_connect_lock); 317 spin_lock_init(&scif_info.port_lock); 318 mutex_init(&scif_info.conflock); 319 mutex_init(&scif_info.connlock); 320 INIT_LIST_HEAD(&scif_info.uaccept); 321 INIT_LIST_HEAD(&scif_info.listen); 322 INIT_LIST_HEAD(&scif_info.zombie); 323 INIT_LIST_HEAD(&scif_info.connected); 324 INIT_LIST_HEAD(&scif_info.disconnected); 325 INIT_LIST_HEAD(&scif_info.nb_connect_list); 326 init_waitqueue_head(&scif_info.exitwq); 327 scif_info.en_msg_log = 0; 328 scif_info.p2p_enable = 1; 329 INIT_WORK(&scif_info.misc_work, scif_misc_handler); 330 INIT_WORK(&scif_info.conn_work, scif_conn_handler); 331 idr_init(&scif_ports); 332 return 0; 333} 334 335static void _scif_exit(void) 336{ 337 idr_destroy(&scif_ports); 338 scif_destroy_scifdev(); 339} 340 341static int __init scif_init(void) 342{ 343 struct miscdevice *mdev = &scif_info.mdev; 344 int rc; 345 346 _scif_init(); 347 rc = scif_peer_bus_init(); 348 if (rc) 349 goto exit; 350 rc = scif_peer_register_driver(&scif_peer_driver); 351 if (rc) 352 goto peer_bus_exit; 353 rc = scif_register_driver(&scif_driver); 354 if (rc) 355 goto unreg_scif_peer; 356 rc = misc_register(mdev); 357 if (rc) 358 goto unreg_scif; 359 scif_init_debugfs(); 360 return 0; 361unreg_scif: 362 scif_unregister_driver(&scif_driver); 363unreg_scif_peer: 364 scif_peer_unregister_driver(&scif_peer_driver); 365peer_bus_exit: 366 scif_peer_bus_exit(); 367exit: 368 _scif_exit(); 369 return rc; 370} 371 372static void __exit scif_exit(void) 373{ 374 scif_exit_debugfs(); 375 misc_deregister(&scif_info.mdev); 376 scif_unregister_driver(&scif_driver); 377 scif_peer_unregister_driver(&scif_peer_driver); 378 scif_peer_bus_exit(); 379 _scif_exit(); 380} 381 382module_init(scif_init); 383module_exit(scif_exit); 384 385MODULE_DEVICE_TABLE(scif, id_table); 386MODULE_AUTHOR("Intel Corporation"); 387MODULE_DESCRIPTION("Intel(R) SCIF driver"); 388MODULE_LICENSE("GPL v2");