diff options
author | Logan Gunthorpe <logang@deltatee.com> | 2017-03-06 19:30:54 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-03-06 19:33:34 -0500 |
commit | 080b47def5e5e28b2509c5bb92160d1df730f27b (patch) | |
tree | 72a20cad07f6cad6ddd437af46616fd53e0f78c5 /drivers/pci/switch/switchtec.c | |
parent | c1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201 (diff) |
MicroSemi Switchtec management interface driver
Microsemi's "Switchtec" line of PCI switch devices is already well
supported by the kernel with standard PCI switch drivers. However, the
Switchtec device advertises a special management endpoint with a separate
PCI function address and class code. This endpoint enables some additional
functionality which includes:
* Packet and Byte Counters
* Switch Firmware Upgrades
* Event and Error logs
* Querying port link status
* Custom user firmware commands
Add a switchtec kernel module which provides PCI driver that exposes a char
device. The char device provides userspace access to this interface
through read, write and (optionally) poll calls.
A userspace tool and library which utilizes this interface is available
at [1]. This tool takes inspiration (and borrows some code) from
nvme-cli [2]. The tool is largely complete at this time but additional
features may be added in the future.
[1] https://github.com/sbates130272/switchtec-user
[2] https://github.com/linux-nvme/nvme-cli
[Dan Carpenter <dan.carpenter@oracle.com>: don't invert error codes]
[Christophe JAILLET <christophe.jaillet@wanadoo.fr>: fix
switchtec_dev_open() error handling]
Tested-by: Krishna Dhulipala <krishnad@fb.com>
Signed-off-by: Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Stephen Bates <stephen.bates@microsemi.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Wei Zhang <wzhang@fb.com>
Reviewed-by: Jens Axboe <axboe@fb.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/pci/switch/switchtec.c')
-rw-r--r-- | drivers/pci/switch/switchtec.c | 1006 |
1 files changed, 1006 insertions, 0 deletions
diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c new file mode 100644 index 000000000000..0bbe90df968d --- /dev/null +++ b/drivers/pci/switch/switchtec.c | |||
@@ -0,0 +1,1006 @@ | |||
1 | /* | ||
2 | * Microsemi Switchtec(tm) PCIe Management Driver | ||
3 | * Copyright (c) 2017, Microsemi Corporation | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/uaccess.h> | ||
20 | #include <linux/poll.h> | ||
21 | #include <linux/pci.h> | ||
22 | #include <linux/cdev.h> | ||
23 | #include <linux/wait.h> | ||
24 | |||
25 | MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); | ||
26 | MODULE_VERSION("0.1"); | ||
27 | MODULE_LICENSE("GPL"); | ||
28 | MODULE_AUTHOR("Microsemi Corporation"); | ||
29 | |||
30 | static int max_devices = 16; | ||
31 | module_param(max_devices, int, 0644); | ||
32 | MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); | ||
33 | |||
34 | static dev_t switchtec_devt; | ||
35 | static struct class *switchtec_class; | ||
36 | static DEFINE_IDA(switchtec_minor_ida); | ||
37 | |||
38 | #define MICROSEMI_VENDOR_ID 0x11f8 | ||
39 | #define MICROSEMI_NTB_CLASSCODE 0x068000 | ||
40 | #define MICROSEMI_MGMT_CLASSCODE 0x058000 | ||
41 | |||
42 | #define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024 | ||
43 | #define SWITCHTEC_MAX_PFF_CSR 48 | ||
44 | |||
45 | #define SWITCHTEC_EVENT_OCCURRED BIT(0) | ||
46 | #define SWITCHTEC_EVENT_CLEAR BIT(0) | ||
47 | #define SWITCHTEC_EVENT_EN_LOG BIT(1) | ||
48 | #define SWITCHTEC_EVENT_EN_CLI BIT(2) | ||
49 | #define SWITCHTEC_EVENT_EN_IRQ BIT(3) | ||
50 | #define SWITCHTEC_EVENT_FATAL BIT(4) | ||
51 | |||
52 | enum { | ||
53 | SWITCHTEC_GAS_MRPC_OFFSET = 0x0000, | ||
54 | SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000, | ||
55 | SWITCHTEC_GAS_SW_EVENT_OFFSET = 0x1800, | ||
56 | SWITCHTEC_GAS_SYS_INFO_OFFSET = 0x2000, | ||
57 | SWITCHTEC_GAS_FLASH_INFO_OFFSET = 0x2200, | ||
58 | SWITCHTEC_GAS_PART_CFG_OFFSET = 0x4000, | ||
59 | SWITCHTEC_GAS_NTB_OFFSET = 0x10000, | ||
60 | SWITCHTEC_GAS_PFF_CSR_OFFSET = 0x134000, | ||
61 | }; | ||
62 | |||
63 | struct mrpc_regs { | ||
64 | u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; | ||
65 | u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; | ||
66 | u32 cmd; | ||
67 | u32 status; | ||
68 | u32 ret_value; | ||
69 | } __packed; | ||
70 | |||
71 | enum mrpc_status { | ||
72 | SWITCHTEC_MRPC_STATUS_INPROGRESS = 1, | ||
73 | SWITCHTEC_MRPC_STATUS_DONE = 2, | ||
74 | SWITCHTEC_MRPC_STATUS_ERROR = 0xFF, | ||
75 | SWITCHTEC_MRPC_STATUS_INTERRUPTED = 0x100, | ||
76 | }; | ||
77 | |||
78 | struct sw_event_regs { | ||
79 | u64 event_report_ctrl; | ||
80 | u64 reserved1; | ||
81 | u64 part_event_bitmap; | ||
82 | u64 reserved2; | ||
83 | u32 global_summary; | ||
84 | u32 reserved3[3]; | ||
85 | u32 stack_error_event_hdr; | ||
86 | u32 stack_error_event_data; | ||
87 | u32 reserved4[4]; | ||
88 | u32 ppu_error_event_hdr; | ||
89 | u32 ppu_error_event_data; | ||
90 | u32 reserved5[4]; | ||
91 | u32 isp_error_event_hdr; | ||
92 | u32 isp_error_event_data; | ||
93 | u32 reserved6[4]; | ||
94 | u32 sys_reset_event_hdr; | ||
95 | u32 reserved7[5]; | ||
96 | u32 fw_exception_hdr; | ||
97 | u32 reserved8[5]; | ||
98 | u32 fw_nmi_hdr; | ||
99 | u32 reserved9[5]; | ||
100 | u32 fw_non_fatal_hdr; | ||
101 | u32 reserved10[5]; | ||
102 | u32 fw_fatal_hdr; | ||
103 | u32 reserved11[5]; | ||
104 | u32 twi_mrpc_comp_hdr; | ||
105 | u32 twi_mrpc_comp_data; | ||
106 | u32 reserved12[4]; | ||
107 | u32 twi_mrpc_comp_async_hdr; | ||
108 | u32 twi_mrpc_comp_async_data; | ||
109 | u32 reserved13[4]; | ||
110 | u32 cli_mrpc_comp_hdr; | ||
111 | u32 cli_mrpc_comp_data; | ||
112 | u32 reserved14[4]; | ||
113 | u32 cli_mrpc_comp_async_hdr; | ||
114 | u32 cli_mrpc_comp_async_data; | ||
115 | u32 reserved15[4]; | ||
116 | u32 gpio_interrupt_hdr; | ||
117 | u32 gpio_interrupt_data; | ||
118 | u32 reserved16[4]; | ||
119 | } __packed; | ||
120 | |||
121 | struct sys_info_regs { | ||
122 | u32 device_id; | ||
123 | u32 device_version; | ||
124 | u32 firmware_version; | ||
125 | u32 reserved1; | ||
126 | u32 vendor_table_revision; | ||
127 | u32 table_format_version; | ||
128 | u32 partition_id; | ||
129 | u32 cfg_file_fmt_version; | ||
130 | u32 reserved2[58]; | ||
131 | char vendor_id[8]; | ||
132 | char product_id[16]; | ||
133 | char product_revision[4]; | ||
134 | char component_vendor[8]; | ||
135 | u16 component_id; | ||
136 | u8 component_revision; | ||
137 | } __packed; | ||
138 | |||
139 | struct flash_info_regs { | ||
140 | u32 flash_part_map_upd_idx; | ||
141 | |||
142 | struct active_partition_info { | ||
143 | u32 address; | ||
144 | u32 build_version; | ||
145 | u32 build_string; | ||
146 | } active_img; | ||
147 | |||
148 | struct active_partition_info active_cfg; | ||
149 | struct active_partition_info inactive_img; | ||
150 | struct active_partition_info inactive_cfg; | ||
151 | |||
152 | u32 flash_length; | ||
153 | |||
154 | struct partition_info { | ||
155 | u32 address; | ||
156 | u32 length; | ||
157 | } cfg0; | ||
158 | |||
159 | struct partition_info cfg1; | ||
160 | struct partition_info img0; | ||
161 | struct partition_info img1; | ||
162 | struct partition_info nvlog; | ||
163 | struct partition_info vendor[8]; | ||
164 | }; | ||
165 | |||
166 | struct ntb_info_regs { | ||
167 | u8 partition_count; | ||
168 | u8 partition_id; | ||
169 | u16 reserved1; | ||
170 | u64 ep_map; | ||
171 | u16 requester_id; | ||
172 | } __packed; | ||
173 | |||
174 | struct part_cfg_regs { | ||
175 | u32 status; | ||
176 | u32 state; | ||
177 | u32 port_cnt; | ||
178 | u32 usp_port_mode; | ||
179 | u32 usp_pff_inst_id; | ||
180 | u32 vep_pff_inst_id; | ||
181 | u32 dsp_pff_inst_id[47]; | ||
182 | u32 reserved1[11]; | ||
183 | u16 vep_vector_number; | ||
184 | u16 usp_vector_number; | ||
185 | u32 port_event_bitmap; | ||
186 | u32 reserved2[3]; | ||
187 | u32 part_event_summary; | ||
188 | u32 reserved3[3]; | ||
189 | u32 part_reset_hdr; | ||
190 | u32 part_reset_data[5]; | ||
191 | u32 mrpc_comp_hdr; | ||
192 | u32 mrpc_comp_data[5]; | ||
193 | u32 mrpc_comp_async_hdr; | ||
194 | u32 mrpc_comp_async_data[5]; | ||
195 | u32 dyn_binding_hdr; | ||
196 | u32 dyn_binding_data[5]; | ||
197 | u32 reserved4[159]; | ||
198 | } __packed; | ||
199 | |||
200 | enum { | ||
201 | SWITCHTEC_PART_CFG_EVENT_RESET = 1 << 0, | ||
202 | SWITCHTEC_PART_CFG_EVENT_MRPC_CMP = 1 << 1, | ||
203 | SWITCHTEC_PART_CFG_EVENT_MRPC_ASYNC_CMP = 1 << 2, | ||
204 | SWITCHTEC_PART_CFG_EVENT_DYN_PART_CMP = 1 << 3, | ||
205 | }; | ||
206 | |||
207 | struct pff_csr_regs { | ||
208 | u16 vendor_id; | ||
209 | u16 device_id; | ||
210 | u32 pci_cfg_header[15]; | ||
211 | u32 pci_cap_region[48]; | ||
212 | u32 pcie_cap_region[448]; | ||
213 | u32 indirect_gas_window[128]; | ||
214 | u32 indirect_gas_window_off; | ||
215 | u32 reserved[127]; | ||
216 | u32 pff_event_summary; | ||
217 | u32 reserved2[3]; | ||
218 | u32 aer_in_p2p_hdr; | ||
219 | u32 aer_in_p2p_data[5]; | ||
220 | u32 aer_in_vep_hdr; | ||
221 | u32 aer_in_vep_data[5]; | ||
222 | u32 dpc_hdr; | ||
223 | u32 dpc_data[5]; | ||
224 | u32 cts_hdr; | ||
225 | u32 cts_data[5]; | ||
226 | u32 reserved3[6]; | ||
227 | u32 hotplug_hdr; | ||
228 | u32 hotplug_data[5]; | ||
229 | u32 ier_hdr; | ||
230 | u32 ier_data[5]; | ||
231 | u32 threshold_hdr; | ||
232 | u32 threshold_data[5]; | ||
233 | u32 power_mgmt_hdr; | ||
234 | u32 power_mgmt_data[5]; | ||
235 | u32 tlp_throttling_hdr; | ||
236 | u32 tlp_throttling_data[5]; | ||
237 | u32 force_speed_hdr; | ||
238 | u32 force_speed_data[5]; | ||
239 | u32 credit_timeout_hdr; | ||
240 | u32 credit_timeout_data[5]; | ||
241 | u32 link_state_hdr; | ||
242 | u32 link_state_data[5]; | ||
243 | u32 reserved4[174]; | ||
244 | } __packed; | ||
245 | |||
246 | struct switchtec_dev { | ||
247 | struct pci_dev *pdev; | ||
248 | struct device dev; | ||
249 | struct cdev cdev; | ||
250 | |||
251 | int partition; | ||
252 | int partition_count; | ||
253 | int pff_csr_count; | ||
254 | char pff_local[SWITCHTEC_MAX_PFF_CSR]; | ||
255 | |||
256 | void __iomem *mmio; | ||
257 | struct mrpc_regs __iomem *mmio_mrpc; | ||
258 | struct sw_event_regs __iomem *mmio_sw_event; | ||
259 | struct sys_info_regs __iomem *mmio_sys_info; | ||
260 | struct flash_info_regs __iomem *mmio_flash_info; | ||
261 | struct ntb_info_regs __iomem *mmio_ntb; | ||
262 | struct part_cfg_regs __iomem *mmio_part_cfg; | ||
263 | struct part_cfg_regs __iomem *mmio_part_cfg_all; | ||
264 | struct pff_csr_regs __iomem *mmio_pff_csr; | ||
265 | |||
266 | /* | ||
267 | * The mrpc mutex must be held when accessing the other | ||
268 | * mrpc_ fields, alive flag and stuser->state field | ||
269 | */ | ||
270 | struct mutex mrpc_mutex; | ||
271 | struct list_head mrpc_queue; | ||
272 | int mrpc_busy; | ||
273 | struct work_struct mrpc_work; | ||
274 | struct delayed_work mrpc_timeout; | ||
275 | bool alive; | ||
276 | |||
277 | wait_queue_head_t event_wq; | ||
278 | atomic_t event_cnt; | ||
279 | }; | ||
280 | |||
281 | static struct switchtec_dev *to_stdev(struct device *dev) | ||
282 | { | ||
283 | return container_of(dev, struct switchtec_dev, dev); | ||
284 | } | ||
285 | |||
286 | enum mrpc_state { | ||
287 | MRPC_IDLE = 0, | ||
288 | MRPC_QUEUED, | ||
289 | MRPC_RUNNING, | ||
290 | MRPC_DONE, | ||
291 | }; | ||
292 | |||
293 | struct switchtec_user { | ||
294 | struct switchtec_dev *stdev; | ||
295 | |||
296 | enum mrpc_state state; | ||
297 | |||
298 | struct completion comp; | ||
299 | struct kref kref; | ||
300 | struct list_head list; | ||
301 | |||
302 | u32 cmd; | ||
303 | u32 status; | ||
304 | u32 return_code; | ||
305 | size_t data_len; | ||
306 | size_t read_len; | ||
307 | unsigned char data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; | ||
308 | int event_cnt; | ||
309 | }; | ||
310 | |||
311 | static struct switchtec_user *stuser_create(struct switchtec_dev *stdev) | ||
312 | { | ||
313 | struct switchtec_user *stuser; | ||
314 | |||
315 | stuser = kzalloc(sizeof(*stuser), GFP_KERNEL); | ||
316 | if (!stuser) | ||
317 | return ERR_PTR(-ENOMEM); | ||
318 | |||
319 | get_device(&stdev->dev); | ||
320 | stuser->stdev = stdev; | ||
321 | kref_init(&stuser->kref); | ||
322 | INIT_LIST_HEAD(&stuser->list); | ||
323 | init_completion(&stuser->comp); | ||
324 | stuser->event_cnt = atomic_read(&stdev->event_cnt); | ||
325 | |||
326 | dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser); | ||
327 | |||
328 | return stuser; | ||
329 | } | ||
330 | |||
331 | static void stuser_free(struct kref *kref) | ||
332 | { | ||
333 | struct switchtec_user *stuser; | ||
334 | |||
335 | stuser = container_of(kref, struct switchtec_user, kref); | ||
336 | |||
337 | dev_dbg(&stuser->stdev->dev, "%s: %p\n", __func__, stuser); | ||
338 | |||
339 | put_device(&stuser->stdev->dev); | ||
340 | kfree(stuser); | ||
341 | } | ||
342 | |||
343 | static void stuser_put(struct switchtec_user *stuser) | ||
344 | { | ||
345 | kref_put(&stuser->kref, stuser_free); | ||
346 | } | ||
347 | |||
348 | static void stuser_set_state(struct switchtec_user *stuser, | ||
349 | enum mrpc_state state) | ||
350 | { | ||
351 | /* requires the mrpc_mutex to already be held when called */ | ||
352 | |||
353 | const char * const state_names[] = { | ||
354 | [MRPC_IDLE] = "IDLE", | ||
355 | [MRPC_QUEUED] = "QUEUED", | ||
356 | [MRPC_RUNNING] = "RUNNING", | ||
357 | [MRPC_DONE] = "DONE", | ||
358 | }; | ||
359 | |||
360 | stuser->state = state; | ||
361 | |||
362 | dev_dbg(&stuser->stdev->dev, "stuser state %p -> %s", | ||
363 | stuser, state_names[state]); | ||
364 | } | ||
365 | |||
366 | static void mrpc_complete_cmd(struct switchtec_dev *stdev); | ||
367 | |||
368 | static void mrpc_cmd_submit(struct switchtec_dev *stdev) | ||
369 | { | ||
370 | /* requires the mrpc_mutex to already be held when called */ | ||
371 | |||
372 | struct switchtec_user *stuser; | ||
373 | |||
374 | if (stdev->mrpc_busy) | ||
375 | return; | ||
376 | |||
377 | if (list_empty(&stdev->mrpc_queue)) | ||
378 | return; | ||
379 | |||
380 | stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, | ||
381 | list); | ||
382 | |||
383 | stuser_set_state(stuser, MRPC_RUNNING); | ||
384 | stdev->mrpc_busy = 1; | ||
385 | memcpy_toio(&stdev->mmio_mrpc->input_data, | ||
386 | stuser->data, stuser->data_len); | ||
387 | iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd); | ||
388 | |||
389 | stuser->status = ioread32(&stdev->mmio_mrpc->status); | ||
390 | if (stuser->status != SWITCHTEC_MRPC_STATUS_INPROGRESS) | ||
391 | mrpc_complete_cmd(stdev); | ||
392 | |||
393 | schedule_delayed_work(&stdev->mrpc_timeout, | ||
394 | msecs_to_jiffies(500)); | ||
395 | } | ||
396 | |||
397 | static int mrpc_queue_cmd(struct switchtec_user *stuser) | ||
398 | { | ||
399 | /* requires the mrpc_mutex to already be held when called */ | ||
400 | |||
401 | struct switchtec_dev *stdev = stuser->stdev; | ||
402 | |||
403 | kref_get(&stuser->kref); | ||
404 | stuser->read_len = sizeof(stuser->data); | ||
405 | stuser_set_state(stuser, MRPC_QUEUED); | ||
406 | init_completion(&stuser->comp); | ||
407 | list_add_tail(&stuser->list, &stdev->mrpc_queue); | ||
408 | |||
409 | mrpc_cmd_submit(stdev); | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static void mrpc_complete_cmd(struct switchtec_dev *stdev) | ||
415 | { | ||
416 | /* requires the mrpc_mutex to already be held when called */ | ||
417 | struct switchtec_user *stuser; | ||
418 | |||
419 | if (list_empty(&stdev->mrpc_queue)) | ||
420 | return; | ||
421 | |||
422 | stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, | ||
423 | list); | ||
424 | |||
425 | stuser->status = ioread32(&stdev->mmio_mrpc->status); | ||
426 | if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS) | ||
427 | return; | ||
428 | |||
429 | stuser_set_state(stuser, MRPC_DONE); | ||
430 | stuser->return_code = 0; | ||
431 | |||
432 | if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE) | ||
433 | goto out; | ||
434 | |||
435 | stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value); | ||
436 | if (stuser->return_code != 0) | ||
437 | goto out; | ||
438 | |||
439 | memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, | ||
440 | stuser->read_len); | ||
441 | |||
442 | out: | ||
443 | complete_all(&stuser->comp); | ||
444 | list_del_init(&stuser->list); | ||
445 | stuser_put(stuser); | ||
446 | stdev->mrpc_busy = 0; | ||
447 | |||
448 | mrpc_cmd_submit(stdev); | ||
449 | } | ||
450 | |||
451 | static void mrpc_event_work(struct work_struct *work) | ||
452 | { | ||
453 | struct switchtec_dev *stdev; | ||
454 | |||
455 | stdev = container_of(work, struct switchtec_dev, mrpc_work); | ||
456 | |||
457 | dev_dbg(&stdev->dev, "%s\n", __func__); | ||
458 | |||
459 | mutex_lock(&stdev->mrpc_mutex); | ||
460 | cancel_delayed_work(&stdev->mrpc_timeout); | ||
461 | mrpc_complete_cmd(stdev); | ||
462 | mutex_unlock(&stdev->mrpc_mutex); | ||
463 | } | ||
464 | |||
465 | static void mrpc_timeout_work(struct work_struct *work) | ||
466 | { | ||
467 | struct switchtec_dev *stdev; | ||
468 | u32 status; | ||
469 | |||
470 | stdev = container_of(work, struct switchtec_dev, mrpc_timeout.work); | ||
471 | |||
472 | dev_dbg(&stdev->dev, "%s\n", __func__); | ||
473 | |||
474 | mutex_lock(&stdev->mrpc_mutex); | ||
475 | |||
476 | status = ioread32(&stdev->mmio_mrpc->status); | ||
477 | if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) { | ||
478 | schedule_delayed_work(&stdev->mrpc_timeout, | ||
479 | msecs_to_jiffies(500)); | ||
480 | goto out; | ||
481 | } | ||
482 | |||
483 | mrpc_complete_cmd(stdev); | ||
484 | |||
485 | out: | ||
486 | mutex_unlock(&stdev->mrpc_mutex); | ||
487 | } | ||
488 | |||
489 | static int switchtec_dev_open(struct inode *inode, struct file *filp) | ||
490 | { | ||
491 | struct switchtec_dev *stdev; | ||
492 | struct switchtec_user *stuser; | ||
493 | |||
494 | stdev = container_of(inode->i_cdev, struct switchtec_dev, cdev); | ||
495 | |||
496 | stuser = stuser_create(stdev); | ||
497 | if (IS_ERR(stuser)) | ||
498 | return PTR_ERR(stuser); | ||
499 | |||
500 | filp->private_data = stuser; | ||
501 | nonseekable_open(inode, filp); | ||
502 | |||
503 | dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser); | ||
504 | |||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | static int switchtec_dev_release(struct inode *inode, struct file *filp) | ||
509 | { | ||
510 | struct switchtec_user *stuser = filp->private_data; | ||
511 | |||
512 | stuser_put(stuser); | ||
513 | |||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | static int lock_mutex_and_test_alive(struct switchtec_dev *stdev) | ||
518 | { | ||
519 | if (mutex_lock_interruptible(&stdev->mrpc_mutex)) | ||
520 | return -EINTR; | ||
521 | |||
522 | if (!stdev->alive) { | ||
523 | mutex_unlock(&stdev->mrpc_mutex); | ||
524 | return -ENODEV; | ||
525 | } | ||
526 | |||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static ssize_t switchtec_dev_write(struct file *filp, const char __user *data, | ||
531 | size_t size, loff_t *off) | ||
532 | { | ||
533 | struct switchtec_user *stuser = filp->private_data; | ||
534 | struct switchtec_dev *stdev = stuser->stdev; | ||
535 | int rc; | ||
536 | |||
537 | if (size < sizeof(stuser->cmd) || | ||
538 | size > sizeof(stuser->cmd) + sizeof(stuser->data)) | ||
539 | return -EINVAL; | ||
540 | |||
541 | stuser->data_len = size - sizeof(stuser->cmd); | ||
542 | |||
543 | rc = lock_mutex_and_test_alive(stdev); | ||
544 | if (rc) | ||
545 | return rc; | ||
546 | |||
547 | if (stuser->state != MRPC_IDLE) { | ||
548 | rc = -EBADE; | ||
549 | goto out; | ||
550 | } | ||
551 | |||
552 | rc = copy_from_user(&stuser->cmd, data, sizeof(stuser->cmd)); | ||
553 | if (rc) { | ||
554 | rc = -EFAULT; | ||
555 | goto out; | ||
556 | } | ||
557 | |||
558 | data += sizeof(stuser->cmd); | ||
559 | rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd)); | ||
560 | if (rc) { | ||
561 | rc = -EFAULT; | ||
562 | goto out; | ||
563 | } | ||
564 | |||
565 | rc = mrpc_queue_cmd(stuser); | ||
566 | |||
567 | out: | ||
568 | mutex_unlock(&stdev->mrpc_mutex); | ||
569 | |||
570 | if (rc) | ||
571 | return rc; | ||
572 | |||
573 | return size; | ||
574 | } | ||
575 | |||
576 | static ssize_t switchtec_dev_read(struct file *filp, char __user *data, | ||
577 | size_t size, loff_t *off) | ||
578 | { | ||
579 | struct switchtec_user *stuser = filp->private_data; | ||
580 | struct switchtec_dev *stdev = stuser->stdev; | ||
581 | int rc; | ||
582 | |||
583 | if (size < sizeof(stuser->cmd) || | ||
584 | size > sizeof(stuser->cmd) + sizeof(stuser->data)) | ||
585 | return -EINVAL; | ||
586 | |||
587 | rc = lock_mutex_and_test_alive(stdev); | ||
588 | if (rc) | ||
589 | return rc; | ||
590 | |||
591 | if (stuser->state == MRPC_IDLE) { | ||
592 | mutex_unlock(&stdev->mrpc_mutex); | ||
593 | return -EBADE; | ||
594 | } | ||
595 | |||
596 | stuser->read_len = size - sizeof(stuser->return_code); | ||
597 | |||
598 | mutex_unlock(&stdev->mrpc_mutex); | ||
599 | |||
600 | if (filp->f_flags & O_NONBLOCK) { | ||
601 | if (!try_wait_for_completion(&stuser->comp)) | ||
602 | return -EAGAIN; | ||
603 | } else { | ||
604 | rc = wait_for_completion_interruptible(&stuser->comp); | ||
605 | if (rc < 0) | ||
606 | return rc; | ||
607 | } | ||
608 | |||
609 | rc = lock_mutex_and_test_alive(stdev); | ||
610 | if (rc) | ||
611 | return rc; | ||
612 | |||
613 | if (stuser->state != MRPC_DONE) { | ||
614 | mutex_unlock(&stdev->mrpc_mutex); | ||
615 | return -EBADE; | ||
616 | } | ||
617 | |||
618 | rc = copy_to_user(data, &stuser->return_code, | ||
619 | sizeof(stuser->return_code)); | ||
620 | if (rc) { | ||
621 | rc = -EFAULT; | ||
622 | goto out; | ||
623 | } | ||
624 | |||
625 | data += sizeof(stuser->return_code); | ||
626 | rc = copy_to_user(data, &stuser->data, | ||
627 | size - sizeof(stuser->return_code)); | ||
628 | if (rc) { | ||
629 | rc = -EFAULT; | ||
630 | goto out; | ||
631 | } | ||
632 | |||
633 | stuser_set_state(stuser, MRPC_IDLE); | ||
634 | |||
635 | out: | ||
636 | mutex_unlock(&stdev->mrpc_mutex); | ||
637 | |||
638 | if (stuser->status == SWITCHTEC_MRPC_STATUS_DONE) | ||
639 | return size; | ||
640 | else if (stuser->status == SWITCHTEC_MRPC_STATUS_INTERRUPTED) | ||
641 | return -ENXIO; | ||
642 | else | ||
643 | return -EBADMSG; | ||
644 | } | ||
645 | |||
646 | static unsigned int switchtec_dev_poll(struct file *filp, poll_table *wait) | ||
647 | { | ||
648 | struct switchtec_user *stuser = filp->private_data; | ||
649 | struct switchtec_dev *stdev = stuser->stdev; | ||
650 | int ret = 0; | ||
651 | |||
652 | poll_wait(filp, &stuser->comp.wait, wait); | ||
653 | poll_wait(filp, &stdev->event_wq, wait); | ||
654 | |||
655 | if (lock_mutex_and_test_alive(stdev)) | ||
656 | return POLLIN | POLLRDHUP | POLLOUT | POLLERR | POLLHUP; | ||
657 | |||
658 | mutex_unlock(&stdev->mrpc_mutex); | ||
659 | |||
660 | if (try_wait_for_completion(&stuser->comp)) | ||
661 | ret |= POLLIN | POLLRDNORM; | ||
662 | |||
663 | if (stuser->event_cnt != atomic_read(&stdev->event_cnt)) | ||
664 | ret |= POLLPRI | POLLRDBAND; | ||
665 | |||
666 | return ret; | ||
667 | } | ||
668 | |||
669 | static const struct file_operations switchtec_fops = { | ||
670 | .owner = THIS_MODULE, | ||
671 | .open = switchtec_dev_open, | ||
672 | .release = switchtec_dev_release, | ||
673 | .write = switchtec_dev_write, | ||
674 | .read = switchtec_dev_read, | ||
675 | .poll = switchtec_dev_poll, | ||
676 | }; | ||
677 | |||
678 | static void stdev_release(struct device *dev) | ||
679 | { | ||
680 | struct switchtec_dev *stdev = to_stdev(dev); | ||
681 | |||
682 | kfree(stdev); | ||
683 | } | ||
684 | |||
685 | static void stdev_kill(struct switchtec_dev *stdev) | ||
686 | { | ||
687 | struct switchtec_user *stuser, *tmpuser; | ||
688 | |||
689 | pci_clear_master(stdev->pdev); | ||
690 | |||
691 | cancel_delayed_work_sync(&stdev->mrpc_timeout); | ||
692 | |||
693 | /* Mark the hardware as unavailable and complete all completions */ | ||
694 | mutex_lock(&stdev->mrpc_mutex); | ||
695 | stdev->alive = false; | ||
696 | |||
697 | /* Wake up and kill any users waiting on an MRPC request */ | ||
698 | list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) { | ||
699 | complete_all(&stuser->comp); | ||
700 | list_del_init(&stuser->list); | ||
701 | stuser_put(stuser); | ||
702 | } | ||
703 | |||
704 | mutex_unlock(&stdev->mrpc_mutex); | ||
705 | |||
706 | /* Wake up any users waiting on event_wq */ | ||
707 | wake_up_interruptible(&stdev->event_wq); | ||
708 | } | ||
709 | |||
710 | static struct switchtec_dev *stdev_create(struct pci_dev *pdev) | ||
711 | { | ||
712 | struct switchtec_dev *stdev; | ||
713 | int minor; | ||
714 | struct device *dev; | ||
715 | struct cdev *cdev; | ||
716 | int rc; | ||
717 | |||
718 | stdev = kzalloc_node(sizeof(*stdev), GFP_KERNEL, | ||
719 | dev_to_node(&pdev->dev)); | ||
720 | if (!stdev) | ||
721 | return ERR_PTR(-ENOMEM); | ||
722 | |||
723 | stdev->alive = true; | ||
724 | stdev->pdev = pdev; | ||
725 | INIT_LIST_HEAD(&stdev->mrpc_queue); | ||
726 | mutex_init(&stdev->mrpc_mutex); | ||
727 | stdev->mrpc_busy = 0; | ||
728 | INIT_WORK(&stdev->mrpc_work, mrpc_event_work); | ||
729 | INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work); | ||
730 | init_waitqueue_head(&stdev->event_wq); | ||
731 | atomic_set(&stdev->event_cnt, 0); | ||
732 | |||
733 | dev = &stdev->dev; | ||
734 | device_initialize(dev); | ||
735 | dev->class = switchtec_class; | ||
736 | dev->parent = &pdev->dev; | ||
737 | dev->release = stdev_release; | ||
738 | |||
739 | minor = ida_simple_get(&switchtec_minor_ida, 0, 0, | ||
740 | GFP_KERNEL); | ||
741 | if (minor < 0) { | ||
742 | rc = minor; | ||
743 | goto err_put; | ||
744 | } | ||
745 | |||
746 | dev->devt = MKDEV(MAJOR(switchtec_devt), minor); | ||
747 | dev_set_name(dev, "switchtec%d", minor); | ||
748 | |||
749 | cdev = &stdev->cdev; | ||
750 | cdev_init(cdev, &switchtec_fops); | ||
751 | cdev->owner = THIS_MODULE; | ||
752 | cdev->kobj.parent = &dev->kobj; | ||
753 | |||
754 | return stdev; | ||
755 | |||
756 | err_put: | ||
757 | put_device(&stdev->dev); | ||
758 | return ERR_PTR(rc); | ||
759 | } | ||
760 | |||
761 | static irqreturn_t switchtec_event_isr(int irq, void *dev) | ||
762 | { | ||
763 | struct switchtec_dev *stdev = dev; | ||
764 | u32 reg; | ||
765 | irqreturn_t ret = IRQ_NONE; | ||
766 | |||
767 | reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr); | ||
768 | if (reg & SWITCHTEC_EVENT_OCCURRED) { | ||
769 | dev_dbg(&stdev->dev, "%s: mrpc comp\n", __func__); | ||
770 | ret = IRQ_HANDLED; | ||
771 | schedule_work(&stdev->mrpc_work); | ||
772 | iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); | ||
773 | } | ||
774 | |||
775 | return ret; | ||
776 | } | ||
777 | |||
778 | static int switchtec_init_isr(struct switchtec_dev *stdev) | ||
779 | { | ||
780 | int nvecs; | ||
781 | int event_irq; | ||
782 | |||
783 | nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4, | ||
784 | PCI_IRQ_MSIX | PCI_IRQ_MSI); | ||
785 | if (nvecs < 0) | ||
786 | return nvecs; | ||
787 | |||
788 | event_irq = ioread32(&stdev->mmio_part_cfg->vep_vector_number); | ||
789 | if (event_irq < 0 || event_irq >= nvecs) | ||
790 | return -EFAULT; | ||
791 | |||
792 | event_irq = pci_irq_vector(stdev->pdev, event_irq); | ||
793 | if (event_irq < 0) | ||
794 | return event_irq; | ||
795 | |||
796 | return devm_request_irq(&stdev->pdev->dev, event_irq, | ||
797 | switchtec_event_isr, 0, | ||
798 | KBUILD_MODNAME, stdev); | ||
799 | } | ||
800 | |||
801 | static void init_pff(struct switchtec_dev *stdev) | ||
802 | { | ||
803 | int i; | ||
804 | u32 reg; | ||
805 | struct part_cfg_regs *pcfg = stdev->mmio_part_cfg; | ||
806 | |||
807 | for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) { | ||
808 | reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id); | ||
809 | if (reg != MICROSEMI_VENDOR_ID) | ||
810 | break; | ||
811 | } | ||
812 | |||
813 | stdev->pff_csr_count = i; | ||
814 | |||
815 | reg = ioread32(&pcfg->usp_pff_inst_id); | ||
816 | if (reg < SWITCHTEC_MAX_PFF_CSR) | ||
817 | stdev->pff_local[reg] = 1; | ||
818 | |||
819 | reg = ioread32(&pcfg->vep_pff_inst_id); | ||
820 | if (reg < SWITCHTEC_MAX_PFF_CSR) | ||
821 | stdev->pff_local[reg] = 1; | ||
822 | |||
823 | for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) { | ||
824 | reg = ioread32(&pcfg->dsp_pff_inst_id[i]); | ||
825 | if (reg < SWITCHTEC_MAX_PFF_CSR) | ||
826 | stdev->pff_local[reg] = 1; | ||
827 | } | ||
828 | } | ||
829 | |||
830 | static int switchtec_init_pci(struct switchtec_dev *stdev, | ||
831 | struct pci_dev *pdev) | ||
832 | { | ||
833 | int rc; | ||
834 | |||
835 | rc = pcim_enable_device(pdev); | ||
836 | if (rc) | ||
837 | return rc; | ||
838 | |||
839 | rc = pcim_iomap_regions(pdev, 0x1, KBUILD_MODNAME); | ||
840 | if (rc) | ||
841 | return rc; | ||
842 | |||
843 | pci_set_master(pdev); | ||
844 | |||
845 | stdev->mmio = pcim_iomap_table(pdev)[0]; | ||
846 | stdev->mmio_mrpc = stdev->mmio + SWITCHTEC_GAS_MRPC_OFFSET; | ||
847 | stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET; | ||
848 | stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; | ||
849 | stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; | ||
850 | stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET; | ||
851 | stdev->partition = ioread8(&stdev->mmio_ntb->partition_id); | ||
852 | stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count); | ||
853 | stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET; | ||
854 | stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition]; | ||
855 | stdev->mmio_pff_csr = stdev->mmio + SWITCHTEC_GAS_PFF_CSR_OFFSET; | ||
856 | |||
857 | init_pff(stdev); | ||
858 | |||
859 | pci_set_drvdata(pdev, stdev); | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static int switchtec_pci_probe(struct pci_dev *pdev, | ||
865 | const struct pci_device_id *id) | ||
866 | { | ||
867 | struct switchtec_dev *stdev; | ||
868 | int rc; | ||
869 | |||
870 | stdev = stdev_create(pdev); | ||
871 | if (IS_ERR(stdev)) | ||
872 | return PTR_ERR(stdev); | ||
873 | |||
874 | rc = switchtec_init_pci(stdev, pdev); | ||
875 | if (rc) | ||
876 | goto err_put; | ||
877 | |||
878 | rc = switchtec_init_isr(stdev); | ||
879 | if (rc) { | ||
880 | dev_err(&stdev->dev, "failed to init isr.\n"); | ||
881 | goto err_put; | ||
882 | } | ||
883 | |||
884 | iowrite32(SWITCHTEC_EVENT_CLEAR | | ||
885 | SWITCHTEC_EVENT_EN_IRQ, | ||
886 | &stdev->mmio_part_cfg->mrpc_comp_hdr); | ||
887 | |||
888 | rc = cdev_add(&stdev->cdev, stdev->dev.devt, 1); | ||
889 | if (rc) | ||
890 | goto err_put; | ||
891 | |||
892 | rc = device_add(&stdev->dev); | ||
893 | if (rc) | ||
894 | goto err_devadd; | ||
895 | |||
896 | dev_info(&stdev->dev, "Management device registered.\n"); | ||
897 | |||
898 | return 0; | ||
899 | |||
900 | err_devadd: | ||
901 | cdev_del(&stdev->cdev); | ||
902 | stdev_kill(stdev); | ||
903 | err_put: | ||
904 | ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); | ||
905 | put_device(&stdev->dev); | ||
906 | return rc; | ||
907 | } | ||
908 | |||
909 | static void switchtec_pci_remove(struct pci_dev *pdev) | ||
910 | { | ||
911 | struct switchtec_dev *stdev = pci_get_drvdata(pdev); | ||
912 | |||
913 | pci_set_drvdata(pdev, NULL); | ||
914 | |||
915 | device_del(&stdev->dev); | ||
916 | cdev_del(&stdev->cdev); | ||
917 | ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); | ||
918 | dev_info(&stdev->dev, "unregistered.\n"); | ||
919 | |||
920 | stdev_kill(stdev); | ||
921 | put_device(&stdev->dev); | ||
922 | } | ||
923 | |||
924 | #define SWITCHTEC_PCI_DEVICE(device_id) \ | ||
925 | { \ | ||
926 | .vendor = MICROSEMI_VENDOR_ID, \ | ||
927 | .device = device_id, \ | ||
928 | .subvendor = PCI_ANY_ID, \ | ||
929 | .subdevice = PCI_ANY_ID, \ | ||
930 | .class = MICROSEMI_MGMT_CLASSCODE, \ | ||
931 | .class_mask = 0xFFFFFFFF, \ | ||
932 | }, \ | ||
933 | { \ | ||
934 | .vendor = MICROSEMI_VENDOR_ID, \ | ||
935 | .device = device_id, \ | ||
936 | .subvendor = PCI_ANY_ID, \ | ||
937 | .subdevice = PCI_ANY_ID, \ | ||
938 | .class = MICROSEMI_NTB_CLASSCODE, \ | ||
939 | .class_mask = 0xFFFFFFFF, \ | ||
940 | } | ||
941 | |||
942 | static const struct pci_device_id switchtec_pci_tbl[] = { | ||
943 | SWITCHTEC_PCI_DEVICE(0x8531), //PFX 24xG3 | ||
944 | SWITCHTEC_PCI_DEVICE(0x8532), //PFX 32xG3 | ||
945 | SWITCHTEC_PCI_DEVICE(0x8533), //PFX 48xG3 | ||
946 | SWITCHTEC_PCI_DEVICE(0x8534), //PFX 64xG3 | ||
947 | SWITCHTEC_PCI_DEVICE(0x8535), //PFX 80xG3 | ||
948 | SWITCHTEC_PCI_DEVICE(0x8536), //PFX 96xG3 | ||
949 | SWITCHTEC_PCI_DEVICE(0x8543), //PSX 48xG3 | ||
950 | SWITCHTEC_PCI_DEVICE(0x8544), //PSX 64xG3 | ||
951 | SWITCHTEC_PCI_DEVICE(0x8545), //PSX 80xG3 | ||
952 | SWITCHTEC_PCI_DEVICE(0x8546), //PSX 96xG3 | ||
953 | {0} | ||
954 | }; | ||
955 | MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl); | ||
956 | |||
957 | static struct pci_driver switchtec_pci_driver = { | ||
958 | .name = KBUILD_MODNAME, | ||
959 | .id_table = switchtec_pci_tbl, | ||
960 | .probe = switchtec_pci_probe, | ||
961 | .remove = switchtec_pci_remove, | ||
962 | }; | ||
963 | |||
964 | static int __init switchtec_init(void) | ||
965 | { | ||
966 | int rc; | ||
967 | |||
968 | rc = alloc_chrdev_region(&switchtec_devt, 0, max_devices, | ||
969 | "switchtec"); | ||
970 | if (rc) | ||
971 | return rc; | ||
972 | |||
973 | switchtec_class = class_create(THIS_MODULE, "switchtec"); | ||
974 | if (IS_ERR(switchtec_class)) { | ||
975 | rc = PTR_ERR(switchtec_class); | ||
976 | goto err_create_class; | ||
977 | } | ||
978 | |||
979 | rc = pci_register_driver(&switchtec_pci_driver); | ||
980 | if (rc) | ||
981 | goto err_pci_register; | ||
982 | |||
983 | pr_info(KBUILD_MODNAME ": loaded.\n"); | ||
984 | |||
985 | return 0; | ||
986 | |||
987 | err_pci_register: | ||
988 | class_destroy(switchtec_class); | ||
989 | |||
990 | err_create_class: | ||
991 | unregister_chrdev_region(switchtec_devt, max_devices); | ||
992 | |||
993 | return rc; | ||
994 | } | ||
995 | module_init(switchtec_init); | ||
996 | |||
997 | static void __exit switchtec_exit(void) | ||
998 | { | ||
999 | pci_unregister_driver(&switchtec_pci_driver); | ||
1000 | class_destroy(switchtec_class); | ||
1001 | unregister_chrdev_region(switchtec_devt, max_devices); | ||
1002 | ida_destroy(&switchtec_minor_ida); | ||
1003 | |||
1004 | pr_info(KBUILD_MODNAME ": unloaded.\n"); | ||
1005 | } | ||
1006 | module_exit(switchtec_exit); | ||