blob: f90bd06900cb4632786803faa7ea7a09b4716917 [file] [log] [blame]
Sudeep Duttfb4d0e32015-04-29 05:32:33 -07001/*
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
Sudeep Duttfb4d0e32015-04-29 05:32:33 -070083static void scif_qp_setup_handler(struct work_struct *work)
84{
85 struct scif_dev *scifdev = container_of(work, struct scif_dev,
86 qp_dwork.work);
87 struct scif_hw_dev *sdev = scifdev->sdev;
88 dma_addr_t da = 0;
89 int err;
90
91 if (scif_is_mgmt_node()) {
92 struct mic_bootparam *bp = sdev->dp;
93
94 da = bp->scif_card_dma_addr;
95 scifdev->rdb = bp->h2c_scif_db;
96 } else {
97 struct mic_bootparam __iomem *bp = sdev->rdp;
98
99 da = readq(&bp->scif_host_dma_addr);
100 scifdev->rdb = ioread8(&bp->c2h_scif_db);
101 }
102 if (da) {
103 err = scif_qp_response(da, scifdev);
104 if (err)
105 dev_err(&scifdev->sdev->dev,
106 "scif_qp_response err %d\n", err);
107 } else {
108 schedule_delayed_work(&scifdev->qp_dwork,
109 msecs_to_jiffies(1000));
110 }
111}
112
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700113static int scif_setup_scifdev(void)
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700114{
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700115 /* We support a maximum of 129 SCIF nodes including the mgmt node */
116#define MAX_SCIF_NODES 129
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700117 int i;
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700118 u8 num_nodes = MAX_SCIF_NODES;
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700119
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700120 scif_dev = kcalloc(num_nodes, sizeof(*scif_dev), GFP_KERNEL);
121 if (!scif_dev)
122 return -ENOMEM;
123 for (i = 0; i < num_nodes; i++) {
124 struct scif_dev *scifdev = &scif_dev[i];
125
126 scifdev->node = i;
127 scifdev->exit = OP_IDLE;
128 init_waitqueue_head(&scifdev->disconn_wq);
129 mutex_init(&scifdev->lock);
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700130 INIT_WORK(&scifdev->peer_add_work, scif_add_peer_device);
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700131 INIT_DELAYED_WORK(&scifdev->p2p_dwork,
132 scif_poll_qp_state);
133 INIT_DELAYED_WORK(&scifdev->qp_dwork,
134 scif_qp_setup_handler);
135 INIT_LIST_HEAD(&scifdev->p2p);
136 RCU_INIT_POINTER(scifdev->spdev, NULL);
137 }
138 return 0;
139}
140
141static void scif_destroy_scifdev(void)
142{
143 kfree(scif_dev);
144}
145
146static int scif_probe(struct scif_hw_dev *sdev)
147{
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700148 struct scif_dev *scifdev = &scif_dev[sdev->dnode];
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700149 int rc;
150
151 dev_set_drvdata(&sdev->dev, sdev);
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700152 scifdev->sdev = sdev;
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700153
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700154 if (1 == atomic_add_return(1, &g_loopb_cnt)) {
155 struct scif_dev *loopb_dev = &scif_dev[sdev->snode];
156
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700157 loopb_dev->sdev = sdev;
158 rc = scif_setup_loopback_qp(loopb_dev);
159 if (rc)
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700160 goto exit;
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700161 }
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700162
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700163 rc = scif_setup_intr_wq(scifdev);
164 if (rc)
165 goto destroy_loopb;
166 rc = scif_setup_qp(scifdev);
167 if (rc)
168 goto destroy_intr;
169 scifdev->db = sdev->hw_ops->next_db(sdev);
170 scifdev->cookie = sdev->hw_ops->request_irq(sdev, scif_intr_handler,
171 "SCIF_INTR", scifdev,
172 scifdev->db);
173 if (IS_ERR(scifdev->cookie)) {
174 rc = PTR_ERR(scifdev->cookie);
175 goto free_qp;
176 }
177 if (scif_is_mgmt_node()) {
178 struct mic_bootparam *bp = sdev->dp;
179
180 bp->c2h_scif_db = scifdev->db;
181 bp->scif_host_dma_addr = scifdev->qp_dma_addr;
182 } else {
183 struct mic_bootparam __iomem *bp = sdev->rdp;
184
185 iowrite8(scifdev->db, &bp->h2c_scif_db);
186 writeq(scifdev->qp_dma_addr, &bp->scif_card_dma_addr);
187 }
188 schedule_delayed_work(&scifdev->qp_dwork,
189 msecs_to_jiffies(1000));
190 return rc;
191free_qp:
192 scif_free_qp(scifdev);
193destroy_intr:
194 scif_destroy_intr_wq(scifdev);
195destroy_loopb:
196 if (atomic_dec_and_test(&g_loopb_cnt))
197 scif_destroy_loopback_qp(&scif_dev[sdev->snode]);
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700198exit:
199 return rc;
200}
201
202void scif_stop(struct scif_dev *scifdev)
203{
204 struct scif_dev *dev;
205 int i;
206
207 for (i = scif_info.maxid; i >= 0; i--) {
208 dev = &scif_dev[i];
209 if (scifdev_self(dev))
210 continue;
211 scif_handle_remove_node(i);
212 }
213}
214
215static void scif_remove(struct scif_hw_dev *sdev)
216{
217 struct scif_dev *scifdev = &scif_dev[sdev->dnode];
218
219 if (scif_is_mgmt_node()) {
220 struct mic_bootparam *bp = sdev->dp;
221
222 bp->c2h_scif_db = -1;
223 bp->scif_host_dma_addr = 0x0;
224 } else {
225 struct mic_bootparam __iomem *bp = sdev->rdp;
226
227 iowrite8(-1, &bp->h2c_scif_db);
228 writeq(0x0, &bp->scif_card_dma_addr);
229 }
230 if (scif_is_mgmt_node()) {
231 scif_disconnect_node(scifdev->node, true);
232 } else {
233 scif_info.card_initiated_exit = true;
234 scif_stop(scifdev);
235 }
236 if (atomic_dec_and_test(&g_loopb_cnt))
237 scif_destroy_loopback_qp(&scif_dev[sdev->snode]);
238 if (scifdev->cookie) {
239 sdev->hw_ops->free_irq(sdev, scifdev->cookie, scifdev);
240 scifdev->cookie = NULL;
241 }
242 scif_destroy_intr_wq(scifdev);
243 cancel_delayed_work(&scifdev->qp_dwork);
244 scif_free_qp(scifdev);
245 scifdev->rdb = -1;
246 scifdev->sdev = NULL;
247}
248
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700249static struct scif_hw_dev_id id_table[] = {
250 { MIC_SCIF_DEV, SCIF_DEV_ANY_ID },
251 { 0 },
252};
253
254static struct scif_driver scif_driver = {
255 .driver.name = KBUILD_MODNAME,
256 .driver.owner = THIS_MODULE,
257 .id_table = id_table,
258 .probe = scif_probe,
259 .remove = scif_remove,
260};
261
262static int _scif_init(void)
263{
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700264 int rc;
265
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700266 spin_lock_init(&scif_info.eplock);
267 spin_lock_init(&scif_info.nb_connect_lock);
268 spin_lock_init(&scif_info.port_lock);
269 mutex_init(&scif_info.conflock);
270 mutex_init(&scif_info.connlock);
271 INIT_LIST_HEAD(&scif_info.uaccept);
272 INIT_LIST_HEAD(&scif_info.listen);
273 INIT_LIST_HEAD(&scif_info.zombie);
274 INIT_LIST_HEAD(&scif_info.connected);
275 INIT_LIST_HEAD(&scif_info.disconnected);
276 INIT_LIST_HEAD(&scif_info.nb_connect_list);
277 init_waitqueue_head(&scif_info.exitwq);
278 scif_info.en_msg_log = 0;
279 scif_info.p2p_enable = 1;
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700280 rc = scif_setup_scifdev();
281 if (rc)
282 goto error;
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700283 INIT_WORK(&scif_info.misc_work, scif_misc_handler);
Nikhil Rao76371c72015-04-29 05:32:36 -0700284 INIT_WORK(&scif_info.conn_work, scif_conn_handler);
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700285 idr_init(&scif_ports);
286 return 0;
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700287error:
288 return rc;
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700289}
290
291static void _scif_exit(void)
292{
293 idr_destroy(&scif_ports);
294 scif_destroy_scifdev();
295}
296
297static int __init scif_init(void)
298{
299 struct miscdevice *mdev = &scif_info.mdev;
300 int rc;
301
302 _scif_init();
303 rc = scif_peer_bus_init();
304 if (rc)
305 goto exit;
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700306 rc = scif_register_driver(&scif_driver);
307 if (rc)
Ashutosh Dixitd3d912e2015-09-29 18:11:15 -0700308 goto peer_bus_exit;
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700309 rc = misc_register(mdev);
310 if (rc)
311 goto unreg_scif;
312 scif_init_debugfs();
313 return 0;
314unreg_scif:
315 scif_unregister_driver(&scif_driver);
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700316peer_bus_exit:
317 scif_peer_bus_exit();
318exit:
319 _scif_exit();
320 return rc;
321}
322
323static void __exit scif_exit(void)
324{
325 scif_exit_debugfs();
326 misc_deregister(&scif_info.mdev);
327 scif_unregister_driver(&scif_driver);
Sudeep Duttfb4d0e32015-04-29 05:32:33 -0700328 scif_peer_bus_exit();
329 _scif_exit();
330}
331
332module_init(scif_init);
333module_exit(scif_exit);
334
335MODULE_DEVICE_TABLE(scif, id_table);
336MODULE_AUTHOR("Intel Corporation");
337MODULE_DESCRIPTION("Intel(R) SCIF driver");
338MODULE_LICENSE("GPL v2");