xref: /linux/drivers/pci/switch/switchtec.c (revision e40cf640b8f632091a30ef0b030c83546f07c902)
1080b47deSLogan Gunthorpe /*
2080b47deSLogan Gunthorpe  * Microsemi Switchtec(tm) PCIe Management Driver
3080b47deSLogan Gunthorpe  * Copyright (c) 2017, Microsemi Corporation
4080b47deSLogan Gunthorpe  *
5080b47deSLogan Gunthorpe  * This program is free software; you can redistribute it and/or modify it
6080b47deSLogan Gunthorpe  * under the terms and conditions of the GNU General Public License,
7080b47deSLogan Gunthorpe  * version 2, as published by the Free Software Foundation.
8080b47deSLogan Gunthorpe  *
9080b47deSLogan Gunthorpe  * This program is distributed in the hope it will be useful, but WITHOUT
10080b47deSLogan Gunthorpe  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11080b47deSLogan Gunthorpe  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12080b47deSLogan Gunthorpe  * more details.
13080b47deSLogan Gunthorpe  *
14080b47deSLogan Gunthorpe  */
15080b47deSLogan Gunthorpe 
1652eabba5SLogan Gunthorpe #include <linux/switchtec_ioctl.h>
1752eabba5SLogan Gunthorpe 
18080b47deSLogan Gunthorpe #include <linux/interrupt.h>
19080b47deSLogan Gunthorpe #include <linux/module.h>
20080b47deSLogan Gunthorpe #include <linux/fs.h>
21080b47deSLogan Gunthorpe #include <linux/uaccess.h>
22080b47deSLogan Gunthorpe #include <linux/poll.h>
23080b47deSLogan Gunthorpe #include <linux/pci.h>
24080b47deSLogan Gunthorpe #include <linux/cdev.h>
25080b47deSLogan Gunthorpe #include <linux/wait.h>
26080b47deSLogan Gunthorpe 
27080b47deSLogan Gunthorpe MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver");
28080b47deSLogan Gunthorpe MODULE_VERSION("0.1");
29080b47deSLogan Gunthorpe MODULE_LICENSE("GPL");
30080b47deSLogan Gunthorpe MODULE_AUTHOR("Microsemi Corporation");
31080b47deSLogan Gunthorpe 
32080b47deSLogan Gunthorpe static int max_devices = 16;
33080b47deSLogan Gunthorpe module_param(max_devices, int, 0644);
34080b47deSLogan Gunthorpe MODULE_PARM_DESC(max_devices, "max number of switchtec device instances");
35080b47deSLogan Gunthorpe 
36080b47deSLogan Gunthorpe static dev_t switchtec_devt;
37080b47deSLogan Gunthorpe static struct class *switchtec_class;
38080b47deSLogan Gunthorpe static DEFINE_IDA(switchtec_minor_ida);
39080b47deSLogan Gunthorpe 
40080b47deSLogan Gunthorpe #define MICROSEMI_VENDOR_ID         0x11f8
41080b47deSLogan Gunthorpe #define MICROSEMI_NTB_CLASSCODE     0x068000
42080b47deSLogan Gunthorpe #define MICROSEMI_MGMT_CLASSCODE    0x058000
43080b47deSLogan Gunthorpe 
44080b47deSLogan Gunthorpe #define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024
45080b47deSLogan Gunthorpe #define SWITCHTEC_MAX_PFF_CSR 48
46080b47deSLogan Gunthorpe 
47080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_OCCURRED BIT(0)
48080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_CLEAR    BIT(0)
49080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_EN_LOG   BIT(1)
50080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_EN_CLI   BIT(2)
51080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_EN_IRQ   BIT(3)
52080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_FATAL    BIT(4)
53080b47deSLogan Gunthorpe 
54080b47deSLogan Gunthorpe enum {
55080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_MRPC_OFFSET       = 0x0000,
56080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_TOP_CFG_OFFSET    = 0x1000,
57080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_SW_EVENT_OFFSET   = 0x1800,
58080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_SYS_INFO_OFFSET   = 0x2000,
59080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_FLASH_INFO_OFFSET = 0x2200,
60080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_PART_CFG_OFFSET   = 0x4000,
61080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_NTB_OFFSET        = 0x10000,
62080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_PFF_CSR_OFFSET    = 0x134000,
63080b47deSLogan Gunthorpe };
64080b47deSLogan Gunthorpe 
65080b47deSLogan Gunthorpe struct mrpc_regs {
66080b47deSLogan Gunthorpe 	u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
67080b47deSLogan Gunthorpe 	u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
68080b47deSLogan Gunthorpe 	u32 cmd;
69080b47deSLogan Gunthorpe 	u32 status;
70080b47deSLogan Gunthorpe 	u32 ret_value;
71080b47deSLogan Gunthorpe } __packed;
72080b47deSLogan Gunthorpe 
73080b47deSLogan Gunthorpe enum mrpc_status {
74080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_INPROGRESS = 1,
75080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_DONE = 2,
76080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_ERROR = 0xFF,
77080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_INTERRUPTED = 0x100,
78080b47deSLogan Gunthorpe };
79080b47deSLogan Gunthorpe 
80080b47deSLogan Gunthorpe struct sw_event_regs {
81080b47deSLogan Gunthorpe 	u64 event_report_ctrl;
82080b47deSLogan Gunthorpe 	u64 reserved1;
83080b47deSLogan Gunthorpe 	u64 part_event_bitmap;
84080b47deSLogan Gunthorpe 	u64 reserved2;
85080b47deSLogan Gunthorpe 	u32 global_summary;
86080b47deSLogan Gunthorpe 	u32 reserved3[3];
87080b47deSLogan Gunthorpe 	u32 stack_error_event_hdr;
88080b47deSLogan Gunthorpe 	u32 stack_error_event_data;
89080b47deSLogan Gunthorpe 	u32 reserved4[4];
90080b47deSLogan Gunthorpe 	u32 ppu_error_event_hdr;
91080b47deSLogan Gunthorpe 	u32 ppu_error_event_data;
92080b47deSLogan Gunthorpe 	u32 reserved5[4];
93080b47deSLogan Gunthorpe 	u32 isp_error_event_hdr;
94080b47deSLogan Gunthorpe 	u32 isp_error_event_data;
95080b47deSLogan Gunthorpe 	u32 reserved6[4];
96080b47deSLogan Gunthorpe 	u32 sys_reset_event_hdr;
97080b47deSLogan Gunthorpe 	u32 reserved7[5];
98080b47deSLogan Gunthorpe 	u32 fw_exception_hdr;
99080b47deSLogan Gunthorpe 	u32 reserved8[5];
100080b47deSLogan Gunthorpe 	u32 fw_nmi_hdr;
101080b47deSLogan Gunthorpe 	u32 reserved9[5];
102080b47deSLogan Gunthorpe 	u32 fw_non_fatal_hdr;
103080b47deSLogan Gunthorpe 	u32 reserved10[5];
104080b47deSLogan Gunthorpe 	u32 fw_fatal_hdr;
105080b47deSLogan Gunthorpe 	u32 reserved11[5];
106080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_hdr;
107080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_data;
108080b47deSLogan Gunthorpe 	u32 reserved12[4];
109080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_async_hdr;
110080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_async_data;
111080b47deSLogan Gunthorpe 	u32 reserved13[4];
112080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_hdr;
113080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_data;
114080b47deSLogan Gunthorpe 	u32 reserved14[4];
115080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_async_hdr;
116080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_async_data;
117080b47deSLogan Gunthorpe 	u32 reserved15[4];
118080b47deSLogan Gunthorpe 	u32 gpio_interrupt_hdr;
119080b47deSLogan Gunthorpe 	u32 gpio_interrupt_data;
120080b47deSLogan Gunthorpe 	u32 reserved16[4];
121080b47deSLogan Gunthorpe } __packed;
122080b47deSLogan Gunthorpe 
123080b47deSLogan Gunthorpe struct sys_info_regs {
124080b47deSLogan Gunthorpe 	u32 device_id;
125080b47deSLogan Gunthorpe 	u32 device_version;
126080b47deSLogan Gunthorpe 	u32 firmware_version;
127080b47deSLogan Gunthorpe 	u32 reserved1;
128080b47deSLogan Gunthorpe 	u32 vendor_table_revision;
129080b47deSLogan Gunthorpe 	u32 table_format_version;
130080b47deSLogan Gunthorpe 	u32 partition_id;
131080b47deSLogan Gunthorpe 	u32 cfg_file_fmt_version;
132080b47deSLogan Gunthorpe 	u32 reserved2[58];
133080b47deSLogan Gunthorpe 	char vendor_id[8];
134080b47deSLogan Gunthorpe 	char product_id[16];
135080b47deSLogan Gunthorpe 	char product_revision[4];
136080b47deSLogan Gunthorpe 	char component_vendor[8];
137080b47deSLogan Gunthorpe 	u16 component_id;
138080b47deSLogan Gunthorpe 	u8 component_revision;
139080b47deSLogan Gunthorpe } __packed;
140080b47deSLogan Gunthorpe 
141080b47deSLogan Gunthorpe struct flash_info_regs {
142080b47deSLogan Gunthorpe 	u32 flash_part_map_upd_idx;
143080b47deSLogan Gunthorpe 
144080b47deSLogan Gunthorpe 	struct active_partition_info {
145080b47deSLogan Gunthorpe 		u32 address;
146080b47deSLogan Gunthorpe 		u32 build_version;
147080b47deSLogan Gunthorpe 		u32 build_string;
148080b47deSLogan Gunthorpe 	} active_img;
149080b47deSLogan Gunthorpe 
150080b47deSLogan Gunthorpe 	struct active_partition_info active_cfg;
151080b47deSLogan Gunthorpe 	struct active_partition_info inactive_img;
152080b47deSLogan Gunthorpe 	struct active_partition_info inactive_cfg;
153080b47deSLogan Gunthorpe 
154080b47deSLogan Gunthorpe 	u32 flash_length;
155080b47deSLogan Gunthorpe 
156080b47deSLogan Gunthorpe 	struct partition_info {
157080b47deSLogan Gunthorpe 		u32 address;
158080b47deSLogan Gunthorpe 		u32 length;
159080b47deSLogan Gunthorpe 	} cfg0;
160080b47deSLogan Gunthorpe 
161080b47deSLogan Gunthorpe 	struct partition_info cfg1;
162080b47deSLogan Gunthorpe 	struct partition_info img0;
163080b47deSLogan Gunthorpe 	struct partition_info img1;
164080b47deSLogan Gunthorpe 	struct partition_info nvlog;
165080b47deSLogan Gunthorpe 	struct partition_info vendor[8];
166080b47deSLogan Gunthorpe };
167080b47deSLogan Gunthorpe 
168080b47deSLogan Gunthorpe struct ntb_info_regs {
169080b47deSLogan Gunthorpe 	u8  partition_count;
170080b47deSLogan Gunthorpe 	u8  partition_id;
171080b47deSLogan Gunthorpe 	u16 reserved1;
172080b47deSLogan Gunthorpe 	u64 ep_map;
173080b47deSLogan Gunthorpe 	u16 requester_id;
174080b47deSLogan Gunthorpe } __packed;
175080b47deSLogan Gunthorpe 
176080b47deSLogan Gunthorpe struct part_cfg_regs {
177080b47deSLogan Gunthorpe 	u32 status;
178080b47deSLogan Gunthorpe 	u32 state;
179080b47deSLogan Gunthorpe 	u32 port_cnt;
180080b47deSLogan Gunthorpe 	u32 usp_port_mode;
181080b47deSLogan Gunthorpe 	u32 usp_pff_inst_id;
182080b47deSLogan Gunthorpe 	u32 vep_pff_inst_id;
183080b47deSLogan Gunthorpe 	u32 dsp_pff_inst_id[47];
184080b47deSLogan Gunthorpe 	u32 reserved1[11];
185080b47deSLogan Gunthorpe 	u16 vep_vector_number;
186080b47deSLogan Gunthorpe 	u16 usp_vector_number;
187080b47deSLogan Gunthorpe 	u32 port_event_bitmap;
188080b47deSLogan Gunthorpe 	u32 reserved2[3];
189080b47deSLogan Gunthorpe 	u32 part_event_summary;
190080b47deSLogan Gunthorpe 	u32 reserved3[3];
191080b47deSLogan Gunthorpe 	u32 part_reset_hdr;
192080b47deSLogan Gunthorpe 	u32 part_reset_data[5];
193080b47deSLogan Gunthorpe 	u32 mrpc_comp_hdr;
194080b47deSLogan Gunthorpe 	u32 mrpc_comp_data[5];
195080b47deSLogan Gunthorpe 	u32 mrpc_comp_async_hdr;
196080b47deSLogan Gunthorpe 	u32 mrpc_comp_async_data[5];
197080b47deSLogan Gunthorpe 	u32 dyn_binding_hdr;
198080b47deSLogan Gunthorpe 	u32 dyn_binding_data[5];
199080b47deSLogan Gunthorpe 	u32 reserved4[159];
200080b47deSLogan Gunthorpe } __packed;
201080b47deSLogan Gunthorpe 
202080b47deSLogan Gunthorpe enum {
203080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_RESET = 1 << 0,
204080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_MRPC_CMP = 1 << 1,
205080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_MRPC_ASYNC_CMP = 1 << 2,
206080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_DYN_PART_CMP = 1 << 3,
207080b47deSLogan Gunthorpe };
208080b47deSLogan Gunthorpe 
209080b47deSLogan Gunthorpe struct pff_csr_regs {
210080b47deSLogan Gunthorpe 	u16 vendor_id;
211080b47deSLogan Gunthorpe 	u16 device_id;
212080b47deSLogan Gunthorpe 	u32 pci_cfg_header[15];
213080b47deSLogan Gunthorpe 	u32 pci_cap_region[48];
214080b47deSLogan Gunthorpe 	u32 pcie_cap_region[448];
215080b47deSLogan Gunthorpe 	u32 indirect_gas_window[128];
216080b47deSLogan Gunthorpe 	u32 indirect_gas_window_off;
217080b47deSLogan Gunthorpe 	u32 reserved[127];
218080b47deSLogan Gunthorpe 	u32 pff_event_summary;
219080b47deSLogan Gunthorpe 	u32 reserved2[3];
220080b47deSLogan Gunthorpe 	u32 aer_in_p2p_hdr;
221080b47deSLogan Gunthorpe 	u32 aer_in_p2p_data[5];
222080b47deSLogan Gunthorpe 	u32 aer_in_vep_hdr;
223080b47deSLogan Gunthorpe 	u32 aer_in_vep_data[5];
224080b47deSLogan Gunthorpe 	u32 dpc_hdr;
225080b47deSLogan Gunthorpe 	u32 dpc_data[5];
226080b47deSLogan Gunthorpe 	u32 cts_hdr;
227080b47deSLogan Gunthorpe 	u32 cts_data[5];
228080b47deSLogan Gunthorpe 	u32 reserved3[6];
229080b47deSLogan Gunthorpe 	u32 hotplug_hdr;
230080b47deSLogan Gunthorpe 	u32 hotplug_data[5];
231080b47deSLogan Gunthorpe 	u32 ier_hdr;
232080b47deSLogan Gunthorpe 	u32 ier_data[5];
233080b47deSLogan Gunthorpe 	u32 threshold_hdr;
234080b47deSLogan Gunthorpe 	u32 threshold_data[5];
235080b47deSLogan Gunthorpe 	u32 power_mgmt_hdr;
236080b47deSLogan Gunthorpe 	u32 power_mgmt_data[5];
237080b47deSLogan Gunthorpe 	u32 tlp_throttling_hdr;
238080b47deSLogan Gunthorpe 	u32 tlp_throttling_data[5];
239080b47deSLogan Gunthorpe 	u32 force_speed_hdr;
240080b47deSLogan Gunthorpe 	u32 force_speed_data[5];
241080b47deSLogan Gunthorpe 	u32 credit_timeout_hdr;
242080b47deSLogan Gunthorpe 	u32 credit_timeout_data[5];
243080b47deSLogan Gunthorpe 	u32 link_state_hdr;
244080b47deSLogan Gunthorpe 	u32 link_state_data[5];
245080b47deSLogan Gunthorpe 	u32 reserved4[174];
246080b47deSLogan Gunthorpe } __packed;
247080b47deSLogan Gunthorpe 
248080b47deSLogan Gunthorpe struct switchtec_dev {
249080b47deSLogan Gunthorpe 	struct pci_dev *pdev;
250080b47deSLogan Gunthorpe 	struct device dev;
251080b47deSLogan Gunthorpe 	struct cdev cdev;
252080b47deSLogan Gunthorpe 
253080b47deSLogan Gunthorpe 	int partition;
254080b47deSLogan Gunthorpe 	int partition_count;
255080b47deSLogan Gunthorpe 	int pff_csr_count;
256080b47deSLogan Gunthorpe 	char pff_local[SWITCHTEC_MAX_PFF_CSR];
257080b47deSLogan Gunthorpe 
258080b47deSLogan Gunthorpe 	void __iomem *mmio;
259080b47deSLogan Gunthorpe 	struct mrpc_regs __iomem *mmio_mrpc;
260080b47deSLogan Gunthorpe 	struct sw_event_regs __iomem *mmio_sw_event;
261080b47deSLogan Gunthorpe 	struct sys_info_regs __iomem *mmio_sys_info;
262080b47deSLogan Gunthorpe 	struct flash_info_regs __iomem *mmio_flash_info;
263080b47deSLogan Gunthorpe 	struct ntb_info_regs __iomem *mmio_ntb;
264080b47deSLogan Gunthorpe 	struct part_cfg_regs __iomem *mmio_part_cfg;
265080b47deSLogan Gunthorpe 	struct part_cfg_regs __iomem *mmio_part_cfg_all;
266080b47deSLogan Gunthorpe 	struct pff_csr_regs __iomem *mmio_pff_csr;
267080b47deSLogan Gunthorpe 
268080b47deSLogan Gunthorpe 	/*
269080b47deSLogan Gunthorpe 	 * The mrpc mutex must be held when accessing the other
270080b47deSLogan Gunthorpe 	 * mrpc_ fields, alive flag and stuser->state field
271080b47deSLogan Gunthorpe 	 */
272080b47deSLogan Gunthorpe 	struct mutex mrpc_mutex;
273080b47deSLogan Gunthorpe 	struct list_head mrpc_queue;
274080b47deSLogan Gunthorpe 	int mrpc_busy;
275080b47deSLogan Gunthorpe 	struct work_struct mrpc_work;
276080b47deSLogan Gunthorpe 	struct delayed_work mrpc_timeout;
277080b47deSLogan Gunthorpe 	bool alive;
278080b47deSLogan Gunthorpe 
279080b47deSLogan Gunthorpe 	wait_queue_head_t event_wq;
280080b47deSLogan Gunthorpe 	atomic_t event_cnt;
281080b47deSLogan Gunthorpe };
282080b47deSLogan Gunthorpe 
283080b47deSLogan Gunthorpe static struct switchtec_dev *to_stdev(struct device *dev)
284080b47deSLogan Gunthorpe {
285080b47deSLogan Gunthorpe 	return container_of(dev, struct switchtec_dev, dev);
286080b47deSLogan Gunthorpe }
287080b47deSLogan Gunthorpe 
288080b47deSLogan Gunthorpe enum mrpc_state {
289080b47deSLogan Gunthorpe 	MRPC_IDLE = 0,
290080b47deSLogan Gunthorpe 	MRPC_QUEUED,
291080b47deSLogan Gunthorpe 	MRPC_RUNNING,
292080b47deSLogan Gunthorpe 	MRPC_DONE,
293080b47deSLogan Gunthorpe };
294080b47deSLogan Gunthorpe 
295080b47deSLogan Gunthorpe struct switchtec_user {
296080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
297080b47deSLogan Gunthorpe 
298080b47deSLogan Gunthorpe 	enum mrpc_state state;
299080b47deSLogan Gunthorpe 
300080b47deSLogan Gunthorpe 	struct completion comp;
301080b47deSLogan Gunthorpe 	struct kref kref;
302080b47deSLogan Gunthorpe 	struct list_head list;
303080b47deSLogan Gunthorpe 
304080b47deSLogan Gunthorpe 	u32 cmd;
305080b47deSLogan Gunthorpe 	u32 status;
306080b47deSLogan Gunthorpe 	u32 return_code;
307080b47deSLogan Gunthorpe 	size_t data_len;
308080b47deSLogan Gunthorpe 	size_t read_len;
309080b47deSLogan Gunthorpe 	unsigned char data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
310080b47deSLogan Gunthorpe 	int event_cnt;
311080b47deSLogan Gunthorpe };
312080b47deSLogan Gunthorpe 
313080b47deSLogan Gunthorpe static struct switchtec_user *stuser_create(struct switchtec_dev *stdev)
314080b47deSLogan Gunthorpe {
315080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
316080b47deSLogan Gunthorpe 
317080b47deSLogan Gunthorpe 	stuser = kzalloc(sizeof(*stuser), GFP_KERNEL);
318080b47deSLogan Gunthorpe 	if (!stuser)
319080b47deSLogan Gunthorpe 		return ERR_PTR(-ENOMEM);
320080b47deSLogan Gunthorpe 
321080b47deSLogan Gunthorpe 	get_device(&stdev->dev);
322080b47deSLogan Gunthorpe 	stuser->stdev = stdev;
323080b47deSLogan Gunthorpe 	kref_init(&stuser->kref);
324080b47deSLogan Gunthorpe 	INIT_LIST_HEAD(&stuser->list);
325080b47deSLogan Gunthorpe 	init_completion(&stuser->comp);
326080b47deSLogan Gunthorpe 	stuser->event_cnt = atomic_read(&stdev->event_cnt);
327080b47deSLogan Gunthorpe 
328080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser);
329080b47deSLogan Gunthorpe 
330080b47deSLogan Gunthorpe 	return stuser;
331080b47deSLogan Gunthorpe }
332080b47deSLogan Gunthorpe 
333080b47deSLogan Gunthorpe static void stuser_free(struct kref *kref)
334080b47deSLogan Gunthorpe {
335080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
336080b47deSLogan Gunthorpe 
337080b47deSLogan Gunthorpe 	stuser = container_of(kref, struct switchtec_user, kref);
338080b47deSLogan Gunthorpe 
339080b47deSLogan Gunthorpe 	dev_dbg(&stuser->stdev->dev, "%s: %p\n", __func__, stuser);
340080b47deSLogan Gunthorpe 
341080b47deSLogan Gunthorpe 	put_device(&stuser->stdev->dev);
342080b47deSLogan Gunthorpe 	kfree(stuser);
343080b47deSLogan Gunthorpe }
344080b47deSLogan Gunthorpe 
345080b47deSLogan Gunthorpe static void stuser_put(struct switchtec_user *stuser)
346080b47deSLogan Gunthorpe {
347080b47deSLogan Gunthorpe 	kref_put(&stuser->kref, stuser_free);
348080b47deSLogan Gunthorpe }
349080b47deSLogan Gunthorpe 
350080b47deSLogan Gunthorpe static void stuser_set_state(struct switchtec_user *stuser,
351080b47deSLogan Gunthorpe 			     enum mrpc_state state)
352080b47deSLogan Gunthorpe {
353080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
354080b47deSLogan Gunthorpe 
355080b47deSLogan Gunthorpe 	const char * const state_names[] = {
356080b47deSLogan Gunthorpe 		[MRPC_IDLE] = "IDLE",
357080b47deSLogan Gunthorpe 		[MRPC_QUEUED] = "QUEUED",
358080b47deSLogan Gunthorpe 		[MRPC_RUNNING] = "RUNNING",
359080b47deSLogan Gunthorpe 		[MRPC_DONE] = "DONE",
360080b47deSLogan Gunthorpe 	};
361080b47deSLogan Gunthorpe 
362080b47deSLogan Gunthorpe 	stuser->state = state;
363080b47deSLogan Gunthorpe 
364080b47deSLogan Gunthorpe 	dev_dbg(&stuser->stdev->dev, "stuser state %p -> %s",
365080b47deSLogan Gunthorpe 		stuser, state_names[state]);
366080b47deSLogan Gunthorpe }
367080b47deSLogan Gunthorpe 
368080b47deSLogan Gunthorpe static void mrpc_complete_cmd(struct switchtec_dev *stdev);
369080b47deSLogan Gunthorpe 
370080b47deSLogan Gunthorpe static void mrpc_cmd_submit(struct switchtec_dev *stdev)
371080b47deSLogan Gunthorpe {
372080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
373080b47deSLogan Gunthorpe 
374080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
375080b47deSLogan Gunthorpe 
376080b47deSLogan Gunthorpe 	if (stdev->mrpc_busy)
377080b47deSLogan Gunthorpe 		return;
378080b47deSLogan Gunthorpe 
379080b47deSLogan Gunthorpe 	if (list_empty(&stdev->mrpc_queue))
380080b47deSLogan Gunthorpe 		return;
381080b47deSLogan Gunthorpe 
382080b47deSLogan Gunthorpe 	stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
383080b47deSLogan Gunthorpe 			    list);
384080b47deSLogan Gunthorpe 
385080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_RUNNING);
386080b47deSLogan Gunthorpe 	stdev->mrpc_busy = 1;
387080b47deSLogan Gunthorpe 	memcpy_toio(&stdev->mmio_mrpc->input_data,
388080b47deSLogan Gunthorpe 		    stuser->data, stuser->data_len);
389080b47deSLogan Gunthorpe 	iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd);
390080b47deSLogan Gunthorpe 
391080b47deSLogan Gunthorpe 	stuser->status = ioread32(&stdev->mmio_mrpc->status);
392080b47deSLogan Gunthorpe 	if (stuser->status != SWITCHTEC_MRPC_STATUS_INPROGRESS)
393080b47deSLogan Gunthorpe 		mrpc_complete_cmd(stdev);
394080b47deSLogan Gunthorpe 
395080b47deSLogan Gunthorpe 	schedule_delayed_work(&stdev->mrpc_timeout,
396080b47deSLogan Gunthorpe 			      msecs_to_jiffies(500));
397080b47deSLogan Gunthorpe }
398080b47deSLogan Gunthorpe 
399080b47deSLogan Gunthorpe static int mrpc_queue_cmd(struct switchtec_user *stuser)
400080b47deSLogan Gunthorpe {
401080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
402080b47deSLogan Gunthorpe 
403080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
404080b47deSLogan Gunthorpe 
405080b47deSLogan Gunthorpe 	kref_get(&stuser->kref);
406080b47deSLogan Gunthorpe 	stuser->read_len = sizeof(stuser->data);
407080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_QUEUED);
408080b47deSLogan Gunthorpe 	init_completion(&stuser->comp);
409080b47deSLogan Gunthorpe 	list_add_tail(&stuser->list, &stdev->mrpc_queue);
410080b47deSLogan Gunthorpe 
411080b47deSLogan Gunthorpe 	mrpc_cmd_submit(stdev);
412080b47deSLogan Gunthorpe 
413080b47deSLogan Gunthorpe 	return 0;
414080b47deSLogan Gunthorpe }
415080b47deSLogan Gunthorpe 
416080b47deSLogan Gunthorpe static void mrpc_complete_cmd(struct switchtec_dev *stdev)
417080b47deSLogan Gunthorpe {
418080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
419080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
420080b47deSLogan Gunthorpe 
421080b47deSLogan Gunthorpe 	if (list_empty(&stdev->mrpc_queue))
422080b47deSLogan Gunthorpe 		return;
423080b47deSLogan Gunthorpe 
424080b47deSLogan Gunthorpe 	stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
425080b47deSLogan Gunthorpe 			    list);
426080b47deSLogan Gunthorpe 
427080b47deSLogan Gunthorpe 	stuser->status = ioread32(&stdev->mmio_mrpc->status);
428080b47deSLogan Gunthorpe 	if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS)
429080b47deSLogan Gunthorpe 		return;
430080b47deSLogan Gunthorpe 
431080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_DONE);
432080b47deSLogan Gunthorpe 	stuser->return_code = 0;
433080b47deSLogan Gunthorpe 
434080b47deSLogan Gunthorpe 	if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE)
435080b47deSLogan Gunthorpe 		goto out;
436080b47deSLogan Gunthorpe 
437080b47deSLogan Gunthorpe 	stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value);
438080b47deSLogan Gunthorpe 	if (stuser->return_code != 0)
439080b47deSLogan Gunthorpe 		goto out;
440080b47deSLogan Gunthorpe 
441080b47deSLogan Gunthorpe 	memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data,
442080b47deSLogan Gunthorpe 		      stuser->read_len);
443080b47deSLogan Gunthorpe 
444080b47deSLogan Gunthorpe out:
445080b47deSLogan Gunthorpe 	complete_all(&stuser->comp);
446080b47deSLogan Gunthorpe 	list_del_init(&stuser->list);
447080b47deSLogan Gunthorpe 	stuser_put(stuser);
448080b47deSLogan Gunthorpe 	stdev->mrpc_busy = 0;
449080b47deSLogan Gunthorpe 
450080b47deSLogan Gunthorpe 	mrpc_cmd_submit(stdev);
451080b47deSLogan Gunthorpe }
452080b47deSLogan Gunthorpe 
453080b47deSLogan Gunthorpe static void mrpc_event_work(struct work_struct *work)
454080b47deSLogan Gunthorpe {
455080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
456080b47deSLogan Gunthorpe 
457080b47deSLogan Gunthorpe 	stdev = container_of(work, struct switchtec_dev, mrpc_work);
458080b47deSLogan Gunthorpe 
459080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s\n", __func__);
460080b47deSLogan Gunthorpe 
461080b47deSLogan Gunthorpe 	mutex_lock(&stdev->mrpc_mutex);
462080b47deSLogan Gunthorpe 	cancel_delayed_work(&stdev->mrpc_timeout);
463080b47deSLogan Gunthorpe 	mrpc_complete_cmd(stdev);
464080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
465080b47deSLogan Gunthorpe }
466080b47deSLogan Gunthorpe 
467080b47deSLogan Gunthorpe static void mrpc_timeout_work(struct work_struct *work)
468080b47deSLogan Gunthorpe {
469080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
470080b47deSLogan Gunthorpe 	u32 status;
471080b47deSLogan Gunthorpe 
472080b47deSLogan Gunthorpe 	stdev = container_of(work, struct switchtec_dev, mrpc_timeout.work);
473080b47deSLogan Gunthorpe 
474080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s\n", __func__);
475080b47deSLogan Gunthorpe 
476080b47deSLogan Gunthorpe 	mutex_lock(&stdev->mrpc_mutex);
477080b47deSLogan Gunthorpe 
478080b47deSLogan Gunthorpe 	status = ioread32(&stdev->mmio_mrpc->status);
479080b47deSLogan Gunthorpe 	if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) {
480080b47deSLogan Gunthorpe 		schedule_delayed_work(&stdev->mrpc_timeout,
481080b47deSLogan Gunthorpe 				      msecs_to_jiffies(500));
482080b47deSLogan Gunthorpe 		goto out;
483080b47deSLogan Gunthorpe 	}
484080b47deSLogan Gunthorpe 
485080b47deSLogan Gunthorpe 	mrpc_complete_cmd(stdev);
486080b47deSLogan Gunthorpe 
487080b47deSLogan Gunthorpe out:
488080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
489080b47deSLogan Gunthorpe }
490080b47deSLogan Gunthorpe 
4915d8e1881SLogan Gunthorpe static ssize_t device_version_show(struct device *dev,
4925d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
4935d8e1881SLogan Gunthorpe {
4945d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
4955d8e1881SLogan Gunthorpe 	u32 ver;
4965d8e1881SLogan Gunthorpe 
4975d8e1881SLogan Gunthorpe 	ver = ioread32(&stdev->mmio_sys_info->device_version);
4985d8e1881SLogan Gunthorpe 
4995d8e1881SLogan Gunthorpe 	return sprintf(buf, "%x\n", ver);
5005d8e1881SLogan Gunthorpe }
5015d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(device_version);
5025d8e1881SLogan Gunthorpe 
5035d8e1881SLogan Gunthorpe static ssize_t fw_version_show(struct device *dev,
5045d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
5055d8e1881SLogan Gunthorpe {
5065d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
5075d8e1881SLogan Gunthorpe 	u32 ver;
5085d8e1881SLogan Gunthorpe 
5095d8e1881SLogan Gunthorpe 	ver = ioread32(&stdev->mmio_sys_info->firmware_version);
5105d8e1881SLogan Gunthorpe 
5115d8e1881SLogan Gunthorpe 	return sprintf(buf, "%08x\n", ver);
5125d8e1881SLogan Gunthorpe }
5135d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(fw_version);
5145d8e1881SLogan Gunthorpe 
5155d8e1881SLogan Gunthorpe static ssize_t io_string_show(char *buf, void __iomem *attr, size_t len)
5165d8e1881SLogan Gunthorpe {
5175d8e1881SLogan Gunthorpe 	int i;
5185d8e1881SLogan Gunthorpe 
5195d8e1881SLogan Gunthorpe 	memcpy_fromio(buf, attr, len);
5205d8e1881SLogan Gunthorpe 	buf[len] = '\n';
5215d8e1881SLogan Gunthorpe 	buf[len + 1] = 0;
5225d8e1881SLogan Gunthorpe 
5235d8e1881SLogan Gunthorpe 	for (i = len - 1; i > 0; i--) {
5245d8e1881SLogan Gunthorpe 		if (buf[i] != ' ')
5255d8e1881SLogan Gunthorpe 			break;
5265d8e1881SLogan Gunthorpe 		buf[i] = '\n';
5275d8e1881SLogan Gunthorpe 		buf[i + 1] = 0;
5285d8e1881SLogan Gunthorpe 	}
5295d8e1881SLogan Gunthorpe 
5305d8e1881SLogan Gunthorpe 	return strlen(buf);
5315d8e1881SLogan Gunthorpe }
5325d8e1881SLogan Gunthorpe 
5335d8e1881SLogan Gunthorpe #define DEVICE_ATTR_SYS_INFO_STR(field) \
5345d8e1881SLogan Gunthorpe static ssize_t field ## _show(struct device *dev, \
5355d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf) \
5365d8e1881SLogan Gunthorpe { \
5375d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev); \
5385d8e1881SLogan Gunthorpe 	return io_string_show(buf, &stdev->mmio_sys_info->field, \
5395d8e1881SLogan Gunthorpe 			    sizeof(stdev->mmio_sys_info->field)); \
5405d8e1881SLogan Gunthorpe } \
5415d8e1881SLogan Gunthorpe \
5425d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(field)
5435d8e1881SLogan Gunthorpe 
5445d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(vendor_id);
5455d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(product_id);
5465d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(product_revision);
5475d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(component_vendor);
5485d8e1881SLogan Gunthorpe 
5495d8e1881SLogan Gunthorpe static ssize_t component_id_show(struct device *dev,
5505d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
5515d8e1881SLogan Gunthorpe {
5525d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
5535d8e1881SLogan Gunthorpe 	int id = ioread16(&stdev->mmio_sys_info->component_id);
5545d8e1881SLogan Gunthorpe 
5555d8e1881SLogan Gunthorpe 	return sprintf(buf, "PM%04X\n", id);
5565d8e1881SLogan Gunthorpe }
5575d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(component_id);
5585d8e1881SLogan Gunthorpe 
5595d8e1881SLogan Gunthorpe static ssize_t component_revision_show(struct device *dev,
5605d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
5615d8e1881SLogan Gunthorpe {
5625d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
5635d8e1881SLogan Gunthorpe 	int rev = ioread8(&stdev->mmio_sys_info->component_revision);
5645d8e1881SLogan Gunthorpe 
5655d8e1881SLogan Gunthorpe 	return sprintf(buf, "%d\n", rev);
5665d8e1881SLogan Gunthorpe }
5675d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(component_revision);
5685d8e1881SLogan Gunthorpe 
5695d8e1881SLogan Gunthorpe static ssize_t partition_show(struct device *dev,
5705d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
5715d8e1881SLogan Gunthorpe {
5725d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
5735d8e1881SLogan Gunthorpe 
5745d8e1881SLogan Gunthorpe 	return sprintf(buf, "%d\n", stdev->partition);
5755d8e1881SLogan Gunthorpe }
5765d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(partition);
5775d8e1881SLogan Gunthorpe 
5785d8e1881SLogan Gunthorpe static ssize_t partition_count_show(struct device *dev,
5795d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
5805d8e1881SLogan Gunthorpe {
5815d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
5825d8e1881SLogan Gunthorpe 
5835d8e1881SLogan Gunthorpe 	return sprintf(buf, "%d\n", stdev->partition_count);
5845d8e1881SLogan Gunthorpe }
5855d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(partition_count);
5865d8e1881SLogan Gunthorpe 
5875d8e1881SLogan Gunthorpe static struct attribute *switchtec_device_attrs[] = {
5885d8e1881SLogan Gunthorpe 	&dev_attr_device_version.attr,
5895d8e1881SLogan Gunthorpe 	&dev_attr_fw_version.attr,
5905d8e1881SLogan Gunthorpe 	&dev_attr_vendor_id.attr,
5915d8e1881SLogan Gunthorpe 	&dev_attr_product_id.attr,
5925d8e1881SLogan Gunthorpe 	&dev_attr_product_revision.attr,
5935d8e1881SLogan Gunthorpe 	&dev_attr_component_vendor.attr,
5945d8e1881SLogan Gunthorpe 	&dev_attr_component_id.attr,
5955d8e1881SLogan Gunthorpe 	&dev_attr_component_revision.attr,
5965d8e1881SLogan Gunthorpe 	&dev_attr_partition.attr,
5975d8e1881SLogan Gunthorpe 	&dev_attr_partition_count.attr,
5985d8e1881SLogan Gunthorpe 	NULL,
5995d8e1881SLogan Gunthorpe };
6005d8e1881SLogan Gunthorpe 
6015d8e1881SLogan Gunthorpe ATTRIBUTE_GROUPS(switchtec_device);
6025d8e1881SLogan Gunthorpe 
603080b47deSLogan Gunthorpe static int switchtec_dev_open(struct inode *inode, struct file *filp)
604080b47deSLogan Gunthorpe {
605080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
606080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
607080b47deSLogan Gunthorpe 
608080b47deSLogan Gunthorpe 	stdev = container_of(inode->i_cdev, struct switchtec_dev, cdev);
609080b47deSLogan Gunthorpe 
610080b47deSLogan Gunthorpe 	stuser = stuser_create(stdev);
611080b47deSLogan Gunthorpe 	if (IS_ERR(stuser))
612080b47deSLogan Gunthorpe 		return PTR_ERR(stuser);
613080b47deSLogan Gunthorpe 
614080b47deSLogan Gunthorpe 	filp->private_data = stuser;
615080b47deSLogan Gunthorpe 	nonseekable_open(inode, filp);
616080b47deSLogan Gunthorpe 
617080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser);
618080b47deSLogan Gunthorpe 
619080b47deSLogan Gunthorpe 	return 0;
620080b47deSLogan Gunthorpe }
621080b47deSLogan Gunthorpe 
622080b47deSLogan Gunthorpe static int switchtec_dev_release(struct inode *inode, struct file *filp)
623080b47deSLogan Gunthorpe {
624080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
625080b47deSLogan Gunthorpe 
626080b47deSLogan Gunthorpe 	stuser_put(stuser);
627080b47deSLogan Gunthorpe 
628080b47deSLogan Gunthorpe 	return 0;
629080b47deSLogan Gunthorpe }
630080b47deSLogan Gunthorpe 
631080b47deSLogan Gunthorpe static int lock_mutex_and_test_alive(struct switchtec_dev *stdev)
632080b47deSLogan Gunthorpe {
633080b47deSLogan Gunthorpe 	if (mutex_lock_interruptible(&stdev->mrpc_mutex))
634080b47deSLogan Gunthorpe 		return -EINTR;
635080b47deSLogan Gunthorpe 
636080b47deSLogan Gunthorpe 	if (!stdev->alive) {
637080b47deSLogan Gunthorpe 		mutex_unlock(&stdev->mrpc_mutex);
638080b47deSLogan Gunthorpe 		return -ENODEV;
639080b47deSLogan Gunthorpe 	}
640080b47deSLogan Gunthorpe 
641080b47deSLogan Gunthorpe 	return 0;
642080b47deSLogan Gunthorpe }
643080b47deSLogan Gunthorpe 
644080b47deSLogan Gunthorpe static ssize_t switchtec_dev_write(struct file *filp, const char __user *data,
645080b47deSLogan Gunthorpe 				   size_t size, loff_t *off)
646080b47deSLogan Gunthorpe {
647080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
648080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
649080b47deSLogan Gunthorpe 	int rc;
650080b47deSLogan Gunthorpe 
651080b47deSLogan Gunthorpe 	if (size < sizeof(stuser->cmd) ||
652080b47deSLogan Gunthorpe 	    size > sizeof(stuser->cmd) + sizeof(stuser->data))
653080b47deSLogan Gunthorpe 		return -EINVAL;
654080b47deSLogan Gunthorpe 
655080b47deSLogan Gunthorpe 	stuser->data_len = size - sizeof(stuser->cmd);
656080b47deSLogan Gunthorpe 
657080b47deSLogan Gunthorpe 	rc = lock_mutex_and_test_alive(stdev);
658080b47deSLogan Gunthorpe 	if (rc)
659080b47deSLogan Gunthorpe 		return rc;
660080b47deSLogan Gunthorpe 
661080b47deSLogan Gunthorpe 	if (stuser->state != MRPC_IDLE) {
662080b47deSLogan Gunthorpe 		rc = -EBADE;
663080b47deSLogan Gunthorpe 		goto out;
664080b47deSLogan Gunthorpe 	}
665080b47deSLogan Gunthorpe 
666080b47deSLogan Gunthorpe 	rc = copy_from_user(&stuser->cmd, data, sizeof(stuser->cmd));
667080b47deSLogan Gunthorpe 	if (rc) {
668080b47deSLogan Gunthorpe 		rc = -EFAULT;
669080b47deSLogan Gunthorpe 		goto out;
670080b47deSLogan Gunthorpe 	}
671080b47deSLogan Gunthorpe 
672080b47deSLogan Gunthorpe 	data += sizeof(stuser->cmd);
673080b47deSLogan Gunthorpe 	rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd));
674080b47deSLogan Gunthorpe 	if (rc) {
675080b47deSLogan Gunthorpe 		rc = -EFAULT;
676080b47deSLogan Gunthorpe 		goto out;
677080b47deSLogan Gunthorpe 	}
678080b47deSLogan Gunthorpe 
679080b47deSLogan Gunthorpe 	rc = mrpc_queue_cmd(stuser);
680080b47deSLogan Gunthorpe 
681080b47deSLogan Gunthorpe out:
682080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
683080b47deSLogan Gunthorpe 
684080b47deSLogan Gunthorpe 	if (rc)
685080b47deSLogan Gunthorpe 		return rc;
686080b47deSLogan Gunthorpe 
687080b47deSLogan Gunthorpe 	return size;
688080b47deSLogan Gunthorpe }
689080b47deSLogan Gunthorpe 
690080b47deSLogan Gunthorpe static ssize_t switchtec_dev_read(struct file *filp, char __user *data,
691080b47deSLogan Gunthorpe 				  size_t size, loff_t *off)
692080b47deSLogan Gunthorpe {
693080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
694080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
695080b47deSLogan Gunthorpe 	int rc;
696080b47deSLogan Gunthorpe 
697080b47deSLogan Gunthorpe 	if (size < sizeof(stuser->cmd) ||
698080b47deSLogan Gunthorpe 	    size > sizeof(stuser->cmd) + sizeof(stuser->data))
699080b47deSLogan Gunthorpe 		return -EINVAL;
700080b47deSLogan Gunthorpe 
701080b47deSLogan Gunthorpe 	rc = lock_mutex_and_test_alive(stdev);
702080b47deSLogan Gunthorpe 	if (rc)
703080b47deSLogan Gunthorpe 		return rc;
704080b47deSLogan Gunthorpe 
705080b47deSLogan Gunthorpe 	if (stuser->state == MRPC_IDLE) {
706080b47deSLogan Gunthorpe 		mutex_unlock(&stdev->mrpc_mutex);
707080b47deSLogan Gunthorpe 		return -EBADE;
708080b47deSLogan Gunthorpe 	}
709080b47deSLogan Gunthorpe 
710080b47deSLogan Gunthorpe 	stuser->read_len = size - sizeof(stuser->return_code);
711080b47deSLogan Gunthorpe 
712080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
713080b47deSLogan Gunthorpe 
714080b47deSLogan Gunthorpe 	if (filp->f_flags & O_NONBLOCK) {
715080b47deSLogan Gunthorpe 		if (!try_wait_for_completion(&stuser->comp))
716080b47deSLogan Gunthorpe 			return -EAGAIN;
717080b47deSLogan Gunthorpe 	} else {
718080b47deSLogan Gunthorpe 		rc = wait_for_completion_interruptible(&stuser->comp);
719080b47deSLogan Gunthorpe 		if (rc < 0)
720080b47deSLogan Gunthorpe 			return rc;
721080b47deSLogan Gunthorpe 	}
722080b47deSLogan Gunthorpe 
723080b47deSLogan Gunthorpe 	rc = lock_mutex_and_test_alive(stdev);
724080b47deSLogan Gunthorpe 	if (rc)
725080b47deSLogan Gunthorpe 		return rc;
726080b47deSLogan Gunthorpe 
727080b47deSLogan Gunthorpe 	if (stuser->state != MRPC_DONE) {
728080b47deSLogan Gunthorpe 		mutex_unlock(&stdev->mrpc_mutex);
729080b47deSLogan Gunthorpe 		return -EBADE;
730080b47deSLogan Gunthorpe 	}
731080b47deSLogan Gunthorpe 
732080b47deSLogan Gunthorpe 	rc = copy_to_user(data, &stuser->return_code,
733080b47deSLogan Gunthorpe 			  sizeof(stuser->return_code));
734080b47deSLogan Gunthorpe 	if (rc) {
735080b47deSLogan Gunthorpe 		rc = -EFAULT;
736080b47deSLogan Gunthorpe 		goto out;
737080b47deSLogan Gunthorpe 	}
738080b47deSLogan Gunthorpe 
739080b47deSLogan Gunthorpe 	data += sizeof(stuser->return_code);
740080b47deSLogan Gunthorpe 	rc = copy_to_user(data, &stuser->data,
741080b47deSLogan Gunthorpe 			  size - sizeof(stuser->return_code));
742080b47deSLogan Gunthorpe 	if (rc) {
743080b47deSLogan Gunthorpe 		rc = -EFAULT;
744080b47deSLogan Gunthorpe 		goto out;
745080b47deSLogan Gunthorpe 	}
746080b47deSLogan Gunthorpe 
747080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_IDLE);
748080b47deSLogan Gunthorpe 
749080b47deSLogan Gunthorpe out:
750080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
751080b47deSLogan Gunthorpe 
752080b47deSLogan Gunthorpe 	if (stuser->status == SWITCHTEC_MRPC_STATUS_DONE)
753080b47deSLogan Gunthorpe 		return size;
754080b47deSLogan Gunthorpe 	else if (stuser->status == SWITCHTEC_MRPC_STATUS_INTERRUPTED)
755080b47deSLogan Gunthorpe 		return -ENXIO;
756080b47deSLogan Gunthorpe 	else
757080b47deSLogan Gunthorpe 		return -EBADMSG;
758080b47deSLogan Gunthorpe }
759080b47deSLogan Gunthorpe 
760080b47deSLogan Gunthorpe static unsigned int switchtec_dev_poll(struct file *filp, poll_table *wait)
761080b47deSLogan Gunthorpe {
762080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
763080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
764080b47deSLogan Gunthorpe 	int ret = 0;
765080b47deSLogan Gunthorpe 
766080b47deSLogan Gunthorpe 	poll_wait(filp, &stuser->comp.wait, wait);
767080b47deSLogan Gunthorpe 	poll_wait(filp, &stdev->event_wq, wait);
768080b47deSLogan Gunthorpe 
769080b47deSLogan Gunthorpe 	if (lock_mutex_and_test_alive(stdev))
770080b47deSLogan Gunthorpe 		return POLLIN | POLLRDHUP | POLLOUT | POLLERR | POLLHUP;
771080b47deSLogan Gunthorpe 
772080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
773080b47deSLogan Gunthorpe 
774080b47deSLogan Gunthorpe 	if (try_wait_for_completion(&stuser->comp))
775080b47deSLogan Gunthorpe 		ret |= POLLIN | POLLRDNORM;
776080b47deSLogan Gunthorpe 
777080b47deSLogan Gunthorpe 	if (stuser->event_cnt != atomic_read(&stdev->event_cnt))
778080b47deSLogan Gunthorpe 		ret |= POLLPRI | POLLRDBAND;
779080b47deSLogan Gunthorpe 
780080b47deSLogan Gunthorpe 	return ret;
781080b47deSLogan Gunthorpe }
782080b47deSLogan Gunthorpe 
78352eabba5SLogan Gunthorpe static int ioctl_flash_info(struct switchtec_dev *stdev,
78452eabba5SLogan Gunthorpe 			    struct switchtec_ioctl_flash_info __user *uinfo)
78552eabba5SLogan Gunthorpe {
78652eabba5SLogan Gunthorpe 	struct switchtec_ioctl_flash_info info = {0};
78752eabba5SLogan Gunthorpe 	struct flash_info_regs __iomem *fi = stdev->mmio_flash_info;
78852eabba5SLogan Gunthorpe 
78952eabba5SLogan Gunthorpe 	info.flash_length = ioread32(&fi->flash_length);
79052eabba5SLogan Gunthorpe 	info.num_partitions = SWITCHTEC_IOCTL_NUM_PARTITIONS;
79152eabba5SLogan Gunthorpe 
79252eabba5SLogan Gunthorpe 	if (copy_to_user(uinfo, &info, sizeof(info)))
79352eabba5SLogan Gunthorpe 		return -EFAULT;
79452eabba5SLogan Gunthorpe 
79552eabba5SLogan Gunthorpe 	return 0;
79652eabba5SLogan Gunthorpe }
79752eabba5SLogan Gunthorpe 
79852eabba5SLogan Gunthorpe static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info,
79952eabba5SLogan Gunthorpe 			     struct partition_info __iomem *pi)
80052eabba5SLogan Gunthorpe {
80152eabba5SLogan Gunthorpe 	info->address = ioread32(&pi->address);
80252eabba5SLogan Gunthorpe 	info->length = ioread32(&pi->length);
80352eabba5SLogan Gunthorpe }
80452eabba5SLogan Gunthorpe 
80552eabba5SLogan Gunthorpe static int ioctl_flash_part_info(struct switchtec_dev *stdev,
80652eabba5SLogan Gunthorpe 	struct switchtec_ioctl_flash_part_info __user *uinfo)
80752eabba5SLogan Gunthorpe {
80852eabba5SLogan Gunthorpe 	struct switchtec_ioctl_flash_part_info info = {0};
80952eabba5SLogan Gunthorpe 	struct flash_info_regs __iomem *fi = stdev->mmio_flash_info;
81052eabba5SLogan Gunthorpe 	u32 active_addr = -1;
81152eabba5SLogan Gunthorpe 
81252eabba5SLogan Gunthorpe 	if (copy_from_user(&info, uinfo, sizeof(info)))
81352eabba5SLogan Gunthorpe 		return -EFAULT;
81452eabba5SLogan Gunthorpe 
81552eabba5SLogan Gunthorpe 	switch (info.flash_partition) {
81652eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_CFG0:
81752eabba5SLogan Gunthorpe 		active_addr = ioread32(&fi->active_cfg);
81852eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->cfg0);
81952eabba5SLogan Gunthorpe 		break;
82052eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_CFG1:
82152eabba5SLogan Gunthorpe 		active_addr = ioread32(&fi->active_cfg);
82252eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->cfg1);
82352eabba5SLogan Gunthorpe 		break;
82452eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_IMG0:
82552eabba5SLogan Gunthorpe 		active_addr = ioread32(&fi->active_img);
82652eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->img0);
82752eabba5SLogan Gunthorpe 		break;
82852eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_IMG1:
82952eabba5SLogan Gunthorpe 		active_addr = ioread32(&fi->active_img);
83052eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->img1);
83152eabba5SLogan Gunthorpe 		break;
83252eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_NVLOG:
83352eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->nvlog);
83452eabba5SLogan Gunthorpe 		break;
83552eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR0:
83652eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[0]);
83752eabba5SLogan Gunthorpe 		break;
83852eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR1:
83952eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[1]);
84052eabba5SLogan Gunthorpe 		break;
84152eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR2:
84252eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[2]);
84352eabba5SLogan Gunthorpe 		break;
84452eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR3:
84552eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[3]);
84652eabba5SLogan Gunthorpe 		break;
84752eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR4:
84852eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[4]);
84952eabba5SLogan Gunthorpe 		break;
85052eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR5:
85152eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[5]);
85252eabba5SLogan Gunthorpe 		break;
85352eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR6:
85452eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[6]);
85552eabba5SLogan Gunthorpe 		break;
85652eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PART_VENDOR7:
85752eabba5SLogan Gunthorpe 		set_fw_info_part(&info, &fi->vendor[7]);
85852eabba5SLogan Gunthorpe 		break;
85952eabba5SLogan Gunthorpe 	default:
86052eabba5SLogan Gunthorpe 		return -EINVAL;
86152eabba5SLogan Gunthorpe 	}
86252eabba5SLogan Gunthorpe 
86352eabba5SLogan Gunthorpe 	if (info.address == active_addr)
86452eabba5SLogan Gunthorpe 		info.active = 1;
86552eabba5SLogan Gunthorpe 
86652eabba5SLogan Gunthorpe 	if (copy_to_user(uinfo, &info, sizeof(info)))
86752eabba5SLogan Gunthorpe 		return -EFAULT;
86852eabba5SLogan Gunthorpe 
86952eabba5SLogan Gunthorpe 	return 0;
87052eabba5SLogan Gunthorpe }
87152eabba5SLogan Gunthorpe 
87252eabba5SLogan Gunthorpe static int ioctl_event_summary(struct switchtec_dev *stdev,
87352eabba5SLogan Gunthorpe 	struct switchtec_user *stuser,
87452eabba5SLogan Gunthorpe 	struct switchtec_ioctl_event_summary __user *usum)
87552eabba5SLogan Gunthorpe {
87652eabba5SLogan Gunthorpe 	struct switchtec_ioctl_event_summary s = {0};
87752eabba5SLogan Gunthorpe 	int i;
87852eabba5SLogan Gunthorpe 	u32 reg;
87952eabba5SLogan Gunthorpe 
88052eabba5SLogan Gunthorpe 	s.global = ioread32(&stdev->mmio_sw_event->global_summary);
88152eabba5SLogan Gunthorpe 	s.part_bitmap = ioread32(&stdev->mmio_sw_event->part_event_bitmap);
88252eabba5SLogan Gunthorpe 	s.local_part = ioread32(&stdev->mmio_part_cfg->part_event_summary);
88352eabba5SLogan Gunthorpe 
88452eabba5SLogan Gunthorpe 	for (i = 0; i < stdev->partition_count; i++) {
88552eabba5SLogan Gunthorpe 		reg = ioread32(&stdev->mmio_part_cfg_all[i].part_event_summary);
88652eabba5SLogan Gunthorpe 		s.part[i] = reg;
88752eabba5SLogan Gunthorpe 	}
88852eabba5SLogan Gunthorpe 
88952eabba5SLogan Gunthorpe 	for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
89052eabba5SLogan Gunthorpe 		reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id);
89152eabba5SLogan Gunthorpe 		if (reg != MICROSEMI_VENDOR_ID)
89252eabba5SLogan Gunthorpe 			break;
89352eabba5SLogan Gunthorpe 
89452eabba5SLogan Gunthorpe 		reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary);
89552eabba5SLogan Gunthorpe 		s.pff[i] = reg;
89652eabba5SLogan Gunthorpe 	}
89752eabba5SLogan Gunthorpe 
89852eabba5SLogan Gunthorpe 	if (copy_to_user(usum, &s, sizeof(s)))
89952eabba5SLogan Gunthorpe 		return -EFAULT;
90052eabba5SLogan Gunthorpe 
90152eabba5SLogan Gunthorpe 	stuser->event_cnt = atomic_read(&stdev->event_cnt);
90252eabba5SLogan Gunthorpe 
90352eabba5SLogan Gunthorpe 	return 0;
90452eabba5SLogan Gunthorpe }
90552eabba5SLogan Gunthorpe 
90652eabba5SLogan Gunthorpe static u32 __iomem *global_ev_reg(struct switchtec_dev *stdev,
90752eabba5SLogan Gunthorpe 				  size_t offset, int index)
90852eabba5SLogan Gunthorpe {
90952eabba5SLogan Gunthorpe 	return (void __iomem *)stdev->mmio_sw_event + offset;
91052eabba5SLogan Gunthorpe }
91152eabba5SLogan Gunthorpe 
91252eabba5SLogan Gunthorpe static u32 __iomem *part_ev_reg(struct switchtec_dev *stdev,
91352eabba5SLogan Gunthorpe 				size_t offset, int index)
91452eabba5SLogan Gunthorpe {
91552eabba5SLogan Gunthorpe 	return (void __iomem *)&stdev->mmio_part_cfg_all[index] + offset;
91652eabba5SLogan Gunthorpe }
91752eabba5SLogan Gunthorpe 
91852eabba5SLogan Gunthorpe static u32 __iomem *pff_ev_reg(struct switchtec_dev *stdev,
91952eabba5SLogan Gunthorpe 			       size_t offset, int index)
92052eabba5SLogan Gunthorpe {
92152eabba5SLogan Gunthorpe 	return (void __iomem *)&stdev->mmio_pff_csr[index] + offset;
92252eabba5SLogan Gunthorpe }
92352eabba5SLogan Gunthorpe 
92452eabba5SLogan Gunthorpe #define EV_GLB(i, r)[i] = {offsetof(struct sw_event_regs, r), global_ev_reg}
92552eabba5SLogan Gunthorpe #define EV_PAR(i, r)[i] = {offsetof(struct part_cfg_regs, r), part_ev_reg}
92652eabba5SLogan Gunthorpe #define EV_PFF(i, r)[i] = {offsetof(struct pff_csr_regs, r), pff_ev_reg}
92752eabba5SLogan Gunthorpe 
92852eabba5SLogan Gunthorpe const struct event_reg {
92952eabba5SLogan Gunthorpe 	size_t offset;
93052eabba5SLogan Gunthorpe 	u32 __iomem *(*map_reg)(struct switchtec_dev *stdev,
93152eabba5SLogan Gunthorpe 				size_t offset, int index);
93252eabba5SLogan Gunthorpe } event_regs[] = {
93352eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_STACK_ERROR, stack_error_event_hdr),
93452eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_PPU_ERROR, ppu_error_event_hdr),
93552eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_ISP_ERROR, isp_error_event_hdr),
93652eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_SYS_RESET, sys_reset_event_hdr),
93752eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_EXC, fw_exception_hdr),
93852eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NMI, fw_nmi_hdr),
93952eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_NON_FATAL, fw_non_fatal_hdr),
94052eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_FW_FATAL, fw_fatal_hdr),
94152eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP, twi_mrpc_comp_hdr),
94252eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_TWI_MRPC_COMP_ASYNC,
94352eabba5SLogan Gunthorpe 	       twi_mrpc_comp_async_hdr),
94452eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP, cli_mrpc_comp_hdr),
94552eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_CLI_MRPC_COMP_ASYNC,
94652eabba5SLogan Gunthorpe 	       cli_mrpc_comp_async_hdr),
94752eabba5SLogan Gunthorpe 	EV_GLB(SWITCHTEC_IOCTL_EVENT_GPIO_INT, gpio_interrupt_hdr),
94852eabba5SLogan Gunthorpe 	EV_PAR(SWITCHTEC_IOCTL_EVENT_PART_RESET, part_reset_hdr),
94952eabba5SLogan Gunthorpe 	EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP, mrpc_comp_hdr),
95052eabba5SLogan Gunthorpe 	EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC, mrpc_comp_async_hdr),
95152eabba5SLogan Gunthorpe 	EV_PAR(SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP, dyn_binding_hdr),
95252eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_P2P, aer_in_p2p_hdr),
95352eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_VEP, aer_in_vep_hdr),
95452eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_DPC, dpc_hdr),
95552eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_CTS, cts_hdr),
95652eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_HOTPLUG, hotplug_hdr),
95752eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_IER, ier_hdr),
95852eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_THRESH, threshold_hdr),
95952eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_POWER_MGMT, power_mgmt_hdr),
96052eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_TLP_THROTTLING, tlp_throttling_hdr),
96152eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_FORCE_SPEED, force_speed_hdr),
96252eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT, credit_timeout_hdr),
96352eabba5SLogan Gunthorpe 	EV_PFF(SWITCHTEC_IOCTL_EVENT_LINK_STATE, link_state_hdr),
96452eabba5SLogan Gunthorpe };
96552eabba5SLogan Gunthorpe 
96652eabba5SLogan Gunthorpe static u32 __iomem *event_hdr_addr(struct switchtec_dev *stdev,
96752eabba5SLogan Gunthorpe 				   int event_id, int index)
96852eabba5SLogan Gunthorpe {
96952eabba5SLogan Gunthorpe 	size_t off;
97052eabba5SLogan Gunthorpe 
97152eabba5SLogan Gunthorpe 	if (event_id < 0 || event_id >= SWITCHTEC_IOCTL_MAX_EVENTS)
97252eabba5SLogan Gunthorpe 		return ERR_PTR(-EINVAL);
97352eabba5SLogan Gunthorpe 
97452eabba5SLogan Gunthorpe 	off = event_regs[event_id].offset;
97552eabba5SLogan Gunthorpe 
97652eabba5SLogan Gunthorpe 	if (event_regs[event_id].map_reg == part_ev_reg) {
97752eabba5SLogan Gunthorpe 		if (index == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX)
97852eabba5SLogan Gunthorpe 			index = stdev->partition;
97952eabba5SLogan Gunthorpe 		else if (index < 0 || index >= stdev->partition_count)
98052eabba5SLogan Gunthorpe 			return ERR_PTR(-EINVAL);
98152eabba5SLogan Gunthorpe 	} else if (event_regs[event_id].map_reg == pff_ev_reg) {
98252eabba5SLogan Gunthorpe 		if (index < 0 || index >= stdev->pff_csr_count)
98352eabba5SLogan Gunthorpe 			return ERR_PTR(-EINVAL);
98452eabba5SLogan Gunthorpe 	}
98552eabba5SLogan Gunthorpe 
98652eabba5SLogan Gunthorpe 	return event_regs[event_id].map_reg(stdev, off, index);
98752eabba5SLogan Gunthorpe }
98852eabba5SLogan Gunthorpe 
98952eabba5SLogan Gunthorpe static int event_ctl(struct switchtec_dev *stdev,
99052eabba5SLogan Gunthorpe 		     struct switchtec_ioctl_event_ctl *ctl)
99152eabba5SLogan Gunthorpe {
99252eabba5SLogan Gunthorpe 	int i;
99352eabba5SLogan Gunthorpe 	u32 __iomem *reg;
99452eabba5SLogan Gunthorpe 	u32 hdr;
99552eabba5SLogan Gunthorpe 
99652eabba5SLogan Gunthorpe 	reg = event_hdr_addr(stdev, ctl->event_id, ctl->index);
99752eabba5SLogan Gunthorpe 	if (IS_ERR(reg))
99852eabba5SLogan Gunthorpe 		return PTR_ERR(reg);
99952eabba5SLogan Gunthorpe 
100052eabba5SLogan Gunthorpe 	hdr = ioread32(reg);
100152eabba5SLogan Gunthorpe 	for (i = 0; i < ARRAY_SIZE(ctl->data); i++)
100252eabba5SLogan Gunthorpe 		ctl->data[i] = ioread32(&reg[i + 1]);
100352eabba5SLogan Gunthorpe 
100452eabba5SLogan Gunthorpe 	ctl->occurred = hdr & SWITCHTEC_EVENT_OCCURRED;
100552eabba5SLogan Gunthorpe 	ctl->count = (hdr >> 5) & 0xFF;
100652eabba5SLogan Gunthorpe 
100752eabba5SLogan Gunthorpe 	if (!(ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_CLEAR))
100852eabba5SLogan Gunthorpe 		hdr &= ~SWITCHTEC_EVENT_CLEAR;
100952eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL)
101052eabba5SLogan Gunthorpe 		hdr |= SWITCHTEC_EVENT_EN_IRQ;
101152eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_POLL)
101252eabba5SLogan Gunthorpe 		hdr &= ~SWITCHTEC_EVENT_EN_IRQ;
101352eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG)
101452eabba5SLogan Gunthorpe 		hdr |= SWITCHTEC_EVENT_EN_LOG;
101552eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_LOG)
101652eabba5SLogan Gunthorpe 		hdr &= ~SWITCHTEC_EVENT_EN_LOG;
101752eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI)
101852eabba5SLogan Gunthorpe 		hdr |= SWITCHTEC_EVENT_EN_CLI;
101952eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_CLI)
102052eabba5SLogan Gunthorpe 		hdr &= ~SWITCHTEC_EVENT_EN_CLI;
102152eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL)
102252eabba5SLogan Gunthorpe 		hdr |= SWITCHTEC_EVENT_FATAL;
102352eabba5SLogan Gunthorpe 	if (ctl->flags & SWITCHTEC_IOCTL_EVENT_FLAG_DIS_FATAL)
102452eabba5SLogan Gunthorpe 		hdr &= ~SWITCHTEC_EVENT_FATAL;
102552eabba5SLogan Gunthorpe 
102652eabba5SLogan Gunthorpe 	if (ctl->flags)
102752eabba5SLogan Gunthorpe 		iowrite32(hdr, reg);
102852eabba5SLogan Gunthorpe 
102952eabba5SLogan Gunthorpe 	ctl->flags = 0;
103052eabba5SLogan Gunthorpe 	if (hdr & SWITCHTEC_EVENT_EN_IRQ)
103152eabba5SLogan Gunthorpe 		ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_POLL;
103252eabba5SLogan Gunthorpe 	if (hdr & SWITCHTEC_EVENT_EN_LOG)
103352eabba5SLogan Gunthorpe 		ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_LOG;
103452eabba5SLogan Gunthorpe 	if (hdr & SWITCHTEC_EVENT_EN_CLI)
103552eabba5SLogan Gunthorpe 		ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_CLI;
103652eabba5SLogan Gunthorpe 	if (hdr & SWITCHTEC_EVENT_FATAL)
103752eabba5SLogan Gunthorpe 		ctl->flags |= SWITCHTEC_IOCTL_EVENT_FLAG_EN_FATAL;
103852eabba5SLogan Gunthorpe 
103952eabba5SLogan Gunthorpe 	return 0;
104052eabba5SLogan Gunthorpe }
104152eabba5SLogan Gunthorpe 
104252eabba5SLogan Gunthorpe static int ioctl_event_ctl(struct switchtec_dev *stdev,
104352eabba5SLogan Gunthorpe 	struct switchtec_ioctl_event_ctl __user *uctl)
104452eabba5SLogan Gunthorpe {
104552eabba5SLogan Gunthorpe 	int ret;
104652eabba5SLogan Gunthorpe 	int nr_idxs;
104752eabba5SLogan Gunthorpe 	struct switchtec_ioctl_event_ctl ctl;
104852eabba5SLogan Gunthorpe 
104952eabba5SLogan Gunthorpe 	if (copy_from_user(&ctl, uctl, sizeof(ctl)))
105052eabba5SLogan Gunthorpe 		return -EFAULT;
105152eabba5SLogan Gunthorpe 
105252eabba5SLogan Gunthorpe 	if (ctl.event_id >= SWITCHTEC_IOCTL_MAX_EVENTS)
105352eabba5SLogan Gunthorpe 		return -EINVAL;
105452eabba5SLogan Gunthorpe 
105552eabba5SLogan Gunthorpe 	if (ctl.flags & SWITCHTEC_IOCTL_EVENT_FLAG_UNUSED)
105652eabba5SLogan Gunthorpe 		return -EINVAL;
105752eabba5SLogan Gunthorpe 
105852eabba5SLogan Gunthorpe 	if (ctl.index == SWITCHTEC_IOCTL_EVENT_IDX_ALL) {
105952eabba5SLogan Gunthorpe 		if (event_regs[ctl.event_id].map_reg == global_ev_reg)
106052eabba5SLogan Gunthorpe 			nr_idxs = 1;
106152eabba5SLogan Gunthorpe 		else if (event_regs[ctl.event_id].map_reg == part_ev_reg)
106252eabba5SLogan Gunthorpe 			nr_idxs = stdev->partition_count;
106352eabba5SLogan Gunthorpe 		else if (event_regs[ctl.event_id].map_reg == pff_ev_reg)
106452eabba5SLogan Gunthorpe 			nr_idxs = stdev->pff_csr_count;
106552eabba5SLogan Gunthorpe 		else
106652eabba5SLogan Gunthorpe 			return -EINVAL;
106752eabba5SLogan Gunthorpe 
106852eabba5SLogan Gunthorpe 		for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) {
106952eabba5SLogan Gunthorpe 			ret = event_ctl(stdev, &ctl);
107052eabba5SLogan Gunthorpe 			if (ret < 0)
107152eabba5SLogan Gunthorpe 				return ret;
107252eabba5SLogan Gunthorpe 		}
107352eabba5SLogan Gunthorpe 	} else {
107452eabba5SLogan Gunthorpe 		ret = event_ctl(stdev, &ctl);
107552eabba5SLogan Gunthorpe 		if (ret < 0)
107652eabba5SLogan Gunthorpe 			return ret;
107752eabba5SLogan Gunthorpe 	}
107852eabba5SLogan Gunthorpe 
107952eabba5SLogan Gunthorpe 	if (copy_to_user(uctl, &ctl, sizeof(ctl)))
108052eabba5SLogan Gunthorpe 		return -EFAULT;
108152eabba5SLogan Gunthorpe 
108252eabba5SLogan Gunthorpe 	return 0;
108352eabba5SLogan Gunthorpe }
108452eabba5SLogan Gunthorpe 
108552eabba5SLogan Gunthorpe static int ioctl_pff_to_port(struct switchtec_dev *stdev,
108652eabba5SLogan Gunthorpe 			     struct switchtec_ioctl_pff_port *up)
108752eabba5SLogan Gunthorpe {
108852eabba5SLogan Gunthorpe 	int i, part;
108952eabba5SLogan Gunthorpe 	u32 reg;
109052eabba5SLogan Gunthorpe 	struct part_cfg_regs *pcfg;
109152eabba5SLogan Gunthorpe 	struct switchtec_ioctl_pff_port p;
109252eabba5SLogan Gunthorpe 
109352eabba5SLogan Gunthorpe 	if (copy_from_user(&p, up, sizeof(p)))
109452eabba5SLogan Gunthorpe 		return -EFAULT;
109552eabba5SLogan Gunthorpe 
109652eabba5SLogan Gunthorpe 	p.port = -1;
109752eabba5SLogan Gunthorpe 	for (part = 0; part < stdev->partition_count; part++) {
109852eabba5SLogan Gunthorpe 		pcfg = &stdev->mmio_part_cfg_all[part];
109952eabba5SLogan Gunthorpe 		p.partition = part;
110052eabba5SLogan Gunthorpe 
110152eabba5SLogan Gunthorpe 		reg = ioread32(&pcfg->usp_pff_inst_id);
110252eabba5SLogan Gunthorpe 		if (reg == p.pff) {
110352eabba5SLogan Gunthorpe 			p.port = 0;
110452eabba5SLogan Gunthorpe 			break;
110552eabba5SLogan Gunthorpe 		}
110652eabba5SLogan Gunthorpe 
110752eabba5SLogan Gunthorpe 		reg = ioread32(&pcfg->vep_pff_inst_id);
110852eabba5SLogan Gunthorpe 		if (reg == p.pff) {
110952eabba5SLogan Gunthorpe 			p.port = SWITCHTEC_IOCTL_PFF_VEP;
111052eabba5SLogan Gunthorpe 			break;
111152eabba5SLogan Gunthorpe 		}
111252eabba5SLogan Gunthorpe 
111352eabba5SLogan Gunthorpe 		for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) {
111452eabba5SLogan Gunthorpe 			reg = ioread32(&pcfg->dsp_pff_inst_id[i]);
111552eabba5SLogan Gunthorpe 			if (reg != p.pff)
111652eabba5SLogan Gunthorpe 				continue;
111752eabba5SLogan Gunthorpe 
111852eabba5SLogan Gunthorpe 			p.port = i + 1;
111952eabba5SLogan Gunthorpe 			break;
112052eabba5SLogan Gunthorpe 		}
112152eabba5SLogan Gunthorpe 
112252eabba5SLogan Gunthorpe 		if (p.port != -1)
112352eabba5SLogan Gunthorpe 			break;
112452eabba5SLogan Gunthorpe 	}
112552eabba5SLogan Gunthorpe 
112652eabba5SLogan Gunthorpe 	if (copy_to_user(up, &p, sizeof(p)))
112752eabba5SLogan Gunthorpe 		return -EFAULT;
112852eabba5SLogan Gunthorpe 
112952eabba5SLogan Gunthorpe 	return 0;
113052eabba5SLogan Gunthorpe }
113152eabba5SLogan Gunthorpe 
113252eabba5SLogan Gunthorpe static int ioctl_port_to_pff(struct switchtec_dev *stdev,
113352eabba5SLogan Gunthorpe 			     struct switchtec_ioctl_pff_port *up)
113452eabba5SLogan Gunthorpe {
113552eabba5SLogan Gunthorpe 	struct switchtec_ioctl_pff_port p;
113652eabba5SLogan Gunthorpe 	struct part_cfg_regs *pcfg;
113752eabba5SLogan Gunthorpe 
113852eabba5SLogan Gunthorpe 	if (copy_from_user(&p, up, sizeof(p)))
113952eabba5SLogan Gunthorpe 		return -EFAULT;
114052eabba5SLogan Gunthorpe 
114152eabba5SLogan Gunthorpe 	if (p.partition == SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX)
114252eabba5SLogan Gunthorpe 		pcfg = stdev->mmio_part_cfg;
114352eabba5SLogan Gunthorpe 	else if (p.partition < stdev->partition_count)
114452eabba5SLogan Gunthorpe 		pcfg = &stdev->mmio_part_cfg_all[p.partition];
114552eabba5SLogan Gunthorpe 	else
114652eabba5SLogan Gunthorpe 		return -EINVAL;
114752eabba5SLogan Gunthorpe 
114852eabba5SLogan Gunthorpe 	switch (p.port) {
114952eabba5SLogan Gunthorpe 	case 0:
115052eabba5SLogan Gunthorpe 		p.pff = ioread32(&pcfg->usp_pff_inst_id);
115152eabba5SLogan Gunthorpe 		break;
115252eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PFF_VEP:
115352eabba5SLogan Gunthorpe 		p.pff = ioread32(&pcfg->vep_pff_inst_id);
115452eabba5SLogan Gunthorpe 		break;
115552eabba5SLogan Gunthorpe 	default:
115652eabba5SLogan Gunthorpe 		if (p.port > ARRAY_SIZE(pcfg->dsp_pff_inst_id))
115752eabba5SLogan Gunthorpe 			return -EINVAL;
115852eabba5SLogan Gunthorpe 		p.pff = ioread32(&pcfg->dsp_pff_inst_id[p.port - 1]);
115952eabba5SLogan Gunthorpe 		break;
116052eabba5SLogan Gunthorpe 	}
116152eabba5SLogan Gunthorpe 
116252eabba5SLogan Gunthorpe 	if (copy_to_user(up, &p, sizeof(p)))
116352eabba5SLogan Gunthorpe 		return -EFAULT;
116452eabba5SLogan Gunthorpe 
116552eabba5SLogan Gunthorpe 	return 0;
116652eabba5SLogan Gunthorpe }
116752eabba5SLogan Gunthorpe 
116852eabba5SLogan Gunthorpe static long switchtec_dev_ioctl(struct file *filp, unsigned int cmd,
116952eabba5SLogan Gunthorpe 				unsigned long arg)
117052eabba5SLogan Gunthorpe {
117152eabba5SLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
117252eabba5SLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
117352eabba5SLogan Gunthorpe 	int rc;
117452eabba5SLogan Gunthorpe 	void __user *argp = (void __user *)arg;
117552eabba5SLogan Gunthorpe 
117652eabba5SLogan Gunthorpe 	rc = lock_mutex_and_test_alive(stdev);
117752eabba5SLogan Gunthorpe 	if (rc)
117852eabba5SLogan Gunthorpe 		return rc;
117952eabba5SLogan Gunthorpe 
118052eabba5SLogan Gunthorpe 	switch (cmd) {
118152eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_FLASH_INFO:
118252eabba5SLogan Gunthorpe 		rc = ioctl_flash_info(stdev, argp);
118352eabba5SLogan Gunthorpe 		break;
118452eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_FLASH_PART_INFO:
118552eabba5SLogan Gunthorpe 		rc = ioctl_flash_part_info(stdev, argp);
118652eabba5SLogan Gunthorpe 		break;
118752eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_EVENT_SUMMARY:
118852eabba5SLogan Gunthorpe 		rc = ioctl_event_summary(stdev, stuser, argp);
118952eabba5SLogan Gunthorpe 		break;
119052eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_EVENT_CTL:
119152eabba5SLogan Gunthorpe 		rc = ioctl_event_ctl(stdev, argp);
119252eabba5SLogan Gunthorpe 		break;
119352eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PFF_TO_PORT:
119452eabba5SLogan Gunthorpe 		rc = ioctl_pff_to_port(stdev, argp);
119552eabba5SLogan Gunthorpe 		break;
119652eabba5SLogan Gunthorpe 	case SWITCHTEC_IOCTL_PORT_TO_PFF:
119752eabba5SLogan Gunthorpe 		rc = ioctl_port_to_pff(stdev, argp);
119852eabba5SLogan Gunthorpe 		break;
119952eabba5SLogan Gunthorpe 	default:
120052eabba5SLogan Gunthorpe 		rc = -ENOTTY;
120152eabba5SLogan Gunthorpe 		break;
120252eabba5SLogan Gunthorpe 	}
120352eabba5SLogan Gunthorpe 
120452eabba5SLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
120552eabba5SLogan Gunthorpe 	return rc;
120652eabba5SLogan Gunthorpe }
120752eabba5SLogan Gunthorpe 
1208080b47deSLogan Gunthorpe static const struct file_operations switchtec_fops = {
1209080b47deSLogan Gunthorpe 	.owner = THIS_MODULE,
1210080b47deSLogan Gunthorpe 	.open = switchtec_dev_open,
1211080b47deSLogan Gunthorpe 	.release = switchtec_dev_release,
1212080b47deSLogan Gunthorpe 	.write = switchtec_dev_write,
1213080b47deSLogan Gunthorpe 	.read = switchtec_dev_read,
1214080b47deSLogan Gunthorpe 	.poll = switchtec_dev_poll,
121552eabba5SLogan Gunthorpe 	.unlocked_ioctl = switchtec_dev_ioctl,
121652eabba5SLogan Gunthorpe 	.compat_ioctl = switchtec_dev_ioctl,
1217080b47deSLogan Gunthorpe };
1218080b47deSLogan Gunthorpe 
1219080b47deSLogan Gunthorpe static void stdev_release(struct device *dev)
1220080b47deSLogan Gunthorpe {
1221080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
1222080b47deSLogan Gunthorpe 
1223080b47deSLogan Gunthorpe 	kfree(stdev);
1224080b47deSLogan Gunthorpe }
1225080b47deSLogan Gunthorpe 
1226080b47deSLogan Gunthorpe static void stdev_kill(struct switchtec_dev *stdev)
1227080b47deSLogan Gunthorpe {
1228080b47deSLogan Gunthorpe 	struct switchtec_user *stuser, *tmpuser;
1229080b47deSLogan Gunthorpe 
1230080b47deSLogan Gunthorpe 	pci_clear_master(stdev->pdev);
1231080b47deSLogan Gunthorpe 
1232080b47deSLogan Gunthorpe 	cancel_delayed_work_sync(&stdev->mrpc_timeout);
1233080b47deSLogan Gunthorpe 
1234080b47deSLogan Gunthorpe 	/* Mark the hardware as unavailable and complete all completions */
1235080b47deSLogan Gunthorpe 	mutex_lock(&stdev->mrpc_mutex);
1236080b47deSLogan Gunthorpe 	stdev->alive = false;
1237080b47deSLogan Gunthorpe 
1238080b47deSLogan Gunthorpe 	/* Wake up and kill any users waiting on an MRPC request */
1239080b47deSLogan Gunthorpe 	list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) {
1240080b47deSLogan Gunthorpe 		complete_all(&stuser->comp);
1241080b47deSLogan Gunthorpe 		list_del_init(&stuser->list);
1242080b47deSLogan Gunthorpe 		stuser_put(stuser);
1243080b47deSLogan Gunthorpe 	}
1244080b47deSLogan Gunthorpe 
1245080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
1246080b47deSLogan Gunthorpe 
1247080b47deSLogan Gunthorpe 	/* Wake up any users waiting on event_wq */
1248080b47deSLogan Gunthorpe 	wake_up_interruptible(&stdev->event_wq);
1249080b47deSLogan Gunthorpe }
1250080b47deSLogan Gunthorpe 
1251080b47deSLogan Gunthorpe static struct switchtec_dev *stdev_create(struct pci_dev *pdev)
1252080b47deSLogan Gunthorpe {
1253080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
1254080b47deSLogan Gunthorpe 	int minor;
1255080b47deSLogan Gunthorpe 	struct device *dev;
1256080b47deSLogan Gunthorpe 	struct cdev *cdev;
1257080b47deSLogan Gunthorpe 	int rc;
1258080b47deSLogan Gunthorpe 
1259080b47deSLogan Gunthorpe 	stdev = kzalloc_node(sizeof(*stdev), GFP_KERNEL,
1260080b47deSLogan Gunthorpe 			     dev_to_node(&pdev->dev));
1261080b47deSLogan Gunthorpe 	if (!stdev)
1262080b47deSLogan Gunthorpe 		return ERR_PTR(-ENOMEM);
1263080b47deSLogan Gunthorpe 
1264080b47deSLogan Gunthorpe 	stdev->alive = true;
1265080b47deSLogan Gunthorpe 	stdev->pdev = pdev;
1266080b47deSLogan Gunthorpe 	INIT_LIST_HEAD(&stdev->mrpc_queue);
1267080b47deSLogan Gunthorpe 	mutex_init(&stdev->mrpc_mutex);
1268080b47deSLogan Gunthorpe 	stdev->mrpc_busy = 0;
1269080b47deSLogan Gunthorpe 	INIT_WORK(&stdev->mrpc_work, mrpc_event_work);
1270080b47deSLogan Gunthorpe 	INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work);
1271080b47deSLogan Gunthorpe 	init_waitqueue_head(&stdev->event_wq);
1272080b47deSLogan Gunthorpe 	atomic_set(&stdev->event_cnt, 0);
1273080b47deSLogan Gunthorpe 
1274080b47deSLogan Gunthorpe 	dev = &stdev->dev;
1275080b47deSLogan Gunthorpe 	device_initialize(dev);
1276080b47deSLogan Gunthorpe 	dev->class = switchtec_class;
1277080b47deSLogan Gunthorpe 	dev->parent = &pdev->dev;
12785d8e1881SLogan Gunthorpe 	dev->groups = switchtec_device_groups;
1279080b47deSLogan Gunthorpe 	dev->release = stdev_release;
1280080b47deSLogan Gunthorpe 
1281080b47deSLogan Gunthorpe 	minor = ida_simple_get(&switchtec_minor_ida, 0, 0,
1282080b47deSLogan Gunthorpe 			       GFP_KERNEL);
1283080b47deSLogan Gunthorpe 	if (minor < 0) {
1284080b47deSLogan Gunthorpe 		rc = minor;
1285080b47deSLogan Gunthorpe 		goto err_put;
1286080b47deSLogan Gunthorpe 	}
1287080b47deSLogan Gunthorpe 
1288080b47deSLogan Gunthorpe 	dev->devt = MKDEV(MAJOR(switchtec_devt), minor);
1289080b47deSLogan Gunthorpe 	dev_set_name(dev, "switchtec%d", minor);
1290080b47deSLogan Gunthorpe 
1291080b47deSLogan Gunthorpe 	cdev = &stdev->cdev;
1292080b47deSLogan Gunthorpe 	cdev_init(cdev, &switchtec_fops);
1293080b47deSLogan Gunthorpe 	cdev->owner = THIS_MODULE;
1294080b47deSLogan Gunthorpe 
1295080b47deSLogan Gunthorpe 	return stdev;
1296080b47deSLogan Gunthorpe 
1297080b47deSLogan Gunthorpe err_put:
1298080b47deSLogan Gunthorpe 	put_device(&stdev->dev);
1299080b47deSLogan Gunthorpe 	return ERR_PTR(rc);
1300080b47deSLogan Gunthorpe }
1301080b47deSLogan Gunthorpe 
130252eabba5SLogan Gunthorpe static int mask_event(struct switchtec_dev *stdev, int eid, int idx)
130352eabba5SLogan Gunthorpe {
130452eabba5SLogan Gunthorpe 	size_t off = event_regs[eid].offset;
130552eabba5SLogan Gunthorpe 	u32 __iomem *hdr_reg;
130652eabba5SLogan Gunthorpe 	u32 hdr;
130752eabba5SLogan Gunthorpe 
130852eabba5SLogan Gunthorpe 	hdr_reg = event_regs[eid].map_reg(stdev, off, idx);
130952eabba5SLogan Gunthorpe 	hdr = ioread32(hdr_reg);
131052eabba5SLogan Gunthorpe 
131152eabba5SLogan Gunthorpe 	if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ))
131252eabba5SLogan Gunthorpe 		return 0;
131352eabba5SLogan Gunthorpe 
131452eabba5SLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr);
131552eabba5SLogan Gunthorpe 	hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED);
131652eabba5SLogan Gunthorpe 	iowrite32(hdr, hdr_reg);
131752eabba5SLogan Gunthorpe 
131852eabba5SLogan Gunthorpe 	return 1;
131952eabba5SLogan Gunthorpe }
132052eabba5SLogan Gunthorpe 
132152eabba5SLogan Gunthorpe static int mask_all_events(struct switchtec_dev *stdev, int eid)
132252eabba5SLogan Gunthorpe {
132352eabba5SLogan Gunthorpe 	int idx;
132452eabba5SLogan Gunthorpe 	int count = 0;
132552eabba5SLogan Gunthorpe 
132652eabba5SLogan Gunthorpe 	if (event_regs[eid].map_reg == part_ev_reg) {
132752eabba5SLogan Gunthorpe 		for (idx = 0; idx < stdev->partition_count; idx++)
132852eabba5SLogan Gunthorpe 			count += mask_event(stdev, eid, idx);
132952eabba5SLogan Gunthorpe 	} else if (event_regs[eid].map_reg == pff_ev_reg) {
133052eabba5SLogan Gunthorpe 		for (idx = 0; idx < stdev->pff_csr_count; idx++) {
133152eabba5SLogan Gunthorpe 			if (!stdev->pff_local[idx])
133252eabba5SLogan Gunthorpe 				continue;
133352eabba5SLogan Gunthorpe 			count += mask_event(stdev, eid, idx);
133452eabba5SLogan Gunthorpe 		}
133552eabba5SLogan Gunthorpe 	} else {
133652eabba5SLogan Gunthorpe 		count += mask_event(stdev, eid, 0);
133752eabba5SLogan Gunthorpe 	}
133852eabba5SLogan Gunthorpe 
133952eabba5SLogan Gunthorpe 	return count;
134052eabba5SLogan Gunthorpe }
134152eabba5SLogan Gunthorpe 
1342080b47deSLogan Gunthorpe static irqreturn_t switchtec_event_isr(int irq, void *dev)
1343080b47deSLogan Gunthorpe {
1344080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = dev;
1345080b47deSLogan Gunthorpe 	u32 reg;
1346080b47deSLogan Gunthorpe 	irqreturn_t ret = IRQ_NONE;
134752eabba5SLogan Gunthorpe 	int eid, event_count = 0;
1348080b47deSLogan Gunthorpe 
1349080b47deSLogan Gunthorpe 	reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr);
1350080b47deSLogan Gunthorpe 	if (reg & SWITCHTEC_EVENT_OCCURRED) {
1351080b47deSLogan Gunthorpe 		dev_dbg(&stdev->dev, "%s: mrpc comp\n", __func__);
1352080b47deSLogan Gunthorpe 		ret = IRQ_HANDLED;
1353080b47deSLogan Gunthorpe 		schedule_work(&stdev->mrpc_work);
1354080b47deSLogan Gunthorpe 		iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr);
1355080b47deSLogan Gunthorpe 	}
1356080b47deSLogan Gunthorpe 
135752eabba5SLogan Gunthorpe 	for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++)
135852eabba5SLogan Gunthorpe 		event_count += mask_all_events(stdev, eid);
135952eabba5SLogan Gunthorpe 
136052eabba5SLogan Gunthorpe 	if (event_count) {
136152eabba5SLogan Gunthorpe 		atomic_inc(&stdev->event_cnt);
136252eabba5SLogan Gunthorpe 		wake_up_interruptible(&stdev->event_wq);
136352eabba5SLogan Gunthorpe 		dev_dbg(&stdev->dev, "%s: %d events\n", __func__,
136452eabba5SLogan Gunthorpe 			event_count);
136552eabba5SLogan Gunthorpe 		return IRQ_HANDLED;
136652eabba5SLogan Gunthorpe 	}
136752eabba5SLogan Gunthorpe 
1368080b47deSLogan Gunthorpe 	return ret;
1369080b47deSLogan Gunthorpe }
1370080b47deSLogan Gunthorpe 
1371080b47deSLogan Gunthorpe static int switchtec_init_isr(struct switchtec_dev *stdev)
1372080b47deSLogan Gunthorpe {
1373080b47deSLogan Gunthorpe 	int nvecs;
1374080b47deSLogan Gunthorpe 	int event_irq;
1375080b47deSLogan Gunthorpe 
1376080b47deSLogan Gunthorpe 	nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4,
1377080b47deSLogan Gunthorpe 				      PCI_IRQ_MSIX | PCI_IRQ_MSI);
1378080b47deSLogan Gunthorpe 	if (nvecs < 0)
1379080b47deSLogan Gunthorpe 		return nvecs;
1380080b47deSLogan Gunthorpe 
1381080b47deSLogan Gunthorpe 	event_irq = ioread32(&stdev->mmio_part_cfg->vep_vector_number);
1382080b47deSLogan Gunthorpe 	if (event_irq < 0 || event_irq >= nvecs)
1383080b47deSLogan Gunthorpe 		return -EFAULT;
1384080b47deSLogan Gunthorpe 
1385080b47deSLogan Gunthorpe 	event_irq = pci_irq_vector(stdev->pdev, event_irq);
1386080b47deSLogan Gunthorpe 	if (event_irq < 0)
1387080b47deSLogan Gunthorpe 		return event_irq;
1388080b47deSLogan Gunthorpe 
1389080b47deSLogan Gunthorpe 	return devm_request_irq(&stdev->pdev->dev, event_irq,
1390080b47deSLogan Gunthorpe 				switchtec_event_isr, 0,
1391080b47deSLogan Gunthorpe 				KBUILD_MODNAME, stdev);
1392080b47deSLogan Gunthorpe }
1393080b47deSLogan Gunthorpe 
1394080b47deSLogan Gunthorpe static void init_pff(struct switchtec_dev *stdev)
1395080b47deSLogan Gunthorpe {
1396080b47deSLogan Gunthorpe 	int i;
1397080b47deSLogan Gunthorpe 	u32 reg;
1398080b47deSLogan Gunthorpe 	struct part_cfg_regs *pcfg = stdev->mmio_part_cfg;
1399080b47deSLogan Gunthorpe 
1400080b47deSLogan Gunthorpe 	for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
1401080b47deSLogan Gunthorpe 		reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id);
1402080b47deSLogan Gunthorpe 		if (reg != MICROSEMI_VENDOR_ID)
1403080b47deSLogan Gunthorpe 			break;
1404080b47deSLogan Gunthorpe 	}
1405080b47deSLogan Gunthorpe 
1406080b47deSLogan Gunthorpe 	stdev->pff_csr_count = i;
1407080b47deSLogan Gunthorpe 
1408080b47deSLogan Gunthorpe 	reg = ioread32(&pcfg->usp_pff_inst_id);
1409080b47deSLogan Gunthorpe 	if (reg < SWITCHTEC_MAX_PFF_CSR)
1410080b47deSLogan Gunthorpe 		stdev->pff_local[reg] = 1;
1411080b47deSLogan Gunthorpe 
1412080b47deSLogan Gunthorpe 	reg = ioread32(&pcfg->vep_pff_inst_id);
1413080b47deSLogan Gunthorpe 	if (reg < SWITCHTEC_MAX_PFF_CSR)
1414080b47deSLogan Gunthorpe 		stdev->pff_local[reg] = 1;
1415080b47deSLogan Gunthorpe 
1416080b47deSLogan Gunthorpe 	for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) {
1417080b47deSLogan Gunthorpe 		reg = ioread32(&pcfg->dsp_pff_inst_id[i]);
1418080b47deSLogan Gunthorpe 		if (reg < SWITCHTEC_MAX_PFF_CSR)
1419080b47deSLogan Gunthorpe 			stdev->pff_local[reg] = 1;
1420080b47deSLogan Gunthorpe 	}
1421080b47deSLogan Gunthorpe }
1422080b47deSLogan Gunthorpe 
1423080b47deSLogan Gunthorpe static int switchtec_init_pci(struct switchtec_dev *stdev,
1424080b47deSLogan Gunthorpe 			      struct pci_dev *pdev)
1425080b47deSLogan Gunthorpe {
1426080b47deSLogan Gunthorpe 	int rc;
1427080b47deSLogan Gunthorpe 
1428080b47deSLogan Gunthorpe 	rc = pcim_enable_device(pdev);
1429080b47deSLogan Gunthorpe 	if (rc)
1430080b47deSLogan Gunthorpe 		return rc;
1431080b47deSLogan Gunthorpe 
1432080b47deSLogan Gunthorpe 	rc = pcim_iomap_regions(pdev, 0x1, KBUILD_MODNAME);
1433080b47deSLogan Gunthorpe 	if (rc)
1434080b47deSLogan Gunthorpe 		return rc;
1435080b47deSLogan Gunthorpe 
1436080b47deSLogan Gunthorpe 	pci_set_master(pdev);
1437080b47deSLogan Gunthorpe 
1438080b47deSLogan Gunthorpe 	stdev->mmio = pcim_iomap_table(pdev)[0];
1439080b47deSLogan Gunthorpe 	stdev->mmio_mrpc = stdev->mmio + SWITCHTEC_GAS_MRPC_OFFSET;
1440080b47deSLogan Gunthorpe 	stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET;
1441080b47deSLogan Gunthorpe 	stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET;
1442080b47deSLogan Gunthorpe 	stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET;
1443080b47deSLogan Gunthorpe 	stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET;
1444080b47deSLogan Gunthorpe 	stdev->partition = ioread8(&stdev->mmio_ntb->partition_id);
1445080b47deSLogan Gunthorpe 	stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count);
1446080b47deSLogan Gunthorpe 	stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET;
1447080b47deSLogan Gunthorpe 	stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition];
1448080b47deSLogan Gunthorpe 	stdev->mmio_pff_csr = stdev->mmio + SWITCHTEC_GAS_PFF_CSR_OFFSET;
1449080b47deSLogan Gunthorpe 
1450080b47deSLogan Gunthorpe 	init_pff(stdev);
1451080b47deSLogan Gunthorpe 
1452080b47deSLogan Gunthorpe 	pci_set_drvdata(pdev, stdev);
1453080b47deSLogan Gunthorpe 
1454080b47deSLogan Gunthorpe 	return 0;
1455080b47deSLogan Gunthorpe }
1456080b47deSLogan Gunthorpe 
1457080b47deSLogan Gunthorpe static int switchtec_pci_probe(struct pci_dev *pdev,
1458080b47deSLogan Gunthorpe 			       const struct pci_device_id *id)
1459080b47deSLogan Gunthorpe {
1460080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
1461080b47deSLogan Gunthorpe 	int rc;
1462080b47deSLogan Gunthorpe 
1463080b47deSLogan Gunthorpe 	stdev = stdev_create(pdev);
1464080b47deSLogan Gunthorpe 	if (IS_ERR(stdev))
1465080b47deSLogan Gunthorpe 		return PTR_ERR(stdev);
1466080b47deSLogan Gunthorpe 
1467080b47deSLogan Gunthorpe 	rc = switchtec_init_pci(stdev, pdev);
1468080b47deSLogan Gunthorpe 	if (rc)
1469080b47deSLogan Gunthorpe 		goto err_put;
1470080b47deSLogan Gunthorpe 
1471080b47deSLogan Gunthorpe 	rc = switchtec_init_isr(stdev);
1472080b47deSLogan Gunthorpe 	if (rc) {
1473080b47deSLogan Gunthorpe 		dev_err(&stdev->dev, "failed to init isr.\n");
1474080b47deSLogan Gunthorpe 		goto err_put;
1475080b47deSLogan Gunthorpe 	}
1476080b47deSLogan Gunthorpe 
1477080b47deSLogan Gunthorpe 	iowrite32(SWITCHTEC_EVENT_CLEAR |
1478080b47deSLogan Gunthorpe 		  SWITCHTEC_EVENT_EN_IRQ,
1479080b47deSLogan Gunthorpe 		  &stdev->mmio_part_cfg->mrpc_comp_hdr);
1480080b47deSLogan Gunthorpe 
1481*e40cf640SLogan Gunthorpe 	rc = cdev_device_add(&stdev->cdev, &stdev->dev);
1482080b47deSLogan Gunthorpe 	if (rc)
1483080b47deSLogan Gunthorpe 		goto err_devadd;
1484080b47deSLogan Gunthorpe 
1485080b47deSLogan Gunthorpe 	dev_info(&stdev->dev, "Management device registered.\n");
1486080b47deSLogan Gunthorpe 
1487080b47deSLogan Gunthorpe 	return 0;
1488080b47deSLogan Gunthorpe 
1489080b47deSLogan Gunthorpe err_devadd:
1490080b47deSLogan Gunthorpe 	stdev_kill(stdev);
1491080b47deSLogan Gunthorpe err_put:
1492080b47deSLogan Gunthorpe 	ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt));
1493080b47deSLogan Gunthorpe 	put_device(&stdev->dev);
1494080b47deSLogan Gunthorpe 	return rc;
1495080b47deSLogan Gunthorpe }
1496080b47deSLogan Gunthorpe 
1497080b47deSLogan Gunthorpe static void switchtec_pci_remove(struct pci_dev *pdev)
1498080b47deSLogan Gunthorpe {
1499080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = pci_get_drvdata(pdev);
1500080b47deSLogan Gunthorpe 
1501080b47deSLogan Gunthorpe 	pci_set_drvdata(pdev, NULL);
1502080b47deSLogan Gunthorpe 
1503*e40cf640SLogan Gunthorpe 	cdev_device_del(&stdev->cdev, &stdev->dev);
1504080b47deSLogan Gunthorpe 	ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt));
1505080b47deSLogan Gunthorpe 	dev_info(&stdev->dev, "unregistered.\n");
1506080b47deSLogan Gunthorpe 
1507080b47deSLogan Gunthorpe 	stdev_kill(stdev);
1508080b47deSLogan Gunthorpe 	put_device(&stdev->dev);
1509080b47deSLogan Gunthorpe }
1510080b47deSLogan Gunthorpe 
1511080b47deSLogan Gunthorpe #define SWITCHTEC_PCI_DEVICE(device_id) \
1512080b47deSLogan Gunthorpe 	{ \
1513080b47deSLogan Gunthorpe 		.vendor     = MICROSEMI_VENDOR_ID, \
1514080b47deSLogan Gunthorpe 		.device     = device_id, \
1515080b47deSLogan Gunthorpe 		.subvendor  = PCI_ANY_ID, \
1516080b47deSLogan Gunthorpe 		.subdevice  = PCI_ANY_ID, \
1517080b47deSLogan Gunthorpe 		.class      = MICROSEMI_MGMT_CLASSCODE, \
1518080b47deSLogan Gunthorpe 		.class_mask = 0xFFFFFFFF, \
1519080b47deSLogan Gunthorpe 	}, \
1520080b47deSLogan Gunthorpe 	{ \
1521080b47deSLogan Gunthorpe 		.vendor     = MICROSEMI_VENDOR_ID, \
1522080b47deSLogan Gunthorpe 		.device     = device_id, \
1523080b47deSLogan Gunthorpe 		.subvendor  = PCI_ANY_ID, \
1524080b47deSLogan Gunthorpe 		.subdevice  = PCI_ANY_ID, \
1525080b47deSLogan Gunthorpe 		.class      = MICROSEMI_NTB_CLASSCODE, \
1526080b47deSLogan Gunthorpe 		.class_mask = 0xFFFFFFFF, \
1527080b47deSLogan Gunthorpe 	}
1528080b47deSLogan Gunthorpe 
1529080b47deSLogan Gunthorpe static const struct pci_device_id switchtec_pci_tbl[] = {
1530080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8531),  //PFX 24xG3
1531080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8532),  //PFX 32xG3
1532080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8533),  //PFX 48xG3
1533080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8534),  //PFX 64xG3
1534080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8535),  //PFX 80xG3
1535080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8536),  //PFX 96xG3
1536080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8543),  //PSX 48xG3
1537080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8544),  //PSX 64xG3
1538080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8545),  //PSX 80xG3
1539080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8546),  //PSX 96xG3
1540080b47deSLogan Gunthorpe 	{0}
1541080b47deSLogan Gunthorpe };
1542080b47deSLogan Gunthorpe MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl);
1543080b47deSLogan Gunthorpe 
1544080b47deSLogan Gunthorpe static struct pci_driver switchtec_pci_driver = {
1545080b47deSLogan Gunthorpe 	.name		= KBUILD_MODNAME,
1546080b47deSLogan Gunthorpe 	.id_table	= switchtec_pci_tbl,
1547080b47deSLogan Gunthorpe 	.probe		= switchtec_pci_probe,
1548080b47deSLogan Gunthorpe 	.remove		= switchtec_pci_remove,
1549080b47deSLogan Gunthorpe };
1550080b47deSLogan Gunthorpe 
1551080b47deSLogan Gunthorpe static int __init switchtec_init(void)
1552080b47deSLogan Gunthorpe {
1553080b47deSLogan Gunthorpe 	int rc;
1554080b47deSLogan Gunthorpe 
1555080b47deSLogan Gunthorpe 	rc = alloc_chrdev_region(&switchtec_devt, 0, max_devices,
1556080b47deSLogan Gunthorpe 				 "switchtec");
1557080b47deSLogan Gunthorpe 	if (rc)
1558080b47deSLogan Gunthorpe 		return rc;
1559080b47deSLogan Gunthorpe 
1560080b47deSLogan Gunthorpe 	switchtec_class = class_create(THIS_MODULE, "switchtec");
1561080b47deSLogan Gunthorpe 	if (IS_ERR(switchtec_class)) {
1562080b47deSLogan Gunthorpe 		rc = PTR_ERR(switchtec_class);
1563080b47deSLogan Gunthorpe 		goto err_create_class;
1564080b47deSLogan Gunthorpe 	}
1565080b47deSLogan Gunthorpe 
1566080b47deSLogan Gunthorpe 	rc = pci_register_driver(&switchtec_pci_driver);
1567080b47deSLogan Gunthorpe 	if (rc)
1568080b47deSLogan Gunthorpe 		goto err_pci_register;
1569080b47deSLogan Gunthorpe 
1570080b47deSLogan Gunthorpe 	pr_info(KBUILD_MODNAME ": loaded.\n");
1571080b47deSLogan Gunthorpe 
1572080b47deSLogan Gunthorpe 	return 0;
1573080b47deSLogan Gunthorpe 
1574080b47deSLogan Gunthorpe err_pci_register:
1575080b47deSLogan Gunthorpe 	class_destroy(switchtec_class);
1576080b47deSLogan Gunthorpe 
1577080b47deSLogan Gunthorpe err_create_class:
1578080b47deSLogan Gunthorpe 	unregister_chrdev_region(switchtec_devt, max_devices);
1579080b47deSLogan Gunthorpe 
1580080b47deSLogan Gunthorpe 	return rc;
1581080b47deSLogan Gunthorpe }
1582080b47deSLogan Gunthorpe module_init(switchtec_init);
1583080b47deSLogan Gunthorpe 
1584080b47deSLogan Gunthorpe static void __exit switchtec_exit(void)
1585080b47deSLogan Gunthorpe {
1586080b47deSLogan Gunthorpe 	pci_unregister_driver(&switchtec_pci_driver);
1587080b47deSLogan Gunthorpe 	class_destroy(switchtec_class);
1588080b47deSLogan Gunthorpe 	unregister_chrdev_region(switchtec_devt, max_devices);
1589080b47deSLogan Gunthorpe 	ida_destroy(&switchtec_minor_ida);
1590080b47deSLogan Gunthorpe 
1591080b47deSLogan Gunthorpe 	pr_info(KBUILD_MODNAME ": unloaded.\n");
1592080b47deSLogan Gunthorpe }
1593080b47deSLogan Gunthorpe module_exit(switchtec_exit);
1594