]>
Commit | Line | Data |
---|---|---|
8cfab3cf | 1 | // SPDX-License-Identifier: GPL-2.0 |
080b47de LG |
2 | /* |
3 | * Microsemi Switchtec(tm) PCIe Management Driver | |
4 | * Copyright (c) 2017, Microsemi Corporation | |
080b47de LG |
5 | */ |
6 | ||
5a1c269f | 7 | #include <linux/switchtec.h> |
52eabba5 LG |
8 | #include <linux/switchtec_ioctl.h> |
9 | ||
080b47de LG |
10 | #include <linux/interrupt.h> |
11 | #include <linux/module.h> | |
12 | #include <linux/fs.h> | |
13 | #include <linux/uaccess.h> | |
14 | #include <linux/poll.h> | |
080b47de | 15 | #include <linux/wait.h> |
f7eb7b8a | 16 | #include <linux/io-64-nonatomic-lo-hi.h> |
46feb6b4 GS |
17 | #include <linux/nospec.h> |
18 | ||
080b47de LG |
19 | MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); |
20 | MODULE_VERSION("0.1"); | |
21 | MODULE_LICENSE("GPL"); | |
22 | MODULE_AUTHOR("Microsemi Corporation"); | |
23 | ||
24 | static int max_devices = 16; | |
25 | module_param(max_devices, int, 0644); | |
26 | MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); | |
27 | ||
b8af8549 | 28 | static bool use_dma_mrpc = true; |
f7eb7b8a WS |
29 | module_param(use_dma_mrpc, bool, 0644); |
30 | MODULE_PARM_DESC(use_dma_mrpc, | |
31 | "Enable the use of the DMA MRPC feature"); | |
32 | ||
fcdf8e95 LG |
33 | static int nirqs = 32; |
34 | module_param(nirqs, int, 0644); | |
35 | MODULE_PARM_DESC(nirqs, "number of interrupts to allocate (more may be useful for NTB applications)"); | |
36 | ||
080b47de | 37 | static dev_t switchtec_devt; |
080b47de LG |
38 | static DEFINE_IDA(switchtec_minor_ida); |
39 | ||
302e994d LG |
40 | struct class *switchtec_class; |
41 | EXPORT_SYMBOL_GPL(switchtec_class); | |
080b47de LG |
42 | |
43 | enum mrpc_state { | |
44 | MRPC_IDLE = 0, | |
45 | MRPC_QUEUED, | |
46 | MRPC_RUNNING, | |
47 | MRPC_DONE, | |
48 | }; | |
49 | ||
50 | struct switchtec_user { | |
51 | struct switchtec_dev *stdev; | |
52 | ||
53 | enum mrpc_state state; | |
54 | ||
deaa0a8a | 55 | wait_queue_head_t cmd_comp; |
080b47de LG |
56 | struct kref kref; |
57 | struct list_head list; | |
58 | ||
deaa0a8a | 59 | bool cmd_done; |
080b47de LG |
60 | u32 cmd; |
61 | u32 status; | |
62 | u32 return_code; | |
63 | size_t data_len; | |
64 | size_t read_len; | |
65 | unsigned char data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; | |
66 | int event_cnt; | |
67 | }; | |
68 | ||
69 | static struct switchtec_user *stuser_create(struct switchtec_dev *stdev) | |
70 | { | |
71 | struct switchtec_user *stuser; | |
72 | ||
73 | stuser = kzalloc(sizeof(*stuser), GFP_KERNEL); | |
74 | if (!stuser) | |
75 | return ERR_PTR(-ENOMEM); | |
76 | ||
77 | get_device(&stdev->dev); | |
78 | stuser->stdev = stdev; | |
79 | kref_init(&stuser->kref); | |
80 | INIT_LIST_HEAD(&stuser->list); | |
deaa0a8a | 81 | init_waitqueue_head(&stuser->cmd_comp); |
080b47de LG |
82 | stuser->event_cnt = atomic_read(&stdev->event_cnt); |
83 | ||
84 | dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser); | |
85 | ||
86 | return stuser; | |
87 | } | |
88 | ||
89 | static void stuser_free(struct kref *kref) | |
90 | { | |
91 | struct switchtec_user *stuser; | |
92 | ||
93 | stuser = container_of(kref, struct switchtec_user, kref); | |
94 | ||
95 | dev_dbg(&stuser->stdev->dev, "%s: %p\n", __func__, stuser); | |
96 | ||
97 | put_device(&stuser->stdev->dev); | |
98 | kfree(stuser); | |
99 | } | |
100 | ||
101 | static void stuser_put(struct switchtec_user *stuser) | |
102 | { | |
103 | kref_put(&stuser->kref, stuser_free); | |
104 | } | |
105 | ||
106 | static void stuser_set_state(struct switchtec_user *stuser, | |
107 | enum mrpc_state state) | |
108 | { | |
109 | /* requires the mrpc_mutex to already be held when called */ | |
110 | ||
111 | const char * const state_names[] = { | |
112 | [MRPC_IDLE] = "IDLE", | |
113 | [MRPC_QUEUED] = "QUEUED", | |
114 | [MRPC_RUNNING] = "RUNNING", | |
115 | [MRPC_DONE] = "DONE", | |
116 | }; | |
117 | ||
118 | stuser->state = state; | |
119 | ||
120 | dev_dbg(&stuser->stdev->dev, "stuser state %p -> %s", | |
121 | stuser, state_names[state]); | |
122 | } | |
123 | ||
124 | static void mrpc_complete_cmd(struct switchtec_dev *stdev); | |
125 | ||
52d8db8e KC |
126 | static void flush_wc_buf(struct switchtec_dev *stdev) |
127 | { | |
128 | struct ntb_dbmsg_regs __iomem *mmio_dbmsg; | |
129 | ||
130 | /* | |
131 | * odb (outbound doorbell) register is processed by low latency | |
132 | * hardware and w/o side effect | |
133 | */ | |
134 | mmio_dbmsg = (void __iomem *)stdev->mmio_ntb + | |
135 | SWITCHTEC_NTB_REG_DBMSG_OFFSET; | |
136 | ioread32(&mmio_dbmsg->odb); | |
137 | } | |
138 | ||
080b47de LG |
139 | static void mrpc_cmd_submit(struct switchtec_dev *stdev) |
140 | { | |
141 | /* requires the mrpc_mutex to already be held when called */ | |
142 | ||
143 | struct switchtec_user *stuser; | |
144 | ||
145 | if (stdev->mrpc_busy) | |
146 | return; | |
147 | ||
148 | if (list_empty(&stdev->mrpc_queue)) | |
149 | return; | |
150 | ||
151 | stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, | |
152 | list); | |
153 | ||
f7eb7b8a WS |
154 | if (stdev->dma_mrpc) { |
155 | stdev->dma_mrpc->status = SWITCHTEC_MRPC_STATUS_INPROGRESS; | |
156 | memset(stdev->dma_mrpc->data, 0xFF, SWITCHTEC_MRPC_PAYLOAD_SIZE); | |
157 | } | |
158 | ||
080b47de LG |
159 | stuser_set_state(stuser, MRPC_RUNNING); |
160 | stdev->mrpc_busy = 1; | |
161 | memcpy_toio(&stdev->mmio_mrpc->input_data, | |
162 | stuser->data, stuser->data_len); | |
52d8db8e | 163 | flush_wc_buf(stdev); |
080b47de LG |
164 | iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd); |
165 | ||
080b47de LG |
166 | schedule_delayed_work(&stdev->mrpc_timeout, |
167 | msecs_to_jiffies(500)); | |
168 | } | |
169 | ||
170 | static int mrpc_queue_cmd(struct switchtec_user *stuser) | |
171 | { | |
172 | /* requires the mrpc_mutex to already be held when called */ | |
173 | ||
174 | struct switchtec_dev *stdev = stuser->stdev; | |
175 | ||
176 | kref_get(&stuser->kref); | |
177 | stuser->read_len = sizeof(stuser->data); | |
178 | stuser_set_state(stuser, MRPC_QUEUED); | |
deaa0a8a | 179 | stuser->cmd_done = false; |
080b47de LG |
180 | list_add_tail(&stuser->list, &stdev->mrpc_queue); |
181 | ||
182 | mrpc_cmd_submit(stdev); | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
187 | static void mrpc_complete_cmd(struct switchtec_dev *stdev) | |
188 | { | |
189 | /* requires the mrpc_mutex to already be held when called */ | |
190 | struct switchtec_user *stuser; | |
191 | ||
192 | if (list_empty(&stdev->mrpc_queue)) | |
193 | return; | |
194 | ||
195 | stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, | |
196 | list); | |
197 | ||
f7eb7b8a WS |
198 | if (stdev->dma_mrpc) |
199 | stuser->status = stdev->dma_mrpc->status; | |
200 | else | |
201 | stuser->status = ioread32(&stdev->mmio_mrpc->status); | |
202 | ||
080b47de LG |
203 | if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS) |
204 | return; | |
205 | ||
206 | stuser_set_state(stuser, MRPC_DONE); | |
207 | stuser->return_code = 0; | |
208 | ||
209 | if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE) | |
210 | goto out; | |
211 | ||
f7eb7b8a WS |
212 | if (stdev->dma_mrpc) |
213 | stuser->return_code = stdev->dma_mrpc->rtn_code; | |
214 | else | |
215 | stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value); | |
080b47de LG |
216 | if (stuser->return_code != 0) |
217 | goto out; | |
218 | ||
f7eb7b8a WS |
219 | if (stdev->dma_mrpc) |
220 | memcpy(stuser->data, &stdev->dma_mrpc->data, | |
221 | stuser->read_len); | |
222 | else | |
223 | memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, | |
224 | stuser->read_len); | |
080b47de | 225 | out: |
deaa0a8a SAS |
226 | stuser->cmd_done = true; |
227 | wake_up_interruptible(&stuser->cmd_comp); | |
080b47de LG |
228 | list_del_init(&stuser->list); |
229 | stuser_put(stuser); | |
230 | stdev->mrpc_busy = 0; | |
231 | ||
232 | mrpc_cmd_submit(stdev); | |
233 | } | |
234 | ||
235 | static void mrpc_event_work(struct work_struct *work) | |
236 | { | |
237 | struct switchtec_dev *stdev; | |
238 | ||
239 | stdev = container_of(work, struct switchtec_dev, mrpc_work); | |
240 | ||
241 | dev_dbg(&stdev->dev, "%s\n", __func__); | |
242 | ||
243 | mutex_lock(&stdev->mrpc_mutex); | |
244 | cancel_delayed_work(&stdev->mrpc_timeout); | |
245 | mrpc_complete_cmd(stdev); | |
246 | mutex_unlock(&stdev->mrpc_mutex); | |
247 | } | |
248 | ||
249 | static void mrpc_timeout_work(struct work_struct *work) | |
250 | { | |
251 | struct switchtec_dev *stdev; | |
252 | u32 status; | |
253 | ||
254 | stdev = container_of(work, struct switchtec_dev, mrpc_timeout.work); | |
255 | ||
256 | dev_dbg(&stdev->dev, "%s\n", __func__); | |
257 | ||
258 | mutex_lock(&stdev->mrpc_mutex); | |
259 | ||
f7eb7b8a WS |
260 | if (stdev->dma_mrpc) |
261 | status = stdev->dma_mrpc->status; | |
262 | else | |
263 | status = ioread32(&stdev->mmio_mrpc->status); | |
080b47de LG |
264 | if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) { |
265 | schedule_delayed_work(&stdev->mrpc_timeout, | |
266 | msecs_to_jiffies(500)); | |
267 | goto out; | |
268 | } | |
269 | ||
270 | mrpc_complete_cmd(stdev); | |
080b47de LG |
271 | out: |
272 | mutex_unlock(&stdev->mrpc_mutex); | |
273 | } | |
274 | ||
5d8e1881 LG |
275 | static ssize_t device_version_show(struct device *dev, |
276 | struct device_attribute *attr, char *buf) | |
277 | { | |
278 | struct switchtec_dev *stdev = to_stdev(dev); | |
279 | u32 ver; | |
280 | ||
281 | ver = ioread32(&stdev->mmio_sys_info->device_version); | |
282 | ||
283 | return sprintf(buf, "%x\n", ver); | |
284 | } | |
285 | static DEVICE_ATTR_RO(device_version); | |
286 | ||
287 | static ssize_t fw_version_show(struct device *dev, | |
288 | struct device_attribute *attr, char *buf) | |
289 | { | |
290 | struct switchtec_dev *stdev = to_stdev(dev); | |
291 | u32 ver; | |
292 | ||
293 | ver = ioread32(&stdev->mmio_sys_info->firmware_version); | |
294 | ||
295 | return sprintf(buf, "%08x\n", ver); | |
296 | } | |
297 | static DEVICE_ATTR_RO(fw_version); | |
298 | ||
299 | static ssize_t io_string_show(char *buf, void __iomem *attr, size_t len) | |
300 | { | |
301 | int i; | |
302 | ||
303 | memcpy_fromio(buf, attr, len); | |
304 | buf[len] = '\n'; | |
305 | buf[len + 1] = 0; | |
306 | ||
307 | for (i = len - 1; i > 0; i--) { | |
308 | if (buf[i] != ' ') | |
309 | break; | |
310 | buf[i] = '\n'; | |
311 | buf[i + 1] = 0; | |
312 | } | |
313 | ||
314 | return strlen(buf); | |
315 | } | |
316 | ||
317 | #define DEVICE_ATTR_SYS_INFO_STR(field) \ | |
318 | static ssize_t field ## _show(struct device *dev, \ | |
319 | struct device_attribute *attr, char *buf) \ | |
320 | { \ | |
321 | struct switchtec_dev *stdev = to_stdev(dev); \ | |
993d208d LG |
322 | struct sys_info_regs __iomem *si = stdev->mmio_sys_info; \ |
323 | if (stdev->gen == SWITCHTEC_GEN3) \ | |
324 | return io_string_show(buf, &si->gen3.field, \ | |
325 | sizeof(si->gen3.field)); \ | |
a3321ca3 LG |
326 | else if (stdev->gen == SWITCHTEC_GEN4) \ |
327 | return io_string_show(buf, &si->gen4.field, \ | |
328 | sizeof(si->gen4.field)); \ | |
993d208d LG |
329 | else \ |
330 | return -ENOTSUPP; \ | |
5d8e1881 LG |
331 | } \ |
332 | \ | |
333 | static DEVICE_ATTR_RO(field) | |
334 | ||
335 | DEVICE_ATTR_SYS_INFO_STR(vendor_id); | |
336 | DEVICE_ATTR_SYS_INFO_STR(product_id); | |
337 | DEVICE_ATTR_SYS_INFO_STR(product_revision); | |
b13313a0 LG |
338 | |
339 | static ssize_t component_vendor_show(struct device *dev, | |
340 | struct device_attribute *attr, char *buf) | |
341 | { | |
342 | struct switchtec_dev *stdev = to_stdev(dev); | |
343 | struct sys_info_regs __iomem *si = stdev->mmio_sys_info; | |
344 | ||
345 | /* component_vendor field not supported after gen3 */ | |
346 | if (stdev->gen != SWITCHTEC_GEN3) | |
347 | return sprintf(buf, "none\n"); | |
348 | ||
993d208d LG |
349 | return io_string_show(buf, &si->gen3.component_vendor, |
350 | sizeof(si->gen3.component_vendor)); | |
b13313a0 LG |
351 | } |
352 | static DEVICE_ATTR_RO(component_vendor); | |
5d8e1881 LG |
353 | |
354 | static ssize_t component_id_show(struct device *dev, | |
355 | struct device_attribute *attr, char *buf) | |
356 | { | |
357 | struct switchtec_dev *stdev = to_stdev(dev); | |
993d208d | 358 | int id = ioread16(&stdev->mmio_sys_info->gen3.component_id); |
5d8e1881 | 359 | |
b13313a0 LG |
360 | /* component_id field not supported after gen3 */ |
361 | if (stdev->gen != SWITCHTEC_GEN3) | |
362 | return sprintf(buf, "none\n"); | |
363 | ||
5d8e1881 LG |
364 | return sprintf(buf, "PM%04X\n", id); |
365 | } | |
366 | static DEVICE_ATTR_RO(component_id); | |
367 | ||
368 | static ssize_t component_revision_show(struct device *dev, | |
369 | struct device_attribute *attr, char *buf) | |
370 | { | |
371 | struct switchtec_dev *stdev = to_stdev(dev); | |
993d208d | 372 | int rev = ioread8(&stdev->mmio_sys_info->gen3.component_revision); |
5d8e1881 | 373 | |
b13313a0 LG |
374 | /* component_revision field not supported after gen3 */ |
375 | if (stdev->gen != SWITCHTEC_GEN3) | |
376 | return sprintf(buf, "255\n"); | |
377 | ||
5d8e1881 LG |
378 | return sprintf(buf, "%d\n", rev); |
379 | } | |
380 | static DEVICE_ATTR_RO(component_revision); | |
381 | ||
382 | static ssize_t partition_show(struct device *dev, | |
383 | struct device_attribute *attr, char *buf) | |
384 | { | |
385 | struct switchtec_dev *stdev = to_stdev(dev); | |
386 | ||
387 | return sprintf(buf, "%d\n", stdev->partition); | |
388 | } | |
389 | static DEVICE_ATTR_RO(partition); | |
390 | ||
391 | static ssize_t partition_count_show(struct device *dev, | |
392 | struct device_attribute *attr, char *buf) | |
393 | { | |
394 | struct switchtec_dev *stdev = to_stdev(dev); | |
395 | ||
396 | return sprintf(buf, "%d\n", stdev->partition_count); | |
397 | } | |
398 | static DEVICE_ATTR_RO(partition_count); | |
399 | ||
400 | static struct attribute *switchtec_device_attrs[] = { | |
401 | &dev_attr_device_version.attr, | |
402 | &dev_attr_fw_version.attr, | |
403 | &dev_attr_vendor_id.attr, | |
404 | &dev_attr_product_id.attr, | |
405 | &dev_attr_product_revision.attr, | |
406 | &dev_attr_component_vendor.attr, | |
407 | &dev_attr_component_id.attr, | |
408 | &dev_attr_component_revision.attr, | |
409 | &dev_attr_partition.attr, | |
410 | &dev_attr_partition_count.attr, | |
411 | NULL, | |
412 | }; | |
413 | ||
414 | ATTRIBUTE_GROUPS(switchtec_device); | |
415 | ||
080b47de LG |
416 | static int switchtec_dev_open(struct inode *inode, struct file *filp) |
417 | { | |
418 | struct switchtec_dev *stdev; | |
419 | struct switchtec_user *stuser; | |
420 | ||
421 | stdev = container_of(inode->i_cdev, struct switchtec_dev, cdev); | |
422 | ||
423 | stuser = stuser_create(stdev); | |
424 | if (IS_ERR(stuser)) | |
425 | return PTR_ERR(stuser); | |
426 | ||
427 | filp->private_data = stuser; | |
c5bf68fe | 428 | stream_open(inode, filp); |
080b47de LG |
429 | |
430 | dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser); | |
431 | ||
432 | return 0; | |
433 | } | |
434 | ||
435 | static int switchtec_dev_release(struct inode *inode, struct file *filp) | |
436 | { | |
437 | struct switchtec_user *stuser = filp->private_data; | |
438 | ||
439 | stuser_put(stuser); | |
440 | ||
441 | return 0; | |
442 | } | |
443 | ||
444 | static int lock_mutex_and_test_alive(struct switchtec_dev *stdev) | |
445 | { | |
446 | if (mutex_lock_interruptible(&stdev->mrpc_mutex)) | |
447 | return -EINTR; | |
448 | ||
449 | if (!stdev->alive) { | |
450 | mutex_unlock(&stdev->mrpc_mutex); | |
451 | return -ENODEV; | |
452 | } | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
457 | static ssize_t switchtec_dev_write(struct file *filp, const char __user *data, | |
458 | size_t size, loff_t *off) | |
459 | { | |
460 | struct switchtec_user *stuser = filp->private_data; | |
461 | struct switchtec_dev *stdev = stuser->stdev; | |
462 | int rc; | |
463 | ||
464 | if (size < sizeof(stuser->cmd) || | |
465 | size > sizeof(stuser->cmd) + sizeof(stuser->data)) | |
466 | return -EINVAL; | |
467 | ||
468 | stuser->data_len = size - sizeof(stuser->cmd); | |
469 | ||
470 | rc = lock_mutex_and_test_alive(stdev); | |
471 | if (rc) | |
472 | return rc; | |
473 | ||
474 | if (stuser->state != MRPC_IDLE) { | |
475 | rc = -EBADE; | |
476 | goto out; | |
477 | } | |
478 | ||
479 | rc = copy_from_user(&stuser->cmd, data, sizeof(stuser->cmd)); | |
480 | if (rc) { | |
481 | rc = -EFAULT; | |
482 | goto out; | |
483 | } | |
ce7c8860 KC |
484 | if (((MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_WRITE) || |
485 | (MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_READ)) && | |
486 | !capable(CAP_SYS_ADMIN)) { | |
487 | rc = -EPERM; | |
488 | goto out; | |
489 | } | |
080b47de LG |
490 | |
491 | data += sizeof(stuser->cmd); | |
492 | rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd)); | |
493 | if (rc) { | |
494 | rc = -EFAULT; | |
495 | goto out; | |
496 | } | |
497 | ||
498 | rc = mrpc_queue_cmd(stuser); | |
499 | ||
500 | out: | |
501 | mutex_unlock(&stdev->mrpc_mutex); | |
502 | ||
503 | if (rc) | |
504 | return rc; | |
505 | ||
506 | return size; | |
507 | } | |
508 | ||
509 | static ssize_t switchtec_dev_read(struct file *filp, char __user *data, | |
510 | size_t size, loff_t *off) | |
511 | { | |
512 | struct switchtec_user *stuser = filp->private_data; | |
513 | struct switchtec_dev *stdev = stuser->stdev; | |
514 | int rc; | |
515 | ||
516 | if (size < sizeof(stuser->cmd) || | |
517 | size > sizeof(stuser->cmd) + sizeof(stuser->data)) | |
518 | return -EINVAL; | |
519 | ||
520 | rc = lock_mutex_and_test_alive(stdev); | |
521 | if (rc) | |
522 | return rc; | |
523 | ||
524 | if (stuser->state == MRPC_IDLE) { | |
525 | mutex_unlock(&stdev->mrpc_mutex); | |
526 | return -EBADE; | |
527 | } | |
528 | ||
529 | stuser->read_len = size - sizeof(stuser->return_code); | |
530 | ||
531 | mutex_unlock(&stdev->mrpc_mutex); | |
532 | ||
533 | if (filp->f_flags & O_NONBLOCK) { | |
deaa0a8a | 534 | if (!stuser->cmd_done) |
080b47de LG |
535 | return -EAGAIN; |
536 | } else { | |
deaa0a8a SAS |
537 | rc = wait_event_interruptible(stuser->cmd_comp, |
538 | stuser->cmd_done); | |
080b47de LG |
539 | if (rc < 0) |
540 | return rc; | |
541 | } | |
542 | ||
543 | rc = lock_mutex_and_test_alive(stdev); | |
544 | if (rc) | |
545 | return rc; | |
546 | ||
547 | if (stuser->state != MRPC_DONE) { | |
548 | mutex_unlock(&stdev->mrpc_mutex); | |
549 | return -EBADE; | |
550 | } | |
551 | ||
552 | rc = copy_to_user(data, &stuser->return_code, | |
553 | sizeof(stuser->return_code)); | |
554 | if (rc) { | |
555 | rc = -EFAULT; | |
556 | goto out; | |
557 | } | |
558 | ||
559 | data += sizeof(stuser->return_code); | |
560 | rc = copy_to_user(data, &stuser->data, | |
561 | size - sizeof(stuser->return_code)); | |
562 | if (rc) { | |
563 | rc = -EFAULT; | |
564 | goto out; | |
565 | } | |
566 | ||
567 | stuser_set_state(stuser, MRPC_IDLE); | |
568 | ||
569 | out: | |
570 | mutex_unlock(&stdev->mrpc_mutex); | |
571 | ||
572 | if (stuser->status == SWITCHTEC_MRPC_STATUS_DONE) | |
573 | return size; | |
574 | else if (stuser->status == SWITCHTEC_MRPC_STATUS_INTERRUPTED) | |
575 | return -ENXIO; | |
576 | else | |
577 | return -EBADMSG; | |
578 | } | |
579 | ||
afc9a42b | 580 | static __poll_t switchtec_dev_poll(struct file *filp, poll_table *wait) |
080b47de LG |
581 | { |
582 | struct switchtec_user *stuser = filp->private_data; | |
583 | struct switchtec_dev *stdev = stuser->stdev; | |
afc9a42b | 584 | __poll_t ret = 0; |
080b47de | 585 | |
deaa0a8a | 586 | poll_wait(filp, &stuser->cmd_comp, wait); |
080b47de LG |
587 | poll_wait(filp, &stdev->event_wq, wait); |
588 | ||
589 | if (lock_mutex_and_test_alive(stdev)) | |
a9a08845 | 590 | return EPOLLIN | EPOLLRDHUP | EPOLLOUT | EPOLLERR | EPOLLHUP; |
080b47de LG |
591 | |
592 | mutex_unlock(&stdev->mrpc_mutex); | |
593 | ||
deaa0a8a | 594 | if (stuser->cmd_done) |
a9a08845 | 595 | ret |= EPOLLIN | EPOLLRDNORM; |
080b47de LG |
596 | |
597 | if (stuser->event_cnt != atomic_read(&stdev->event_cnt)) | |
a9a08845 | 598 | ret |= EPOLLPRI | EPOLLRDBAND; |
080b47de LG |
599 | |
600 | return ret; | |
601 | } | |
602 | ||
52eabba5 LG |
603 | static int ioctl_flash_info(struct switchtec_dev *stdev, |
604 | struct switchtec_ioctl_flash_info __user *uinfo) | |
605 | { | |
606 | struct switchtec_ioctl_flash_info info = {0}; | |
607 | struct flash_info_regs __iomem *fi = stdev->mmio_flash_info; | |
608 | ||
993d208d LG |
609 | if (stdev->gen == SWITCHTEC_GEN3) { |
610 | info.flash_length = ioread32(&fi->gen3.flash_length); | |
611 | info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3; | |
4efa1d2e KC |
612 | } else if (stdev->gen == SWITCHTEC_GEN4) { |
613 | info.flash_length = ioread32(&fi->gen4.flash_length); | |
614 | info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN4; | |
993d208d LG |
615 | } else { |
616 | return -ENOTSUPP; | |
617 | } | |
52eabba5 LG |
618 | |
619 | if (copy_to_user(uinfo, &info, sizeof(info))) | |
620 | return -EFAULT; | |
621 | ||
622 | return 0; | |
623 | } | |
624 | ||
625 | static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info, | |
626 | struct partition_info __iomem *pi) | |
627 | { | |
628 | info->address = ioread32(&pi->address); | |
629 | info->length = ioread32(&pi->length); | |
630 | } | |
631 | ||
6a3d1b54 LG |
632 | static int flash_part_info_gen3(struct switchtec_dev *stdev, |
633 | struct switchtec_ioctl_flash_part_info *info) | |
52eabba5 | 634 | { |
993d208d LG |
635 | struct flash_info_regs_gen3 __iomem *fi = |
636 | &stdev->mmio_flash_info->gen3; | |
637 | struct sys_info_regs_gen3 __iomem *si = &stdev->mmio_sys_info->gen3; | |
52eabba5 LG |
638 | u32 active_addr = -1; |
639 | ||
6a3d1b54 | 640 | switch (info->flash_partition) { |
52eabba5 LG |
641 | case SWITCHTEC_IOCTL_PART_CFG0: |
642 | active_addr = ioread32(&fi->active_cfg); | |
6a3d1b54 | 643 | set_fw_info_part(info, &fi->cfg0); |
fcccd282 | 644 | if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG0_RUNNING) |
6a3d1b54 | 645 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; |
52eabba5 LG |
646 | break; |
647 | case SWITCHTEC_IOCTL_PART_CFG1: | |
648 | active_addr = ioread32(&fi->active_cfg); | |
6a3d1b54 | 649 | set_fw_info_part(info, &fi->cfg1); |
fcccd282 | 650 | if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG1_RUNNING) |
6a3d1b54 | 651 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; |
52eabba5 LG |
652 | break; |
653 | case SWITCHTEC_IOCTL_PART_IMG0: | |
654 | active_addr = ioread32(&fi->active_img); | |
6a3d1b54 | 655 | set_fw_info_part(info, &fi->img0); |
fcccd282 | 656 | if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG0_RUNNING) |
6a3d1b54 | 657 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; |
52eabba5 LG |
658 | break; |
659 | case SWITCHTEC_IOCTL_PART_IMG1: | |
660 | active_addr = ioread32(&fi->active_img); | |
6a3d1b54 | 661 | set_fw_info_part(info, &fi->img1); |
fcccd282 | 662 | if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG1_RUNNING) |
6a3d1b54 | 663 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; |
52eabba5 LG |
664 | break; |
665 | case SWITCHTEC_IOCTL_PART_NVLOG: | |
6a3d1b54 | 666 | set_fw_info_part(info, &fi->nvlog); |
52eabba5 LG |
667 | break; |
668 | case SWITCHTEC_IOCTL_PART_VENDOR0: | |
6a3d1b54 | 669 | set_fw_info_part(info, &fi->vendor[0]); |
52eabba5 LG |
670 | break; |
671 | case SWITCHTEC_IOCTL_PART_VENDOR1: | |
6a3d1b54 | 672 | set_fw_info_part(info, &fi->vendor[1]); |
52eabba5 LG |
673 | break; |
674 | case SWITCHTEC_IOCTL_PART_VENDOR2: | |
6a3d1b54 | 675 | set_fw_info_part(info, &fi->vendor[2]); |
52eabba5 LG |
676 | break; |
677 | case SWITCHTEC_IOCTL_PART_VENDOR3: | |
6a3d1b54 | 678 | set_fw_info_part(info, &fi->vendor[3]); |
52eabba5 LG |
679 | break; |
680 | case SWITCHTEC_IOCTL_PART_VENDOR4: | |
6a3d1b54 | 681 | set_fw_info_part(info, &fi->vendor[4]); |
52eabba5 LG |
682 | break; |
683 | case SWITCHTEC_IOCTL_PART_VENDOR5: | |
6a3d1b54 | 684 | set_fw_info_part(info, &fi->vendor[5]); |
52eabba5 LG |
685 | break; |
686 | case SWITCHTEC_IOCTL_PART_VENDOR6: | |
6a3d1b54 | 687 | set_fw_info_part(info, &fi->vendor[6]); |
52eabba5 LG |
688 | break; |
689 | case SWITCHTEC_IOCTL_PART_VENDOR7: | |
6a3d1b54 | 690 | set_fw_info_part(info, &fi->vendor[7]); |
52eabba5 LG |
691 | break; |
692 | default: | |
693 | return -EINVAL; | |
694 | } | |
695 | ||
6a3d1b54 LG |
696 | if (info->address == active_addr) |
697 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
698 | ||
699 | return 0; | |
700 | } | |
701 | ||
4efa1d2e KC |
702 | static int flash_part_info_gen4(struct switchtec_dev *stdev, |
703 | struct switchtec_ioctl_flash_part_info *info) | |
704 | { | |
705 | struct flash_info_regs_gen4 __iomem *fi = &stdev->mmio_flash_info->gen4; | |
706 | struct sys_info_regs_gen4 __iomem *si = &stdev->mmio_sys_info->gen4; | |
707 | struct active_partition_info_gen4 __iomem *af = &fi->active_flag; | |
708 | ||
709 | switch (info->flash_partition) { | |
710 | case SWITCHTEC_IOCTL_PART_MAP_0: | |
711 | set_fw_info_part(info, &fi->map0); | |
712 | break; | |
713 | case SWITCHTEC_IOCTL_PART_MAP_1: | |
714 | set_fw_info_part(info, &fi->map1); | |
715 | break; | |
716 | case SWITCHTEC_IOCTL_PART_KEY_0: | |
717 | set_fw_info_part(info, &fi->key0); | |
718 | if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY0_ACTIVE) | |
719 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
720 | if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY0_RUNNING) | |
721 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
722 | break; | |
723 | case SWITCHTEC_IOCTL_PART_KEY_1: | |
724 | set_fw_info_part(info, &fi->key1); | |
725 | if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY1_ACTIVE) | |
726 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
727 | if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY1_RUNNING) | |
728 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
729 | break; | |
730 | case SWITCHTEC_IOCTL_PART_BL2_0: | |
731 | set_fw_info_part(info, &fi->bl2_0); | |
732 | if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_0_ACTIVE) | |
733 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
734 | if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_0_RUNNING) | |
735 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
736 | break; | |
737 | case SWITCHTEC_IOCTL_PART_BL2_1: | |
738 | set_fw_info_part(info, &fi->bl2_1); | |
739 | if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_1_ACTIVE) | |
740 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
741 | if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_1_RUNNING) | |
742 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
743 | break; | |
744 | case SWITCHTEC_IOCTL_PART_CFG0: | |
745 | set_fw_info_part(info, &fi->cfg0); | |
746 | if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG0_ACTIVE) | |
747 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
748 | if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG0_RUNNING) | |
749 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
750 | break; | |
751 | case SWITCHTEC_IOCTL_PART_CFG1: | |
752 | set_fw_info_part(info, &fi->cfg1); | |
753 | if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG1_ACTIVE) | |
754 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
755 | if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG1_RUNNING) | |
756 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
757 | break; | |
758 | case SWITCHTEC_IOCTL_PART_IMG0: | |
759 | set_fw_info_part(info, &fi->img0); | |
760 | if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG0_ACTIVE) | |
761 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
762 | if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG0_RUNNING) | |
763 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
764 | break; | |
765 | case SWITCHTEC_IOCTL_PART_IMG1: | |
766 | set_fw_info_part(info, &fi->img1); | |
767 | if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG1_ACTIVE) | |
768 | info->active |= SWITCHTEC_IOCTL_PART_ACTIVE; | |
769 | if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG1_RUNNING) | |
770 | info->active |= SWITCHTEC_IOCTL_PART_RUNNING; | |
771 | break; | |
772 | case SWITCHTEC_IOCTL_PART_NVLOG: | |
773 | set_fw_info_part(info, &fi->nvlog); | |
774 | break; | |
775 | case SWITCHTEC_IOCTL_PART_VENDOR0: | |
776 | set_fw_info_part(info, &fi->vendor[0]); | |
777 | break; | |
778 | case SWITCHTEC_IOCTL_PART_VENDOR1: | |
779 | set_fw_info_part(info, &fi->vendor[1]); | |
780 | break; | |
781 | case SWITCHTEC_IOCTL_PART_VENDOR2: | |
782 | set_fw_info_part(info, &fi->vendor[2]); | |
783 | break; | |
784 | case SWITCHTEC_IOCTL_PART_VENDOR3: | |
785 | set_fw_info_part(info, &fi->vendor[3]); | |
786 | break; | |
787 | case SWITCHTEC_IOCTL_PART_VENDOR4: | |
788 | set_fw_info_part(info, &fi->vendor[4]); | |
789 | break; | |
790 | case SWITCHTEC_IOCTL_PART_VENDOR5: | |
791 | set_fw_info_part(info, &fi->vendor[5]); | |
792 | break; | |
793 | case SWITCHTEC_IOCTL_PART_VENDOR6: | |
794 | set_fw_info_part(info, &fi->vendor[6]); | |
795 | break; | |
796 | case SWITCHTEC_IOCTL_PART_VENDOR7: | |
797 | set_fw_info_part(info, &fi->vendor[7]); | |
798 | break; | |
799 | default: | |
800 | return -EINVAL; | |
801 | } | |
802 | ||
803 | return 0; | |
804 | } | |
805 | ||
6a3d1b54 LG |
806 | static int ioctl_flash_part_info(struct switchtec_dev *stdev, |
807 | struct switchtec_ioctl_flash_part_info __user *uinfo) | |
808 | { | |
809 | int ret; | |
810 | struct switchtec_ioctl_flash_part_info info = {0}; | |
811 | ||
812 | if (copy_from_user(&info, uinfo, sizeof(info))) | |
813 | return -EFAULT; | |
814 | ||
815 | if (stdev->gen == SWITCHTEC_GEN3) { | |
816 | ret = flash_part_info_gen3(stdev, &info); | |
817 | if (ret) | |
818 | return ret; | |
4efa1d2e KC |
819 | } else if (stdev->gen == SWITCHTEC_GEN4) { |
820 | ret = flash_part_info_gen4(stdev, &info); | |
821 | if (ret) | |
822 | return ret; | |
6a3d1b54 LG |
823 | } else { |
824 | return -ENOTSUPP; | |
825 | } | |
52eabba5 LG |
826 | |
827 | if (copy_to_user(uinfo, &info, sizeof(info))) | |
828 | return -EFAULT; | |
829 | ||
830 | return 0; | |
831 | } | |
832 | ||
833 | static int ioctl_event_summary(struct switchtec_dev *stdev, | |
834 | struct switchtec_user *stuser, | |
ba8a3982 WS |
835 | struct switchtec_ioctl_event_summary __user *usum, |
836 | size_t size) | |
52eabba5 | 837 | { |
ba8a3982 | 838 | struct switchtec_ioctl_event_summary *s; |
52eabba5 LG |
839 | int i; |
840 | u32 reg; | |
ba8a3982 | 841 | int ret = 0; |
52eabba5 | 842 | |
ba8a3982 WS |
843 | s = kzalloc(sizeof(*s), GFP_KERNEL); |
844 | if (!s) | |
845 | return -ENOMEM; | |
846 | ||
847 | s->global = ioread32(&stdev->mmio_sw_event->global_summary); | |
6acdf7e1 | 848 | s->part_bitmap = ioread64(&stdev->mmio_sw_event->part_event_bitmap); |
ba8a3982 | 849 | s->local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary); |
52eabba5 LG |
850 | |
851 | for (i = 0; i < stdev->partition_count; i++) { | |
852 | reg = ioread32(&stdev->mmio_part_cfg_all[i].part_event_summary); | |
ba8a3982 | 853 | s->part[i] = reg; |
52eabba5 LG |
854 | } |
855 | ||
7501a02a | 856 | for (i = 0; i < stdev->pff_csr_count; i++) { |
52eabba5 | 857 | reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary); |
ba8a3982 | 858 | s->pff[i] = reg; |
52eabba5 LG |
859 | } |
860 | ||
ba8a3982 WS |
861 | if (copy_to_user(usum, s, size)) { |
862 | ret = -EFAULT; | |
863 | goto error_case; | |
864 | } | |
52eabba5 LG |
865 | |
866 | stuser->event_cnt = atomic_read(&stdev->event_cnt); | |
867 | ||
ba8a3982 WS |
868 | error_case: |
869 | kfree(s); | |
870 | return ret; | |
52eabba5 LG |
871 | } |
872 | ||
873 | static u32 __iomem *global_ev_reg(struct switchtec_dev *stdev, | |
874 | size_t offset, int index) | |
875 | { | |
876 | return (void __iomem *)stdev->mmio_sw_event + offset; | |
877 | } | |
878 | ||
879 | static u32 __iomem *part_ev_reg(struct switchtec_dev *stdev, | |
880 | size_t offset, int index) | |
881 | { | |
882 | return (void __iomem *)&stdev->mmio_part_cfg_all[index] + offset; | |
883 | } | |
884 | ||
885 | static u32 __iomem *pff_ev_reg(struct switchtec_dev *stdev, | |
886 | size_t offset, int index) | |
887 | { | |
888 | return (void __iomem *)&stdev->mmio_pff_csr[index] + offset; | |
889 | } | |
890 | ||
891 | #define EV_GLB(i, r)[i] = {offsetof(struct sw_event_regs, r), global_ev_reg} | |
892 | #define EV_PAR(i, r)[i] = {offsetof(struct part_cfg_regs, r), part_ev_reg} | |
893 | #define EV_PFF(i, r)[i] = {offsetof(struct pff_csr_regs, r), pff_ev_reg} | |
894 | ||
f05f7355 | 895 | static const struct event_reg { |
52eabba5 LG |
896 | size_t offset; |
897 | u32 __iomem *(*map_reg)(struct switchtec_dev *stdev, | |
898 | size_t offset, int index); | |
899 | } event_regs[] = { | |
900 | EV_GLB(SWITCHTEC_IOCTL_EVENT_STACK_ERROR, stack_error_event_hdr), | |
901 | EV_GLB(SWITCHTEC_IOCTL_EVENT_PPU_ERROR, ppu_error_event_hdr), | |
902 | EV_GLB(SWITCHTEC_IOCTL_EVENT_ISP_ERROR, isp_error_event_hdr), | |
903 | EV_GLB(SWITCHTEC_IOCTL_EVENT_SYS_RESET, sys_reset_event_hdr), | |
904 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_EXC, fw_exception_hdr), | |
905 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NMI, fw_nmi_hdr), | |
906 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NON_FATAL, fw_non_fatal_hdr), | |
907 | EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_FATAL, fw_fatal_hdr), | |
908 | EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP, twi_mrpc_comp_hdr), | |
909 | EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP_ASYNC, | |
910 | twi_mrpc_comp_async_hdr), | |
911 | EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP, cli_mrpc_comp_hdr), | |
912 | EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP_ASYNC, | |
913 | cli_mrpc_comp_async_hdr), | |
914 | EV_GLB(SWITCHTEC_IOCTL_EVENT_GPIO_INT, gpio_interrupt_hdr), | |
f0edce7a | 915 | EV_GLB(SWITCHTEC_IOCTL_EVENT_GFMS, gfms_event_hdr), |
52eabba5 LG |
916 | EV_PAR(SWITCHTEC_IOCTL_EVENT_PART_RESET, part_reset_hdr), |
917 | EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP, mrpc_comp_hdr), | |
918 | EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC, mrpc_comp_async_hdr), | |
919 | EV_PAR(SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP, dyn_binding_hdr), | |
a6b0ef9a LG |
920 | EV_PAR(SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY, |
921 | intercomm_notify_hdr), | |
52eabba5 LG |
922 | EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_P2P, aer_in_p2p_hdr), |
923 | EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_VEP, aer_in_vep_hdr), | |
924 | EV_PFF(SWITCHTEC_IOCTL_EVENT_DPC, dpc_hdr), | |
925 | EV_PFF(SWITCHTEC_IOCTL_EVENT_CTS, cts_hdr), | |
a6b0ef9a | 926 | EV_PFF(SWITCHTEC_IOCTL_EVENT_UEC, uec_hdr), |
52eabba5 LG |
927 | EV_PFF(SWITCHTEC_IOCTL_EVENT_HOTPLUG, hotplug_hdr), |
928 | EV_PFF(SWITCHTEC_IOCTL_EVENT_IER, ier_hdr), | |
929 | EV_PFF(SWITCHTEC_IOCTL_EVENT_THRESH, threshold_hdr), | |
930 | EV_PFF(SWITCHTEC_IOCTL_EVENT_POWER_MGMT, power_mgmt_hdr), | |
931 | EV_PFF(SWITCHTEC_IOCTL_EVENT_TLP_THROTTLING, tlp_throttling_hdr), | |
932 | EV_PFF(SWITCHTEC_IOCTL_EVENT_FORCE_SPEED, force_speed_hdr), | |
933 | EV_PFF(SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT, credit_timeout_hdr), | |
934 | EV_PFF(SWITCHTEC_IOCTL_EVENT_LINK_STATE, link_state_hdr), | |
935 | }; | |
936 | ||
937 | static u32 __iomem *event_hdr_addr(struct switchtec_dev *stdev, | |
938 | int event_id, int index) | |
939 | { | |
940 | size_t off; | |
941 | ||
942 | if (event_id < 0 || event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) | |
5f11723b | 943 | return (u32 __iomem *)ERR_PTR(-EINVAL); |
52eabba5 LG |
944 | |
945 | off = event_regs[event_id].offset; | |
946 | ||
947 | if (event_regs[event_id].map_reg == part_ev_reg) { | |
948 | if (index == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) | |
949 | index = stdev->partition; | |
950 | else if (index < 0 || index >= stdev->partition_count) | |
5f11723b | 951 | return (u32 __iomem *)ERR_PTR(-EINVAL); |
52eabba5 LG |
952 | } else if (event_regs[event_id].map_reg == pff_ev_reg) { |
953 | if (index < 0 || index >= stdev->pff_csr_count) | |
5f11723b | 954 | return (u32 __iomem *)ERR_PTR(-EINVAL); |
52eabba5 LG |
955 | } |
956 | ||
957 | return event_regs[event_id].map_reg(stdev, off, index); | |
958 | } | |
959 | ||
960 | static int event_ctl(struct switchtec_dev *stdev, | |
961 | struct switchtec_ioctl_event_ctl *ctl) | |
962 | { | |
963 | int i; | |
964 | u32 __iomem *reg; | |
965 | u32 hdr; | |
966 | ||
967 | reg = event_hdr_addr(stdev, ctl->event_id, ctl->index); | |
968 | if (IS_ERR(reg)) | |
969 | return PTR_ERR(reg); | |
970 | ||
971 | hdr = ioread32(reg); | |
972 | for (i = 0; i < ARRAY_SIZE(ctl->data); i++) | |
973 | ctl->data[i] = ioread32(®[i + 1]); | |
974 | ||
975 | ctl->occurred = hdr & SWITCHTEC_EVENT_OCCURRED; | |
976 | ctl->count = (hdr >> 5) & 0xFF; | |
977 | ||
978 | if (!(ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR)) | |
979 | hdr &= ~SWITCHTEC_EVENT_CLEAR; | |
980 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL) | |
981 | hdr |= SWITCHTEC_EVENT_EN_IRQ; | |
982 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL) | |
983 | hdr &= ~SWITCHTEC_EVENT_EN_IRQ; | |
984 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG) | |
985 | hdr |= SWITCHTEC_EVENT_EN_LOG; | |
986 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG) | |
987 | hdr &= ~SWITCHTEC_EVENT_EN_LOG; | |
988 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI) | |
989 | hdr |= SWITCHTEC_EVENT_EN_CLI; | |
990 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI) | |
991 | hdr &= ~SWITCHTEC_EVENT_EN_CLI; | |
992 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL) | |
993 | hdr |= SWITCHTEC_EVENT_FATAL; | |
994 | if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL) | |
995 | hdr &= ~SWITCHTEC_EVENT_FATAL; | |
996 | ||
997 | if (ctl->flags) | |
998 | iowrite32(hdr, reg); | |
999 | ||
1000 | ctl->flags = 0; | |
1001 | if (hdr & SWITCHTEC_EVENT_EN_IRQ) | |
1002 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL; | |
1003 | if (hdr & SWITCHTEC_EVENT_EN_LOG) | |
1004 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG; | |
1005 | if (hdr & SWITCHTEC_EVENT_EN_CLI) | |
1006 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI; | |
1007 | if (hdr & SWITCHTEC_EVENT_FATAL) | |
1008 | ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL; | |
1009 | ||
1010 | return 0; | |
1011 | } | |
1012 | ||
1013 | static int ioctl_event_ctl(struct switchtec_dev *stdev, | |
1014 | struct switchtec_ioctl_event_ctl __user *uctl) | |
1015 | { | |
1016 | int ret; | |
1017 | int nr_idxs; | |
e4a7dca5 | 1018 | unsigned int event_flags; |
52eabba5 LG |
1019 | struct switchtec_ioctl_event_ctl ctl; |
1020 | ||
1021 | if (copy_from_user(&ctl, uctl, sizeof(ctl))) | |
1022 | return -EFAULT; | |
1023 | ||
1024 | if (ctl.event_id >= SWITCHTEC_IOCTL_MAX_EVENTS) | |
1025 | return -EINVAL; | |
1026 | ||
1027 | if (ctl.flags & SWITCHTEC_IOCTL_EVENT_FLAG_UNUSED) | |
1028 | return -EINVAL; | |
1029 | ||
1030 | if (ctl.index == SWITCHTEC_IOCTL_EVENT_IDX_ALL) { | |
1031 | if (event_regs[ctl.event_id].map_reg == global_ev_reg) | |
1032 | nr_idxs = 1; | |
1033 | else if (event_regs[ctl.event_id].map_reg == part_ev_reg) | |
1034 | nr_idxs = stdev->partition_count; | |
1035 | else if (event_regs[ctl.event_id].map_reg == pff_ev_reg) | |
1036 | nr_idxs = stdev->pff_csr_count; | |
1037 | else | |
1038 | return -EINVAL; | |
1039 | ||
e4a7dca5 | 1040 | event_flags = ctl.flags; |
52eabba5 | 1041 | for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { |
e4a7dca5 | 1042 | ctl.flags = event_flags; |
52eabba5 LG |
1043 | ret = event_ctl(stdev, &ctl); |
1044 | if (ret < 0) | |
1045 | return ret; | |
1046 | } | |
1047 | } else { | |
1048 | ret = event_ctl(stdev, &ctl); | |
1049 | if (ret < 0) | |
1050 | return ret; | |
1051 | } | |
1052 | ||
1053 | if (copy_to_user(uctl, &ctl, sizeof(ctl))) | |
1054 | return -EFAULT; | |
1055 | ||
1056 | return 0; | |
1057 | } | |
1058 | ||
1059 | static int ioctl_pff_to_port(struct switchtec_dev *stdev, | |
5f11723b | 1060 | struct switchtec_ioctl_pff_port __user *up) |
52eabba5 LG |
1061 | { |
1062 | int i, part; | |
1063 | u32 reg; | |
5f11723b | 1064 | struct part_cfg_regs __iomem *pcfg; |
52eabba5 LG |
1065 | struct switchtec_ioctl_pff_port p; |
1066 | ||
1067 | if (copy_from_user(&p, up, sizeof(p))) | |
1068 | return -EFAULT; | |
1069 | ||
1070 | p.port = -1; | |
1071 | for (part = 0; part < stdev->partition_count; part++) { | |
1072 | pcfg = &stdev->mmio_part_cfg_all[part]; | |
1073 | p.partition = part; | |
1074 | ||
1075 | reg = ioread32(&pcfg->usp_pff_inst_id); | |
1076 | if (reg == p.pff) { | |
1077 | p.port = 0; | |
1078 | break; | |
1079 | } | |
1080 | ||
1081 | reg = ioread32(&pcfg->vep_pff_inst_id); | |
1082 | if (reg == p.pff) { | |
1083 | p.port = SWITCHTEC_IOCTL_PFF_VEP; | |
1084 | break; | |
1085 | } | |
1086 | ||
1087 | for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { | |
1088 | reg = ioread32(&pcfg->dsp_pff_inst_id[i]); | |
1089 | if (reg != p.pff) | |
1090 | continue; | |
1091 | ||
1092 | p.port = i + 1; | |
1093 | break; | |
1094 | } | |
1095 | ||
1096 | if (p.port != -1) | |
1097 | break; | |
1098 | } | |
1099 | ||
1100 | if (copy_to_user(up, &p, sizeof(p))) | |
1101 | return -EFAULT; | |
1102 | ||
1103 | return 0; | |
1104 | } | |
1105 | ||
1106 | static int ioctl_port_to_pff(struct switchtec_dev *stdev, | |
5f11723b | 1107 | struct switchtec_ioctl_pff_port __user *up) |
52eabba5 LG |
1108 | { |
1109 | struct switchtec_ioctl_pff_port p; | |
5f11723b | 1110 | struct part_cfg_regs __iomem *pcfg; |
52eabba5 LG |
1111 | |
1112 | if (copy_from_user(&p, up, sizeof(p))) | |
1113 | return -EFAULT; | |
1114 | ||
1115 | if (p.partition == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX) | |
1116 | pcfg = stdev->mmio_part_cfg; | |
1117 | else if (p.partition < stdev->partition_count) | |
1118 | pcfg = &stdev->mmio_part_cfg_all[p.partition]; | |
1119 | else | |
1120 | return -EINVAL; | |
1121 | ||
1122 | switch (p.port) { | |
1123 | case 0: | |
1124 | p.pff = ioread32(&pcfg->usp_pff_inst_id); | |
1125 | break; | |
1126 | case SWITCHTEC_IOCTL_PFF_VEP: | |
1127 | p.pff = ioread32(&pcfg->vep_pff_inst_id); | |
1128 | break; | |
1129 | default: | |
1130 | if (p.port > ARRAY_SIZE(pcfg->dsp_pff_inst_id)) | |
1131 | return -EINVAL; | |
46feb6b4 GS |
1132 | p.port = array_index_nospec(p.port, |
1133 | ARRAY_SIZE(pcfg->dsp_pff_inst_id) + 1); | |
52eabba5 LG |
1134 | p.pff = ioread32(&pcfg->dsp_pff_inst_id[p.port - 1]); |
1135 | break; | |
1136 | } | |
1137 | ||
1138 | if (copy_to_user(up, &p, sizeof(p))) | |
1139 | return -EFAULT; | |
1140 | ||
1141 | return 0; | |
1142 | } | |
1143 | ||
1144 | static long switchtec_dev_ioctl(struct file *filp, unsigned int cmd, | |
1145 | unsigned long arg) | |
1146 | { | |
1147 | struct switchtec_user *stuser = filp->private_data; | |
1148 | struct switchtec_dev *stdev = stuser->stdev; | |
1149 | int rc; | |
1150 | void __user *argp = (void __user *)arg; | |
1151 | ||
1152 | rc = lock_mutex_and_test_alive(stdev); | |
1153 | if (rc) | |
1154 | return rc; | |
1155 | ||
1156 | switch (cmd) { | |
1157 | case SWITCHTEC_IOCTL_FLASH_INFO: | |
1158 | rc = ioctl_flash_info(stdev, argp); | |
1159 | break; | |
1160 | case SWITCHTEC_IOCTL_FLASH_PART_INFO: | |
1161 | rc = ioctl_flash_part_info(stdev, argp); | |
1162 | break; | |
ba8a3982 WS |
1163 | case SWITCHTEC_IOCTL_EVENT_SUMMARY_LEGACY: |
1164 | rc = ioctl_event_summary(stdev, stuser, argp, | |
1165 | sizeof(struct switchtec_ioctl_event_summary_legacy)); | |
52eabba5 LG |
1166 | break; |
1167 | case SWITCHTEC_IOCTL_EVENT_CTL: | |
1168 | rc = ioctl_event_ctl(stdev, argp); | |
1169 | break; | |
1170 | case SWITCHTEC_IOCTL_PFF_TO_PORT: | |
1171 | rc = ioctl_pff_to_port(stdev, argp); | |
1172 | break; | |
1173 | case SWITCHTEC_IOCTL_PORT_TO_PFF: | |
1174 | rc = ioctl_port_to_pff(stdev, argp); | |
1175 | break; | |
ba8a3982 WS |
1176 | case SWITCHTEC_IOCTL_EVENT_SUMMARY: |
1177 | rc = ioctl_event_summary(stdev, stuser, argp, | |
1178 | sizeof(struct switchtec_ioctl_event_summary)); | |
1179 | break; | |
52eabba5 LG |
1180 | default: |
1181 | rc = -ENOTTY; | |
1182 | break; | |
1183 | } | |
1184 | ||
1185 | mutex_unlock(&stdev->mrpc_mutex); | |
1186 | return rc; | |
1187 | } | |
1188 | ||
080b47de LG |
1189 | static const struct file_operations switchtec_fops = { |
1190 | .owner = THIS_MODULE, | |
1191 | .open = switchtec_dev_open, | |
1192 | .release = switchtec_dev_release, | |
1193 | .write = switchtec_dev_write, | |
1194 | .read = switchtec_dev_read, | |
1195 | .poll = switchtec_dev_poll, | |
52eabba5 | 1196 | .unlocked_ioctl = switchtec_dev_ioctl, |
1832f2d8 | 1197 | .compat_ioctl = compat_ptr_ioctl, |
080b47de LG |
1198 | }; |
1199 | ||
48c302dc LG |
1200 | static void link_event_work(struct work_struct *work) |
1201 | { | |
1202 | struct switchtec_dev *stdev; | |
1203 | ||
1204 | stdev = container_of(work, struct switchtec_dev, link_event_work); | |
1205 | ||
1206 | if (stdev->link_notifier) | |
1207 | stdev->link_notifier(stdev); | |
1208 | } | |
1209 | ||
1210 | static void check_link_state_events(struct switchtec_dev *stdev) | |
1211 | { | |
1212 | int idx; | |
1213 | u32 reg; | |
1214 | int count; | |
1215 | int occurred = 0; | |
1216 | ||
1217 | for (idx = 0; idx < stdev->pff_csr_count; idx++) { | |
1218 | reg = ioread32(&stdev->mmio_pff_csr[idx].link_state_hdr); | |
1219 | dev_dbg(&stdev->dev, "link_state: %d->%08x\n", idx, reg); | |
1220 | count = (reg >> 5) & 0xFF; | |
1221 | ||
1222 | if (count != stdev->link_event_count[idx]) { | |
1223 | occurred = 1; | |
1224 | stdev->link_event_count[idx] = count; | |
1225 | } | |
1226 | } | |
1227 | ||
1228 | if (occurred) | |
1229 | schedule_work(&stdev->link_event_work); | |
1230 | } | |
1231 | ||
1232 | static void enable_link_state_events(struct switchtec_dev *stdev) | |
1233 | { | |
1234 | int idx; | |
1235 | ||
1236 | for (idx = 0; idx < stdev->pff_csr_count; idx++) { | |
1237 | iowrite32(SWITCHTEC_EVENT_CLEAR | | |
1238 | SWITCHTEC_EVENT_EN_IRQ, | |
1239 | &stdev->mmio_pff_csr[idx].link_state_hdr); | |
1240 | } | |
1241 | } | |
1242 | ||
f7eb7b8a WS |
1243 | static void enable_dma_mrpc(struct switchtec_dev *stdev) |
1244 | { | |
1245 | writeq(stdev->dma_mrpc_dma_addr, &stdev->mmio_mrpc->dma_addr); | |
1246 | flush_wc_buf(stdev); | |
1247 | iowrite32(SWITCHTEC_DMA_MRPC_EN, &stdev->mmio_mrpc->dma_en); | |
1248 | } | |
1249 | ||
080b47de LG |
1250 | static void stdev_release(struct device *dev) |
1251 | { | |
1252 | struct switchtec_dev *stdev = to_stdev(dev); | |
1253 | ||
f7eb7b8a WS |
1254 | if (stdev->dma_mrpc) { |
1255 | iowrite32(0, &stdev->mmio_mrpc->dma_en); | |
1256 | flush_wc_buf(stdev); | |
1257 | writeq(0, &stdev->mmio_mrpc->dma_addr); | |
1258 | dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc), | |
1259 | stdev->dma_mrpc, stdev->dma_mrpc_dma_addr); | |
1260 | } | |
080b47de LG |
1261 | kfree(stdev); |
1262 | } | |
1263 | ||
1264 | static void stdev_kill(struct switchtec_dev *stdev) | |
1265 | { | |
1266 | struct switchtec_user *stuser, *tmpuser; | |
1267 | ||
1268 | pci_clear_master(stdev->pdev); | |
1269 | ||
1270 | cancel_delayed_work_sync(&stdev->mrpc_timeout); | |
1271 | ||
1272 | /* Mark the hardware as unavailable and complete all completions */ | |
1273 | mutex_lock(&stdev->mrpc_mutex); | |
1274 | stdev->alive = false; | |
1275 | ||
1276 | /* Wake up and kill any users waiting on an MRPC request */ | |
1277 | list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) { | |
deaa0a8a SAS |
1278 | stuser->cmd_done = true; |
1279 | wake_up_interruptible(&stuser->cmd_comp); | |
080b47de LG |
1280 | list_del_init(&stuser->list); |
1281 | stuser_put(stuser); | |
1282 | } | |
1283 | ||
1284 | mutex_unlock(&stdev->mrpc_mutex); | |
1285 | ||
1286 | /* Wake up any users waiting on event_wq */ | |
1287 | wake_up_interruptible(&stdev->event_wq); | |
1288 | } | |
1289 | ||
1290 | static struct switchtec_dev *stdev_create(struct pci_dev *pdev) | |
1291 | { | |
1292 | struct switchtec_dev *stdev; | |
1293 | int minor; | |
1294 | struct device *dev; | |
1295 | struct cdev *cdev; | |
1296 | int rc; | |
1297 | ||
1298 | stdev = kzalloc_node(sizeof(*stdev), GFP_KERNEL, | |
1299 | dev_to_node(&pdev->dev)); | |
1300 | if (!stdev) | |
1301 | return ERR_PTR(-ENOMEM); | |
1302 | ||
1303 | stdev->alive = true; | |
1304 | stdev->pdev = pdev; | |
1305 | INIT_LIST_HEAD(&stdev->mrpc_queue); | |
1306 | mutex_init(&stdev->mrpc_mutex); | |
1307 | stdev->mrpc_busy = 0; | |
1308 | INIT_WORK(&stdev->mrpc_work, mrpc_event_work); | |
1309 | INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work); | |
48c302dc | 1310 | INIT_WORK(&stdev->link_event_work, link_event_work); |
080b47de LG |
1311 | init_waitqueue_head(&stdev->event_wq); |
1312 | atomic_set(&stdev->event_cnt, 0); | |
1313 | ||
1314 | dev = &stdev->dev; | |
1315 | device_initialize(dev); | |
1316 | dev->class = switchtec_class; | |
1317 | dev->parent = &pdev->dev; | |
5d8e1881 | 1318 | dev->groups = switchtec_device_groups; |
080b47de LG |
1319 | dev->release = stdev_release; |
1320 | ||
1321 | minor = ida_simple_get(&switchtec_minor_ida, 0, 0, | |
1322 | GFP_KERNEL); | |
1323 | if (minor < 0) { | |
1324 | rc = minor; | |
1325 | goto err_put; | |
1326 | } | |
1327 | ||
1328 | dev->devt = MKDEV(MAJOR(switchtec_devt), minor); | |
1329 | dev_set_name(dev, "switchtec%d", minor); | |
1330 | ||
1331 | cdev = &stdev->cdev; | |
1332 | cdev_init(cdev, &switchtec_fops); | |
1333 | cdev->owner = THIS_MODULE; | |
080b47de LG |
1334 | |
1335 | return stdev; | |
1336 | ||
1337 | err_put: | |
1338 | put_device(&stdev->dev); | |
1339 | return ERR_PTR(rc); | |
1340 | } | |
1341 | ||
52eabba5 LG |
1342 | static int mask_event(struct switchtec_dev *stdev, int eid, int idx) |
1343 | { | |
1344 | size_t off = event_regs[eid].offset; | |
1345 | u32 __iomem *hdr_reg; | |
1346 | u32 hdr; | |
1347 | ||
1348 | hdr_reg = event_regs[eid].map_reg(stdev, off, idx); | |
1349 | hdr = ioread32(hdr_reg); | |
1350 | ||
1351 | if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) | |
1352 | return 0; | |
1353 | ||
1354 | dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr); | |
1355 | hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED); | |
1356 | iowrite32(hdr, hdr_reg); | |
1357 | ||
1358 | return 1; | |
1359 | } | |
1360 | ||
1361 | static int mask_all_events(struct switchtec_dev *stdev, int eid) | |
1362 | { | |
1363 | int idx; | |
1364 | int count = 0; | |
1365 | ||
1366 | if (event_regs[eid].map_reg == part_ev_reg) { | |
1367 | for (idx = 0; idx < stdev->partition_count; idx++) | |
1368 | count += mask_event(stdev, eid, idx); | |
1369 | } else if (event_regs[eid].map_reg == pff_ev_reg) { | |
1370 | for (idx = 0; idx < stdev->pff_csr_count; idx++) { | |
1371 | if (!stdev->pff_local[idx]) | |
1372 | continue; | |
48c302dc | 1373 | |
52eabba5 LG |
1374 | count += mask_event(stdev, eid, idx); |
1375 | } | |
1376 | } else { | |
1377 | count += mask_event(stdev, eid, 0); | |
1378 | } | |
1379 | ||
1380 | return count; | |
1381 | } | |
1382 | ||
080b47de LG |
1383 | static irqreturn_t switchtec_event_isr(int irq, void *dev) |
1384 | { | |
1385 | struct switchtec_dev *stdev = dev; | |
1386 | u32 reg; | |
1387 | irqreturn_t ret = IRQ_NONE; | |
52eabba5 | 1388 | int eid, event_count = 0; |
080b47de LG |
1389 | |
1390 | reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr); | |
1391 | if (reg & SWITCHTEC_EVENT_OCCURRED) { | |
1392 | dev_dbg(&stdev->dev, "%s: mrpc comp\n", __func__); | |
1393 | ret = IRQ_HANDLED; | |
1394 | schedule_work(&stdev->mrpc_work); | |
1395 | iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); | |
1396 | } | |
1397 | ||
48c302dc LG |
1398 | check_link_state_events(stdev); |
1399 | ||
2085747d WS |
1400 | for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) { |
1401 | if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE || | |
1402 | eid == SWITCHTEC_IOCTL_EVENT_MRPC_COMP) | |
1403 | continue; | |
1404 | ||
52eabba5 | 1405 | event_count += mask_all_events(stdev, eid); |
2085747d | 1406 | } |
52eabba5 LG |
1407 | |
1408 | if (event_count) { | |
1409 | atomic_inc(&stdev->event_cnt); | |
1410 | wake_up_interruptible(&stdev->event_wq); | |
1411 | dev_dbg(&stdev->dev, "%s: %d events\n", __func__, | |
1412 | event_count); | |
1413 | return IRQ_HANDLED; | |
1414 | } | |
1415 | ||
080b47de LG |
1416 | return ret; |
1417 | } | |
1418 | ||
f7eb7b8a WS |
1419 | |
1420 | static irqreturn_t switchtec_dma_mrpc_isr(int irq, void *dev) | |
1421 | { | |
1422 | struct switchtec_dev *stdev = dev; | |
1423 | irqreturn_t ret = IRQ_NONE; | |
1424 | ||
1425 | iowrite32(SWITCHTEC_EVENT_CLEAR | | |
1426 | SWITCHTEC_EVENT_EN_IRQ, | |
1427 | &stdev->mmio_part_cfg->mrpc_comp_hdr); | |
1428 | schedule_work(&stdev->mrpc_work); | |
1429 | ||
1430 | ret = IRQ_HANDLED; | |
1431 | return ret; | |
1432 | } | |
1433 | ||
080b47de LG |
1434 | static int switchtec_init_isr(struct switchtec_dev *stdev) |
1435 | { | |
1436 | int nvecs; | |
1437 | int event_irq; | |
f7eb7b8a WS |
1438 | int dma_mrpc_irq; |
1439 | int rc; | |
080b47de | 1440 | |
fcdf8e95 LG |
1441 | if (nirqs < 4) |
1442 | nirqs = 4; | |
1443 | ||
1444 | nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, nirqs, | |
1445 | PCI_IRQ_MSIX | PCI_IRQ_MSI | | |
1446 | PCI_IRQ_VIRTUAL); | |
080b47de LG |
1447 | if (nvecs < 0) |
1448 | return nvecs; | |
1449 | ||
9375646b | 1450 | event_irq = ioread16(&stdev->mmio_part_cfg->vep_vector_number); |
080b47de LG |
1451 | if (event_irq < 0 || event_irq >= nvecs) |
1452 | return -EFAULT; | |
1453 | ||
1454 | event_irq = pci_irq_vector(stdev->pdev, event_irq); | |
1455 | if (event_irq < 0) | |
1456 | return event_irq; | |
1457 | ||
f7eb7b8a | 1458 | rc = devm_request_irq(&stdev->pdev->dev, event_irq, |
080b47de LG |
1459 | switchtec_event_isr, 0, |
1460 | KBUILD_MODNAME, stdev); | |
f7eb7b8a WS |
1461 | |
1462 | if (rc) | |
1463 | return rc; | |
1464 | ||
1465 | if (!stdev->dma_mrpc) | |
1466 | return rc; | |
1467 | ||
1468 | dma_mrpc_irq = ioread32(&stdev->mmio_mrpc->dma_vector); | |
1469 | if (dma_mrpc_irq < 0 || dma_mrpc_irq >= nvecs) | |
1470 | return -EFAULT; | |
1471 | ||
1472 | dma_mrpc_irq = pci_irq_vector(stdev->pdev, dma_mrpc_irq); | |
1473 | if (dma_mrpc_irq < 0) | |
1474 | return dma_mrpc_irq; | |
1475 | ||
1476 | rc = devm_request_irq(&stdev->pdev->dev, dma_mrpc_irq, | |
1477 | switchtec_dma_mrpc_isr, 0, | |
1478 | KBUILD_MODNAME, stdev); | |
1479 | ||
1480 | return rc; | |
080b47de LG |
1481 | } |
1482 | ||
1483 | static void init_pff(struct switchtec_dev *stdev) | |
1484 | { | |
1485 | int i; | |
1486 | u32 reg; | |
42dae893 | 1487 | struct part_cfg_regs __iomem *pcfg = stdev->mmio_part_cfg; |
080b47de LG |
1488 | |
1489 | for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { | |
1490 | reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id); | |
cfdfc14e | 1491 | if (reg != PCI_VENDOR_ID_MICROSEMI) |
080b47de LG |
1492 | break; |
1493 | } | |
1494 | ||
1495 | stdev->pff_csr_count = i; | |
1496 | ||
1497 | reg = ioread32(&pcfg->usp_pff_inst_id); | |
7501a02a | 1498 | if (reg < stdev->pff_csr_count) |
080b47de LG |
1499 | stdev->pff_local[reg] = 1; |
1500 | ||
1501 | reg = ioread32(&pcfg->vep_pff_inst_id); | |
7501a02a | 1502 | if (reg < stdev->pff_csr_count) |
080b47de LG |
1503 | stdev->pff_local[reg] = 1; |
1504 | ||
1505 | for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { | |
1506 | reg = ioread32(&pcfg->dsp_pff_inst_id[i]); | |
7501a02a | 1507 | if (reg < stdev->pff_csr_count) |
080b47de LG |
1508 | stdev->pff_local[reg] = 1; |
1509 | } | |
1510 | } | |
1511 | ||
1512 | static int switchtec_init_pci(struct switchtec_dev *stdev, | |
1513 | struct pci_dev *pdev) | |
1514 | { | |
1515 | int rc; | |
52d8db8e KC |
1516 | void __iomem *map; |
1517 | unsigned long res_start, res_len; | |
993d208d | 1518 | u32 __iomem *part_id; |
080b47de LG |
1519 | |
1520 | rc = pcim_enable_device(pdev); | |
1521 | if (rc) | |
1522 | return rc; | |
1523 | ||
aa82130a | 1524 | rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); |
aff614c6 BG |
1525 | if (rc) |
1526 | return rc; | |
1527 | ||
080b47de LG |
1528 | pci_set_master(pdev); |
1529 | ||
52d8db8e KC |
1530 | res_start = pci_resource_start(pdev, 0); |
1531 | res_len = pci_resource_len(pdev, 0); | |
1532 | ||
1533 | if (!devm_request_mem_region(&pdev->dev, res_start, | |
1534 | res_len, KBUILD_MODNAME)) | |
1535 | return -EBUSY; | |
1536 | ||
1537 | stdev->mmio_mrpc = devm_ioremap_wc(&pdev->dev, res_start, | |
1538 | SWITCHTEC_GAS_TOP_CFG_OFFSET); | |
1539 | if (!stdev->mmio_mrpc) | |
1540 | return -ENOMEM; | |
1541 | ||
1542 | map = devm_ioremap(&pdev->dev, | |
1543 | res_start + SWITCHTEC_GAS_TOP_CFG_OFFSET, | |
1544 | res_len - SWITCHTEC_GAS_TOP_CFG_OFFSET); | |
1545 | if (!map) | |
1546 | return -ENOMEM; | |
1547 | ||
1548 | stdev->mmio = map - SWITCHTEC_GAS_TOP_CFG_OFFSET; | |
080b47de LG |
1549 | stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET; |
1550 | stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; | |
1551 | stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; | |
1552 | stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET; | |
993d208d LG |
1553 | |
1554 | if (stdev->gen == SWITCHTEC_GEN3) | |
1555 | part_id = &stdev->mmio_sys_info->gen3.partition_id; | |
a3321ca3 LG |
1556 | else if (stdev->gen == SWITCHTEC_GEN4) |
1557 | part_id = &stdev->mmio_sys_info->gen4.partition_id; | |
993d208d LG |
1558 | else |
1559 | return -ENOTSUPP; | |
1560 | ||
1561 | stdev->partition = ioread8(part_id); | |
080b47de LG |
1562 | stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count); |
1563 | stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET; | |
1564 | stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition]; | |
1565 | stdev->mmio_pff_csr = stdev->mmio + SWITCHTEC_GAS_PFF_CSR_OFFSET; | |
1566 | ||
9871e9bb LG |
1567 | if (stdev->partition_count < 1) |
1568 | stdev->partition_count = 1; | |
1569 | ||
080b47de LG |
1570 | init_pff(stdev); |
1571 | ||
1572 | pci_set_drvdata(pdev, stdev); | |
1573 | ||
f7eb7b8a WS |
1574 | if (!use_dma_mrpc) |
1575 | return 0; | |
1576 | ||
1577 | if (ioread32(&stdev->mmio_mrpc->dma_ver) == 0) | |
1578 | return 0; | |
1579 | ||
750afb08 LC |
1580 | stdev->dma_mrpc = dma_alloc_coherent(&stdev->pdev->dev, |
1581 | sizeof(*stdev->dma_mrpc), | |
1582 | &stdev->dma_mrpc_dma_addr, | |
1583 | GFP_KERNEL); | |
f7eb7b8a WS |
1584 | if (stdev->dma_mrpc == NULL) |
1585 | return -ENOMEM; | |
1586 | ||
080b47de LG |
1587 | return 0; |
1588 | } | |
1589 | ||
1590 | static int switchtec_pci_probe(struct pci_dev *pdev, | |
1591 | const struct pci_device_id *id) | |
1592 | { | |
1593 | struct switchtec_dev *stdev; | |
1594 | int rc; | |
1595 | ||
cfdfc14e | 1596 | if (pdev->class == (PCI_CLASS_BRIDGE_OTHER << 8)) |
33dea5aa LG |
1597 | request_module_nowait("ntb_hw_switchtec"); |
1598 | ||
080b47de LG |
1599 | stdev = stdev_create(pdev); |
1600 | if (IS_ERR(stdev)) | |
1601 | return PTR_ERR(stdev); | |
1602 | ||
b13313a0 LG |
1603 | stdev->gen = id->driver_data; |
1604 | ||
080b47de LG |
1605 | rc = switchtec_init_pci(stdev, pdev); |
1606 | if (rc) | |
1607 | goto err_put; | |
1608 | ||
1609 | rc = switchtec_init_isr(stdev); | |
1610 | if (rc) { | |
1611 | dev_err(&stdev->dev, "failed to init isr.\n"); | |
1612 | goto err_put; | |
1613 | } | |
1614 | ||
1615 | iowrite32(SWITCHTEC_EVENT_CLEAR | | |
1616 | SWITCHTEC_EVENT_EN_IRQ, | |
1617 | &stdev->mmio_part_cfg->mrpc_comp_hdr); | |
48c302dc | 1618 | enable_link_state_events(stdev); |
080b47de | 1619 | |
f7eb7b8a WS |
1620 | if (stdev->dma_mrpc) |
1621 | enable_dma_mrpc(stdev); | |
1622 | ||
e40cf640 | 1623 | rc = cdev_device_add(&stdev->cdev, &stdev->dev); |
080b47de LG |
1624 | if (rc) |
1625 | goto err_devadd; | |
1626 | ||
1627 | dev_info(&stdev->dev, "Management device registered.\n"); | |
1628 | ||
1629 | return 0; | |
1630 | ||
1631 | err_devadd: | |
080b47de LG |
1632 | stdev_kill(stdev); |
1633 | err_put: | |
1634 | ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); | |
1635 | put_device(&stdev->dev); | |
1636 | return rc; | |
1637 | } | |
1638 | ||
1639 | static void switchtec_pci_remove(struct pci_dev *pdev) | |
1640 | { | |
1641 | struct switchtec_dev *stdev = pci_get_drvdata(pdev); | |
1642 | ||
1643 | pci_set_drvdata(pdev, NULL); | |
1644 | ||
e40cf640 | 1645 | cdev_device_del(&stdev->cdev, &stdev->dev); |
080b47de LG |
1646 | ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); |
1647 | dev_info(&stdev->dev, "unregistered.\n"); | |
080b47de LG |
1648 | stdev_kill(stdev); |
1649 | put_device(&stdev->dev); | |
1650 | } | |
1651 | ||
b13313a0 | 1652 | #define SWITCHTEC_PCI_DEVICE(device_id, gen) \ |
080b47de | 1653 | { \ |
cfdfc14e | 1654 | .vendor = PCI_VENDOR_ID_MICROSEMI, \ |
080b47de LG |
1655 | .device = device_id, \ |
1656 | .subvendor = PCI_ANY_ID, \ | |
1657 | .subdevice = PCI_ANY_ID, \ | |
cfdfc14e | 1658 | .class = (PCI_CLASS_MEMORY_OTHER << 8), \ |
080b47de | 1659 | .class_mask = 0xFFFFFFFF, \ |
b13313a0 | 1660 | .driver_data = gen, \ |
080b47de LG |
1661 | }, \ |
1662 | { \ | |
cfdfc14e | 1663 | .vendor = PCI_VENDOR_ID_MICROSEMI, \ |
080b47de LG |
1664 | .device = device_id, \ |
1665 | .subvendor = PCI_ANY_ID, \ | |
1666 | .subdevice = PCI_ANY_ID, \ | |
cfdfc14e | 1667 | .class = (PCI_CLASS_BRIDGE_OTHER << 8), \ |
080b47de | 1668 | .class_mask = 0xFFFFFFFF, \ |
b13313a0 | 1669 | .driver_data = gen, \ |
080b47de LG |
1670 | } |
1671 | ||
1672 | static const struct pci_device_id switchtec_pci_tbl[] = { | |
b13313a0 LG |
1673 | SWITCHTEC_PCI_DEVICE(0x8531, SWITCHTEC_GEN3), //PFX 24xG3 |
1674 | SWITCHTEC_PCI_DEVICE(0x8532, SWITCHTEC_GEN3), //PFX 32xG3 | |
1675 | SWITCHTEC_PCI_DEVICE(0x8533, SWITCHTEC_GEN3), //PFX 48xG3 | |
1676 | SWITCHTEC_PCI_DEVICE(0x8534, SWITCHTEC_GEN3), //PFX 64xG3 | |
1677 | SWITCHTEC_PCI_DEVICE(0x8535, SWITCHTEC_GEN3), //PFX 80xG3 | |
1678 | SWITCHTEC_PCI_DEVICE(0x8536, SWITCHTEC_GEN3), //PFX 96xG3 | |
1679 | SWITCHTEC_PCI_DEVICE(0x8541, SWITCHTEC_GEN3), //PSX 24xG3 | |
1680 | SWITCHTEC_PCI_DEVICE(0x8542, SWITCHTEC_GEN3), //PSX 32xG3 | |
1681 | SWITCHTEC_PCI_DEVICE(0x8543, SWITCHTEC_GEN3), //PSX 48xG3 | |
1682 | SWITCHTEC_PCI_DEVICE(0x8544, SWITCHTEC_GEN3), //PSX 64xG3 | |
1683 | SWITCHTEC_PCI_DEVICE(0x8545, SWITCHTEC_GEN3), //PSX 80xG3 | |
1684 | SWITCHTEC_PCI_DEVICE(0x8546, SWITCHTEC_GEN3), //PSX 96xG3 | |
1685 | SWITCHTEC_PCI_DEVICE(0x8551, SWITCHTEC_GEN3), //PAX 24XG3 | |
1686 | SWITCHTEC_PCI_DEVICE(0x8552, SWITCHTEC_GEN3), //PAX 32XG3 | |
1687 | SWITCHTEC_PCI_DEVICE(0x8553, SWITCHTEC_GEN3), //PAX 48XG3 | |
1688 | SWITCHTEC_PCI_DEVICE(0x8554, SWITCHTEC_GEN3), //PAX 64XG3 | |
1689 | SWITCHTEC_PCI_DEVICE(0x8555, SWITCHTEC_GEN3), //PAX 80XG3 | |
1690 | SWITCHTEC_PCI_DEVICE(0x8556, SWITCHTEC_GEN3), //PAX 96XG3 | |
1691 | SWITCHTEC_PCI_DEVICE(0x8561, SWITCHTEC_GEN3), //PFXL 24XG3 | |
1692 | SWITCHTEC_PCI_DEVICE(0x8562, SWITCHTEC_GEN3), //PFXL 32XG3 | |
1693 | SWITCHTEC_PCI_DEVICE(0x8563, SWITCHTEC_GEN3), //PFXL 48XG3 | |
1694 | SWITCHTEC_PCI_DEVICE(0x8564, SWITCHTEC_GEN3), //PFXL 64XG3 | |
1695 | SWITCHTEC_PCI_DEVICE(0x8565, SWITCHTEC_GEN3), //PFXL 80XG3 | |
1696 | SWITCHTEC_PCI_DEVICE(0x8566, SWITCHTEC_GEN3), //PFXL 96XG3 | |
1697 | SWITCHTEC_PCI_DEVICE(0x8571, SWITCHTEC_GEN3), //PFXI 24XG3 | |
1698 | SWITCHTEC_PCI_DEVICE(0x8572, SWITCHTEC_GEN3), //PFXI 32XG3 | |
1699 | SWITCHTEC_PCI_DEVICE(0x8573, SWITCHTEC_GEN3), //PFXI 48XG3 | |
1700 | SWITCHTEC_PCI_DEVICE(0x8574, SWITCHTEC_GEN3), //PFXI 64XG3 | |
1701 | SWITCHTEC_PCI_DEVICE(0x8575, SWITCHTEC_GEN3), //PFXI 80XG3 | |
1702 | SWITCHTEC_PCI_DEVICE(0x8576, SWITCHTEC_GEN3), //PFXI 96XG3 | |
7a30ebb9 KC |
1703 | SWITCHTEC_PCI_DEVICE(0x4000, SWITCHTEC_GEN4), //PFX 100XG4 |
1704 | SWITCHTEC_PCI_DEVICE(0x4084, SWITCHTEC_GEN4), //PFX 84XG4 | |
1705 | SWITCHTEC_PCI_DEVICE(0x4068, SWITCHTEC_GEN4), //PFX 68XG4 | |
1706 | SWITCHTEC_PCI_DEVICE(0x4052, SWITCHTEC_GEN4), //PFX 52XG4 | |
1707 | SWITCHTEC_PCI_DEVICE(0x4036, SWITCHTEC_GEN4), //PFX 36XG4 | |
1708 | SWITCHTEC_PCI_DEVICE(0x4028, SWITCHTEC_GEN4), //PFX 28XG4 | |
1709 | SWITCHTEC_PCI_DEVICE(0x4100, SWITCHTEC_GEN4), //PSX 100XG4 | |
1710 | SWITCHTEC_PCI_DEVICE(0x4184, SWITCHTEC_GEN4), //PSX 84XG4 | |
1711 | SWITCHTEC_PCI_DEVICE(0x4168, SWITCHTEC_GEN4), //PSX 68XG4 | |
1712 | SWITCHTEC_PCI_DEVICE(0x4152, SWITCHTEC_GEN4), //PSX 52XG4 | |
1713 | SWITCHTEC_PCI_DEVICE(0x4136, SWITCHTEC_GEN4), //PSX 36XG4 | |
1714 | SWITCHTEC_PCI_DEVICE(0x4128, SWITCHTEC_GEN4), //PSX 28XG4 | |
1715 | SWITCHTEC_PCI_DEVICE(0x4200, SWITCHTEC_GEN4), //PAX 100XG4 | |
1716 | SWITCHTEC_PCI_DEVICE(0x4284, SWITCHTEC_GEN4), //PAX 84XG4 | |
1717 | SWITCHTEC_PCI_DEVICE(0x4268, SWITCHTEC_GEN4), //PAX 68XG4 | |
1718 | SWITCHTEC_PCI_DEVICE(0x4252, SWITCHTEC_GEN4), //PAX 52XG4 | |
1719 | SWITCHTEC_PCI_DEVICE(0x4236, SWITCHTEC_GEN4), //PAX 36XG4 | |
1720 | SWITCHTEC_PCI_DEVICE(0x4228, SWITCHTEC_GEN4), //PAX 28XG4 | |
080b47de LG |
1721 | {0} |
1722 | }; | |
1723 | MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl); | |
1724 | ||
1725 | static struct pci_driver switchtec_pci_driver = { | |
1726 | .name = KBUILD_MODNAME, | |
1727 | .id_table = switchtec_pci_tbl, | |
1728 | .probe = switchtec_pci_probe, | |
1729 | .remove = switchtec_pci_remove, | |
1730 | }; | |
1731 | ||
1732 | static int __init switchtec_init(void) | |
1733 | { | |
1734 | int rc; | |
1735 | ||
1736 | rc = alloc_chrdev_region(&switchtec_devt, 0, max_devices, | |
1737 | "switchtec"); | |
1738 | if (rc) | |
1739 | return rc; | |
1740 | ||
1741 | switchtec_class = class_create(THIS_MODULE, "switchtec"); | |
1742 | if (IS_ERR(switchtec_class)) { | |
1743 | rc = PTR_ERR(switchtec_class); | |
1744 | goto err_create_class; | |
1745 | } | |
1746 | ||
1747 | rc = pci_register_driver(&switchtec_pci_driver); | |
1748 | if (rc) | |
1749 | goto err_pci_register; | |
1750 | ||
1751 | pr_info(KBUILD_MODNAME ": loaded.\n"); | |
1752 | ||
1753 | return 0; | |
1754 | ||
1755 | err_pci_register: | |
1756 | class_destroy(switchtec_class); | |
1757 | ||
1758 | err_create_class: | |
1759 | unregister_chrdev_region(switchtec_devt, max_devices); | |
1760 | ||
1761 | return rc; | |
1762 | } | |
1763 | module_init(switchtec_init); | |
1764 | ||
1765 | static void __exit switchtec_exit(void) | |
1766 | { | |
1767 | pci_unregister_driver(&switchtec_pci_driver); | |
1768 | class_destroy(switchtec_class); | |
1769 | unregister_chrdev_region(switchtec_devt, max_devices); | |
1770 | ida_destroy(&switchtec_minor_ida); | |
1771 | ||
1772 | pr_info(KBUILD_MODNAME ": unloaded.\n"); | |
1773 | } | |
1774 | module_exit(switchtec_exit); |