xref: /linux/drivers/pci/switch/switchtec.c (revision 5d8e1881f431cf470170813eb048e6a384340904)
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 
16080b47deSLogan Gunthorpe #include <linux/interrupt.h>
17080b47deSLogan Gunthorpe #include <linux/module.h>
18080b47deSLogan Gunthorpe #include <linux/fs.h>
19080b47deSLogan Gunthorpe #include <linux/uaccess.h>
20080b47deSLogan Gunthorpe #include <linux/poll.h>
21080b47deSLogan Gunthorpe #include <linux/pci.h>
22080b47deSLogan Gunthorpe #include <linux/cdev.h>
23080b47deSLogan Gunthorpe #include <linux/wait.h>
24080b47deSLogan Gunthorpe 
25080b47deSLogan Gunthorpe MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver");
26080b47deSLogan Gunthorpe MODULE_VERSION("0.1");
27080b47deSLogan Gunthorpe MODULE_LICENSE("GPL");
28080b47deSLogan Gunthorpe MODULE_AUTHOR("Microsemi Corporation");
29080b47deSLogan Gunthorpe 
30080b47deSLogan Gunthorpe static int max_devices = 16;
31080b47deSLogan Gunthorpe module_param(max_devices, int, 0644);
32080b47deSLogan Gunthorpe MODULE_PARM_DESC(max_devices, "max number of switchtec device instances");
33080b47deSLogan Gunthorpe 
34080b47deSLogan Gunthorpe static dev_t switchtec_devt;
35080b47deSLogan Gunthorpe static struct class *switchtec_class;
36080b47deSLogan Gunthorpe static DEFINE_IDA(switchtec_minor_ida);
37080b47deSLogan Gunthorpe 
38080b47deSLogan Gunthorpe #define MICROSEMI_VENDOR_ID         0x11f8
39080b47deSLogan Gunthorpe #define MICROSEMI_NTB_CLASSCODE     0x068000
40080b47deSLogan Gunthorpe #define MICROSEMI_MGMT_CLASSCODE    0x058000
41080b47deSLogan Gunthorpe 
42080b47deSLogan Gunthorpe #define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024
43080b47deSLogan Gunthorpe #define SWITCHTEC_MAX_PFF_CSR 48
44080b47deSLogan Gunthorpe 
45080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_OCCURRED BIT(0)
46080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_CLEAR    BIT(0)
47080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_EN_LOG   BIT(1)
48080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_EN_CLI   BIT(2)
49080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_EN_IRQ   BIT(3)
50080b47deSLogan Gunthorpe #define SWITCHTEC_EVENT_FATAL    BIT(4)
51080b47deSLogan Gunthorpe 
52080b47deSLogan Gunthorpe enum {
53080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_MRPC_OFFSET       = 0x0000,
54080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_TOP_CFG_OFFSET    = 0x1000,
55080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_SW_EVENT_OFFSET   = 0x1800,
56080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_SYS_INFO_OFFSET   = 0x2000,
57080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_FLASH_INFO_OFFSET = 0x2200,
58080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_PART_CFG_OFFSET   = 0x4000,
59080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_NTB_OFFSET        = 0x10000,
60080b47deSLogan Gunthorpe 	SWITCHTEC_GAS_PFF_CSR_OFFSET    = 0x134000,
61080b47deSLogan Gunthorpe };
62080b47deSLogan Gunthorpe 
63080b47deSLogan Gunthorpe struct mrpc_regs {
64080b47deSLogan Gunthorpe 	u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
65080b47deSLogan Gunthorpe 	u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
66080b47deSLogan Gunthorpe 	u32 cmd;
67080b47deSLogan Gunthorpe 	u32 status;
68080b47deSLogan Gunthorpe 	u32 ret_value;
69080b47deSLogan Gunthorpe } __packed;
70080b47deSLogan Gunthorpe 
71080b47deSLogan Gunthorpe enum mrpc_status {
72080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_INPROGRESS = 1,
73080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_DONE = 2,
74080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_ERROR = 0xFF,
75080b47deSLogan Gunthorpe 	SWITCHTEC_MRPC_STATUS_INTERRUPTED = 0x100,
76080b47deSLogan Gunthorpe };
77080b47deSLogan Gunthorpe 
78080b47deSLogan Gunthorpe struct sw_event_regs {
79080b47deSLogan Gunthorpe 	u64 event_report_ctrl;
80080b47deSLogan Gunthorpe 	u64 reserved1;
81080b47deSLogan Gunthorpe 	u64 part_event_bitmap;
82080b47deSLogan Gunthorpe 	u64 reserved2;
83080b47deSLogan Gunthorpe 	u32 global_summary;
84080b47deSLogan Gunthorpe 	u32 reserved3[3];
85080b47deSLogan Gunthorpe 	u32 stack_error_event_hdr;
86080b47deSLogan Gunthorpe 	u32 stack_error_event_data;
87080b47deSLogan Gunthorpe 	u32 reserved4[4];
88080b47deSLogan Gunthorpe 	u32 ppu_error_event_hdr;
89080b47deSLogan Gunthorpe 	u32 ppu_error_event_data;
90080b47deSLogan Gunthorpe 	u32 reserved5[4];
91080b47deSLogan Gunthorpe 	u32 isp_error_event_hdr;
92080b47deSLogan Gunthorpe 	u32 isp_error_event_data;
93080b47deSLogan Gunthorpe 	u32 reserved6[4];
94080b47deSLogan Gunthorpe 	u32 sys_reset_event_hdr;
95080b47deSLogan Gunthorpe 	u32 reserved7[5];
96080b47deSLogan Gunthorpe 	u32 fw_exception_hdr;
97080b47deSLogan Gunthorpe 	u32 reserved8[5];
98080b47deSLogan Gunthorpe 	u32 fw_nmi_hdr;
99080b47deSLogan Gunthorpe 	u32 reserved9[5];
100080b47deSLogan Gunthorpe 	u32 fw_non_fatal_hdr;
101080b47deSLogan Gunthorpe 	u32 reserved10[5];
102080b47deSLogan Gunthorpe 	u32 fw_fatal_hdr;
103080b47deSLogan Gunthorpe 	u32 reserved11[5];
104080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_hdr;
105080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_data;
106080b47deSLogan Gunthorpe 	u32 reserved12[4];
107080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_async_hdr;
108080b47deSLogan Gunthorpe 	u32 twi_mrpc_comp_async_data;
109080b47deSLogan Gunthorpe 	u32 reserved13[4];
110080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_hdr;
111080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_data;
112080b47deSLogan Gunthorpe 	u32 reserved14[4];
113080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_async_hdr;
114080b47deSLogan Gunthorpe 	u32 cli_mrpc_comp_async_data;
115080b47deSLogan Gunthorpe 	u32 reserved15[4];
116080b47deSLogan Gunthorpe 	u32 gpio_interrupt_hdr;
117080b47deSLogan Gunthorpe 	u32 gpio_interrupt_data;
118080b47deSLogan Gunthorpe 	u32 reserved16[4];
119080b47deSLogan Gunthorpe } __packed;
120080b47deSLogan Gunthorpe 
121080b47deSLogan Gunthorpe struct sys_info_regs {
122080b47deSLogan Gunthorpe 	u32 device_id;
123080b47deSLogan Gunthorpe 	u32 device_version;
124080b47deSLogan Gunthorpe 	u32 firmware_version;
125080b47deSLogan Gunthorpe 	u32 reserved1;
126080b47deSLogan Gunthorpe 	u32 vendor_table_revision;
127080b47deSLogan Gunthorpe 	u32 table_format_version;
128080b47deSLogan Gunthorpe 	u32 partition_id;
129080b47deSLogan Gunthorpe 	u32 cfg_file_fmt_version;
130080b47deSLogan Gunthorpe 	u32 reserved2[58];
131080b47deSLogan Gunthorpe 	char vendor_id[8];
132080b47deSLogan Gunthorpe 	char product_id[16];
133080b47deSLogan Gunthorpe 	char product_revision[4];
134080b47deSLogan Gunthorpe 	char component_vendor[8];
135080b47deSLogan Gunthorpe 	u16 component_id;
136080b47deSLogan Gunthorpe 	u8 component_revision;
137080b47deSLogan Gunthorpe } __packed;
138080b47deSLogan Gunthorpe 
139080b47deSLogan Gunthorpe struct flash_info_regs {
140080b47deSLogan Gunthorpe 	u32 flash_part_map_upd_idx;
141080b47deSLogan Gunthorpe 
142080b47deSLogan Gunthorpe 	struct active_partition_info {
143080b47deSLogan Gunthorpe 		u32 address;
144080b47deSLogan Gunthorpe 		u32 build_version;
145080b47deSLogan Gunthorpe 		u32 build_string;
146080b47deSLogan Gunthorpe 	} active_img;
147080b47deSLogan Gunthorpe 
148080b47deSLogan Gunthorpe 	struct active_partition_info active_cfg;
149080b47deSLogan Gunthorpe 	struct active_partition_info inactive_img;
150080b47deSLogan Gunthorpe 	struct active_partition_info inactive_cfg;
151080b47deSLogan Gunthorpe 
152080b47deSLogan Gunthorpe 	u32 flash_length;
153080b47deSLogan Gunthorpe 
154080b47deSLogan Gunthorpe 	struct partition_info {
155080b47deSLogan Gunthorpe 		u32 address;
156080b47deSLogan Gunthorpe 		u32 length;
157080b47deSLogan Gunthorpe 	} cfg0;
158080b47deSLogan Gunthorpe 
159080b47deSLogan Gunthorpe 	struct partition_info cfg1;
160080b47deSLogan Gunthorpe 	struct partition_info img0;
161080b47deSLogan Gunthorpe 	struct partition_info img1;
162080b47deSLogan Gunthorpe 	struct partition_info nvlog;
163080b47deSLogan Gunthorpe 	struct partition_info vendor[8];
164080b47deSLogan Gunthorpe };
165080b47deSLogan Gunthorpe 
166080b47deSLogan Gunthorpe struct ntb_info_regs {
167080b47deSLogan Gunthorpe 	u8  partition_count;
168080b47deSLogan Gunthorpe 	u8  partition_id;
169080b47deSLogan Gunthorpe 	u16 reserved1;
170080b47deSLogan Gunthorpe 	u64 ep_map;
171080b47deSLogan Gunthorpe 	u16 requester_id;
172080b47deSLogan Gunthorpe } __packed;
173080b47deSLogan Gunthorpe 
174080b47deSLogan Gunthorpe struct part_cfg_regs {
175080b47deSLogan Gunthorpe 	u32 status;
176080b47deSLogan Gunthorpe 	u32 state;
177080b47deSLogan Gunthorpe 	u32 port_cnt;
178080b47deSLogan Gunthorpe 	u32 usp_port_mode;
179080b47deSLogan Gunthorpe 	u32 usp_pff_inst_id;
180080b47deSLogan Gunthorpe 	u32 vep_pff_inst_id;
181080b47deSLogan Gunthorpe 	u32 dsp_pff_inst_id[47];
182080b47deSLogan Gunthorpe 	u32 reserved1[11];
183080b47deSLogan Gunthorpe 	u16 vep_vector_number;
184080b47deSLogan Gunthorpe 	u16 usp_vector_number;
185080b47deSLogan Gunthorpe 	u32 port_event_bitmap;
186080b47deSLogan Gunthorpe 	u32 reserved2[3];
187080b47deSLogan Gunthorpe 	u32 part_event_summary;
188080b47deSLogan Gunthorpe 	u32 reserved3[3];
189080b47deSLogan Gunthorpe 	u32 part_reset_hdr;
190080b47deSLogan Gunthorpe 	u32 part_reset_data[5];
191080b47deSLogan Gunthorpe 	u32 mrpc_comp_hdr;
192080b47deSLogan Gunthorpe 	u32 mrpc_comp_data[5];
193080b47deSLogan Gunthorpe 	u32 mrpc_comp_async_hdr;
194080b47deSLogan Gunthorpe 	u32 mrpc_comp_async_data[5];
195080b47deSLogan Gunthorpe 	u32 dyn_binding_hdr;
196080b47deSLogan Gunthorpe 	u32 dyn_binding_data[5];
197080b47deSLogan Gunthorpe 	u32 reserved4[159];
198080b47deSLogan Gunthorpe } __packed;
199080b47deSLogan Gunthorpe 
200080b47deSLogan Gunthorpe enum {
201080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_RESET = 1 << 0,
202080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_MRPC_CMP = 1 << 1,
203080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_MRPC_ASYNC_CMP = 1 << 2,
204080b47deSLogan Gunthorpe 	SWITCHTEC_PART_CFG_EVENT_DYN_PART_CMP = 1 << 3,
205080b47deSLogan Gunthorpe };
206080b47deSLogan Gunthorpe 
207080b47deSLogan Gunthorpe struct pff_csr_regs {
208080b47deSLogan Gunthorpe 	u16 vendor_id;
209080b47deSLogan Gunthorpe 	u16 device_id;
210080b47deSLogan Gunthorpe 	u32 pci_cfg_header[15];
211080b47deSLogan Gunthorpe 	u32 pci_cap_region[48];
212080b47deSLogan Gunthorpe 	u32 pcie_cap_region[448];
213080b47deSLogan Gunthorpe 	u32 indirect_gas_window[128];
214080b47deSLogan Gunthorpe 	u32 indirect_gas_window_off;
215080b47deSLogan Gunthorpe 	u32 reserved[127];
216080b47deSLogan Gunthorpe 	u32 pff_event_summary;
217080b47deSLogan Gunthorpe 	u32 reserved2[3];
218080b47deSLogan Gunthorpe 	u32 aer_in_p2p_hdr;
219080b47deSLogan Gunthorpe 	u32 aer_in_p2p_data[5];
220080b47deSLogan Gunthorpe 	u32 aer_in_vep_hdr;
221080b47deSLogan Gunthorpe 	u32 aer_in_vep_data[5];
222080b47deSLogan Gunthorpe 	u32 dpc_hdr;
223080b47deSLogan Gunthorpe 	u32 dpc_data[5];
224080b47deSLogan Gunthorpe 	u32 cts_hdr;
225080b47deSLogan Gunthorpe 	u32 cts_data[5];
226080b47deSLogan Gunthorpe 	u32 reserved3[6];
227080b47deSLogan Gunthorpe 	u32 hotplug_hdr;
228080b47deSLogan Gunthorpe 	u32 hotplug_data[5];
229080b47deSLogan Gunthorpe 	u32 ier_hdr;
230080b47deSLogan Gunthorpe 	u32 ier_data[5];
231080b47deSLogan Gunthorpe 	u32 threshold_hdr;
232080b47deSLogan Gunthorpe 	u32 threshold_data[5];
233080b47deSLogan Gunthorpe 	u32 power_mgmt_hdr;
234080b47deSLogan Gunthorpe 	u32 power_mgmt_data[5];
235080b47deSLogan Gunthorpe 	u32 tlp_throttling_hdr;
236080b47deSLogan Gunthorpe 	u32 tlp_throttling_data[5];
237080b47deSLogan Gunthorpe 	u32 force_speed_hdr;
238080b47deSLogan Gunthorpe 	u32 force_speed_data[5];
239080b47deSLogan Gunthorpe 	u32 credit_timeout_hdr;
240080b47deSLogan Gunthorpe 	u32 credit_timeout_data[5];
241080b47deSLogan Gunthorpe 	u32 link_state_hdr;
242080b47deSLogan Gunthorpe 	u32 link_state_data[5];
243080b47deSLogan Gunthorpe 	u32 reserved4[174];
244080b47deSLogan Gunthorpe } __packed;
245080b47deSLogan Gunthorpe 
246080b47deSLogan Gunthorpe struct switchtec_dev {
247080b47deSLogan Gunthorpe 	struct pci_dev *pdev;
248080b47deSLogan Gunthorpe 	struct device dev;
249080b47deSLogan Gunthorpe 	struct cdev cdev;
250080b47deSLogan Gunthorpe 
251080b47deSLogan Gunthorpe 	int partition;
252080b47deSLogan Gunthorpe 	int partition_count;
253080b47deSLogan Gunthorpe 	int pff_csr_count;
254080b47deSLogan Gunthorpe 	char pff_local[SWITCHTEC_MAX_PFF_CSR];
255080b47deSLogan Gunthorpe 
256080b47deSLogan Gunthorpe 	void __iomem *mmio;
257080b47deSLogan Gunthorpe 	struct mrpc_regs __iomem *mmio_mrpc;
258080b47deSLogan Gunthorpe 	struct sw_event_regs __iomem *mmio_sw_event;
259080b47deSLogan Gunthorpe 	struct sys_info_regs __iomem *mmio_sys_info;
260080b47deSLogan Gunthorpe 	struct flash_info_regs __iomem *mmio_flash_info;
261080b47deSLogan Gunthorpe 	struct ntb_info_regs __iomem *mmio_ntb;
262080b47deSLogan Gunthorpe 	struct part_cfg_regs __iomem *mmio_part_cfg;
263080b47deSLogan Gunthorpe 	struct part_cfg_regs __iomem *mmio_part_cfg_all;
264080b47deSLogan Gunthorpe 	struct pff_csr_regs __iomem *mmio_pff_csr;
265080b47deSLogan Gunthorpe 
266080b47deSLogan Gunthorpe 	/*
267080b47deSLogan Gunthorpe 	 * The mrpc mutex must be held when accessing the other
268080b47deSLogan Gunthorpe 	 * mrpc_ fields, alive flag and stuser->state field
269080b47deSLogan Gunthorpe 	 */
270080b47deSLogan Gunthorpe 	struct mutex mrpc_mutex;
271080b47deSLogan Gunthorpe 	struct list_head mrpc_queue;
272080b47deSLogan Gunthorpe 	int mrpc_busy;
273080b47deSLogan Gunthorpe 	struct work_struct mrpc_work;
274080b47deSLogan Gunthorpe 	struct delayed_work mrpc_timeout;
275080b47deSLogan Gunthorpe 	bool alive;
276080b47deSLogan Gunthorpe 
277080b47deSLogan Gunthorpe 	wait_queue_head_t event_wq;
278080b47deSLogan Gunthorpe 	atomic_t event_cnt;
279080b47deSLogan Gunthorpe };
280080b47deSLogan Gunthorpe 
281080b47deSLogan Gunthorpe static struct switchtec_dev *to_stdev(struct device *dev)
282080b47deSLogan Gunthorpe {
283080b47deSLogan Gunthorpe 	return container_of(dev, struct switchtec_dev, dev);
284080b47deSLogan Gunthorpe }
285080b47deSLogan Gunthorpe 
286080b47deSLogan Gunthorpe enum mrpc_state {
287080b47deSLogan Gunthorpe 	MRPC_IDLE = 0,
288080b47deSLogan Gunthorpe 	MRPC_QUEUED,
289080b47deSLogan Gunthorpe 	MRPC_RUNNING,
290080b47deSLogan Gunthorpe 	MRPC_DONE,
291080b47deSLogan Gunthorpe };
292080b47deSLogan Gunthorpe 
293080b47deSLogan Gunthorpe struct switchtec_user {
294080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
295080b47deSLogan Gunthorpe 
296080b47deSLogan Gunthorpe 	enum mrpc_state state;
297080b47deSLogan Gunthorpe 
298080b47deSLogan Gunthorpe 	struct completion comp;
299080b47deSLogan Gunthorpe 	struct kref kref;
300080b47deSLogan Gunthorpe 	struct list_head list;
301080b47deSLogan Gunthorpe 
302080b47deSLogan Gunthorpe 	u32 cmd;
303080b47deSLogan Gunthorpe 	u32 status;
304080b47deSLogan Gunthorpe 	u32 return_code;
305080b47deSLogan Gunthorpe 	size_t data_len;
306080b47deSLogan Gunthorpe 	size_t read_len;
307080b47deSLogan Gunthorpe 	unsigned char data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
308080b47deSLogan Gunthorpe 	int event_cnt;
309080b47deSLogan Gunthorpe };
310080b47deSLogan Gunthorpe 
311080b47deSLogan Gunthorpe static struct switchtec_user *stuser_create(struct switchtec_dev *stdev)
312080b47deSLogan Gunthorpe {
313080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
314080b47deSLogan Gunthorpe 
315080b47deSLogan Gunthorpe 	stuser = kzalloc(sizeof(*stuser), GFP_KERNEL);
316080b47deSLogan Gunthorpe 	if (!stuser)
317080b47deSLogan Gunthorpe 		return ERR_PTR(-ENOMEM);
318080b47deSLogan Gunthorpe 
319080b47deSLogan Gunthorpe 	get_device(&stdev->dev);
320080b47deSLogan Gunthorpe 	stuser->stdev = stdev;
321080b47deSLogan Gunthorpe 	kref_init(&stuser->kref);
322080b47deSLogan Gunthorpe 	INIT_LIST_HEAD(&stuser->list);
323080b47deSLogan Gunthorpe 	init_completion(&stuser->comp);
324080b47deSLogan Gunthorpe 	stuser->event_cnt = atomic_read(&stdev->event_cnt);
325080b47deSLogan Gunthorpe 
326080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser);
327080b47deSLogan Gunthorpe 
328080b47deSLogan Gunthorpe 	return stuser;
329080b47deSLogan Gunthorpe }
330080b47deSLogan Gunthorpe 
331080b47deSLogan Gunthorpe static void stuser_free(struct kref *kref)
332080b47deSLogan Gunthorpe {
333080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
334080b47deSLogan Gunthorpe 
335080b47deSLogan Gunthorpe 	stuser = container_of(kref, struct switchtec_user, kref);
336080b47deSLogan Gunthorpe 
337080b47deSLogan Gunthorpe 	dev_dbg(&stuser->stdev->dev, "%s: %p\n", __func__, stuser);
338080b47deSLogan Gunthorpe 
339080b47deSLogan Gunthorpe 	put_device(&stuser->stdev->dev);
340080b47deSLogan Gunthorpe 	kfree(stuser);
341080b47deSLogan Gunthorpe }
342080b47deSLogan Gunthorpe 
343080b47deSLogan Gunthorpe static void stuser_put(struct switchtec_user *stuser)
344080b47deSLogan Gunthorpe {
345080b47deSLogan Gunthorpe 	kref_put(&stuser->kref, stuser_free);
346080b47deSLogan Gunthorpe }
347080b47deSLogan Gunthorpe 
348080b47deSLogan Gunthorpe static void stuser_set_state(struct switchtec_user *stuser,
349080b47deSLogan Gunthorpe 			     enum mrpc_state state)
350080b47deSLogan Gunthorpe {
351080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
352080b47deSLogan Gunthorpe 
353080b47deSLogan Gunthorpe 	const char * const state_names[] = {
354080b47deSLogan Gunthorpe 		[MRPC_IDLE] = "IDLE",
355080b47deSLogan Gunthorpe 		[MRPC_QUEUED] = "QUEUED",
356080b47deSLogan Gunthorpe 		[MRPC_RUNNING] = "RUNNING",
357080b47deSLogan Gunthorpe 		[MRPC_DONE] = "DONE",
358080b47deSLogan Gunthorpe 	};
359080b47deSLogan Gunthorpe 
360080b47deSLogan Gunthorpe 	stuser->state = state;
361080b47deSLogan Gunthorpe 
362080b47deSLogan Gunthorpe 	dev_dbg(&stuser->stdev->dev, "stuser state %p -> %s",
363080b47deSLogan Gunthorpe 		stuser, state_names[state]);
364080b47deSLogan Gunthorpe }
365080b47deSLogan Gunthorpe 
366080b47deSLogan Gunthorpe static void mrpc_complete_cmd(struct switchtec_dev *stdev);
367080b47deSLogan Gunthorpe 
368080b47deSLogan Gunthorpe static void mrpc_cmd_submit(struct switchtec_dev *stdev)
369080b47deSLogan Gunthorpe {
370080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
371080b47deSLogan Gunthorpe 
372080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
373080b47deSLogan Gunthorpe 
374080b47deSLogan Gunthorpe 	if (stdev->mrpc_busy)
375080b47deSLogan Gunthorpe 		return;
376080b47deSLogan Gunthorpe 
377080b47deSLogan Gunthorpe 	if (list_empty(&stdev->mrpc_queue))
378080b47deSLogan Gunthorpe 		return;
379080b47deSLogan Gunthorpe 
380080b47deSLogan Gunthorpe 	stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
381080b47deSLogan Gunthorpe 			    list);
382080b47deSLogan Gunthorpe 
383080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_RUNNING);
384080b47deSLogan Gunthorpe 	stdev->mrpc_busy = 1;
385080b47deSLogan Gunthorpe 	memcpy_toio(&stdev->mmio_mrpc->input_data,
386080b47deSLogan Gunthorpe 		    stuser->data, stuser->data_len);
387080b47deSLogan Gunthorpe 	iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd);
388080b47deSLogan Gunthorpe 
389080b47deSLogan Gunthorpe 	stuser->status = ioread32(&stdev->mmio_mrpc->status);
390080b47deSLogan Gunthorpe 	if (stuser->status != SWITCHTEC_MRPC_STATUS_INPROGRESS)
391080b47deSLogan Gunthorpe 		mrpc_complete_cmd(stdev);
392080b47deSLogan Gunthorpe 
393080b47deSLogan Gunthorpe 	schedule_delayed_work(&stdev->mrpc_timeout,
394080b47deSLogan Gunthorpe 			      msecs_to_jiffies(500));
395080b47deSLogan Gunthorpe }
396080b47deSLogan Gunthorpe 
397080b47deSLogan Gunthorpe static int mrpc_queue_cmd(struct switchtec_user *stuser)
398080b47deSLogan Gunthorpe {
399080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
400080b47deSLogan Gunthorpe 
401080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
402080b47deSLogan Gunthorpe 
403080b47deSLogan Gunthorpe 	kref_get(&stuser->kref);
404080b47deSLogan Gunthorpe 	stuser->read_len = sizeof(stuser->data);
405080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_QUEUED);
406080b47deSLogan Gunthorpe 	init_completion(&stuser->comp);
407080b47deSLogan Gunthorpe 	list_add_tail(&stuser->list, &stdev->mrpc_queue);
408080b47deSLogan Gunthorpe 
409080b47deSLogan Gunthorpe 	mrpc_cmd_submit(stdev);
410080b47deSLogan Gunthorpe 
411080b47deSLogan Gunthorpe 	return 0;
412080b47deSLogan Gunthorpe }
413080b47deSLogan Gunthorpe 
414080b47deSLogan Gunthorpe static void mrpc_complete_cmd(struct switchtec_dev *stdev)
415080b47deSLogan Gunthorpe {
416080b47deSLogan Gunthorpe 	/* requires the mrpc_mutex to already be held when called */
417080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
418080b47deSLogan Gunthorpe 
419080b47deSLogan Gunthorpe 	if (list_empty(&stdev->mrpc_queue))
420080b47deSLogan Gunthorpe 		return;
421080b47deSLogan Gunthorpe 
422080b47deSLogan Gunthorpe 	stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
423080b47deSLogan Gunthorpe 			    list);
424080b47deSLogan Gunthorpe 
425080b47deSLogan Gunthorpe 	stuser->status = ioread32(&stdev->mmio_mrpc->status);
426080b47deSLogan Gunthorpe 	if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS)
427080b47deSLogan Gunthorpe 		return;
428080b47deSLogan Gunthorpe 
429080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_DONE);
430080b47deSLogan Gunthorpe 	stuser->return_code = 0;
431080b47deSLogan Gunthorpe 
432080b47deSLogan Gunthorpe 	if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE)
433080b47deSLogan Gunthorpe 		goto out;
434080b47deSLogan Gunthorpe 
435080b47deSLogan Gunthorpe 	stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value);
436080b47deSLogan Gunthorpe 	if (stuser->return_code != 0)
437080b47deSLogan Gunthorpe 		goto out;
438080b47deSLogan Gunthorpe 
439080b47deSLogan Gunthorpe 	memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data,
440080b47deSLogan Gunthorpe 		      stuser->read_len);
441080b47deSLogan Gunthorpe 
442080b47deSLogan Gunthorpe out:
443080b47deSLogan Gunthorpe 	complete_all(&stuser->comp);
444080b47deSLogan Gunthorpe 	list_del_init(&stuser->list);
445080b47deSLogan Gunthorpe 	stuser_put(stuser);
446080b47deSLogan Gunthorpe 	stdev->mrpc_busy = 0;
447080b47deSLogan Gunthorpe 
448080b47deSLogan Gunthorpe 	mrpc_cmd_submit(stdev);
449080b47deSLogan Gunthorpe }
450080b47deSLogan Gunthorpe 
451080b47deSLogan Gunthorpe static void mrpc_event_work(struct work_struct *work)
452080b47deSLogan Gunthorpe {
453080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
454080b47deSLogan Gunthorpe 
455080b47deSLogan Gunthorpe 	stdev = container_of(work, struct switchtec_dev, mrpc_work);
456080b47deSLogan Gunthorpe 
457080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s\n", __func__);
458080b47deSLogan Gunthorpe 
459080b47deSLogan Gunthorpe 	mutex_lock(&stdev->mrpc_mutex);
460080b47deSLogan Gunthorpe 	cancel_delayed_work(&stdev->mrpc_timeout);
461080b47deSLogan Gunthorpe 	mrpc_complete_cmd(stdev);
462080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
463080b47deSLogan Gunthorpe }
464080b47deSLogan Gunthorpe 
465080b47deSLogan Gunthorpe static void mrpc_timeout_work(struct work_struct *work)
466080b47deSLogan Gunthorpe {
467080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
468080b47deSLogan Gunthorpe 	u32 status;
469080b47deSLogan Gunthorpe 
470080b47deSLogan Gunthorpe 	stdev = container_of(work, struct switchtec_dev, mrpc_timeout.work);
471080b47deSLogan Gunthorpe 
472080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s\n", __func__);
473080b47deSLogan Gunthorpe 
474080b47deSLogan Gunthorpe 	mutex_lock(&stdev->mrpc_mutex);
475080b47deSLogan Gunthorpe 
476080b47deSLogan Gunthorpe 	status = ioread32(&stdev->mmio_mrpc->status);
477080b47deSLogan Gunthorpe 	if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) {
478080b47deSLogan Gunthorpe 		schedule_delayed_work(&stdev->mrpc_timeout,
479080b47deSLogan Gunthorpe 				      msecs_to_jiffies(500));
480080b47deSLogan Gunthorpe 		goto out;
481080b47deSLogan Gunthorpe 	}
482080b47deSLogan Gunthorpe 
483080b47deSLogan Gunthorpe 	mrpc_complete_cmd(stdev);
484080b47deSLogan Gunthorpe 
485080b47deSLogan Gunthorpe out:
486080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
487080b47deSLogan Gunthorpe }
488080b47deSLogan Gunthorpe 
489*5d8e1881SLogan Gunthorpe static ssize_t device_version_show(struct device *dev,
490*5d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
491*5d8e1881SLogan Gunthorpe {
492*5d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
493*5d8e1881SLogan Gunthorpe 	u32 ver;
494*5d8e1881SLogan Gunthorpe 
495*5d8e1881SLogan Gunthorpe 	ver = ioread32(&stdev->mmio_sys_info->device_version);
496*5d8e1881SLogan Gunthorpe 
497*5d8e1881SLogan Gunthorpe 	return sprintf(buf, "%x\n", ver);
498*5d8e1881SLogan Gunthorpe }
499*5d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(device_version);
500*5d8e1881SLogan Gunthorpe 
501*5d8e1881SLogan Gunthorpe static ssize_t fw_version_show(struct device *dev,
502*5d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
503*5d8e1881SLogan Gunthorpe {
504*5d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
505*5d8e1881SLogan Gunthorpe 	u32 ver;
506*5d8e1881SLogan Gunthorpe 
507*5d8e1881SLogan Gunthorpe 	ver = ioread32(&stdev->mmio_sys_info->firmware_version);
508*5d8e1881SLogan Gunthorpe 
509*5d8e1881SLogan Gunthorpe 	return sprintf(buf, "%08x\n", ver);
510*5d8e1881SLogan Gunthorpe }
511*5d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(fw_version);
512*5d8e1881SLogan Gunthorpe 
513*5d8e1881SLogan Gunthorpe static ssize_t io_string_show(char *buf, void __iomem *attr, size_t len)
514*5d8e1881SLogan Gunthorpe {
515*5d8e1881SLogan Gunthorpe 	int i;
516*5d8e1881SLogan Gunthorpe 
517*5d8e1881SLogan Gunthorpe 	memcpy_fromio(buf, attr, len);
518*5d8e1881SLogan Gunthorpe 	buf[len] = '\n';
519*5d8e1881SLogan Gunthorpe 	buf[len + 1] = 0;
520*5d8e1881SLogan Gunthorpe 
521*5d8e1881SLogan Gunthorpe 	for (i = len - 1; i > 0; i--) {
522*5d8e1881SLogan Gunthorpe 		if (buf[i] != ' ')
523*5d8e1881SLogan Gunthorpe 			break;
524*5d8e1881SLogan Gunthorpe 		buf[i] = '\n';
525*5d8e1881SLogan Gunthorpe 		buf[i + 1] = 0;
526*5d8e1881SLogan Gunthorpe 	}
527*5d8e1881SLogan Gunthorpe 
528*5d8e1881SLogan Gunthorpe 	return strlen(buf);
529*5d8e1881SLogan Gunthorpe }
530*5d8e1881SLogan Gunthorpe 
531*5d8e1881SLogan Gunthorpe #define DEVICE_ATTR_SYS_INFO_STR(field) \
532*5d8e1881SLogan Gunthorpe static ssize_t field ## _show(struct device *dev, \
533*5d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf) \
534*5d8e1881SLogan Gunthorpe { \
535*5d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev); \
536*5d8e1881SLogan Gunthorpe 	return io_string_show(buf, &stdev->mmio_sys_info->field, \
537*5d8e1881SLogan Gunthorpe 			    sizeof(stdev->mmio_sys_info->field)); \
538*5d8e1881SLogan Gunthorpe } \
539*5d8e1881SLogan Gunthorpe \
540*5d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(field)
541*5d8e1881SLogan Gunthorpe 
542*5d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(vendor_id);
543*5d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(product_id);
544*5d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(product_revision);
545*5d8e1881SLogan Gunthorpe DEVICE_ATTR_SYS_INFO_STR(component_vendor);
546*5d8e1881SLogan Gunthorpe 
547*5d8e1881SLogan Gunthorpe static ssize_t component_id_show(struct device *dev,
548*5d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
549*5d8e1881SLogan Gunthorpe {
550*5d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
551*5d8e1881SLogan Gunthorpe 	int id = ioread16(&stdev->mmio_sys_info->component_id);
552*5d8e1881SLogan Gunthorpe 
553*5d8e1881SLogan Gunthorpe 	return sprintf(buf, "PM%04X\n", id);
554*5d8e1881SLogan Gunthorpe }
555*5d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(component_id);
556*5d8e1881SLogan Gunthorpe 
557*5d8e1881SLogan Gunthorpe static ssize_t component_revision_show(struct device *dev,
558*5d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
559*5d8e1881SLogan Gunthorpe {
560*5d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
561*5d8e1881SLogan Gunthorpe 	int rev = ioread8(&stdev->mmio_sys_info->component_revision);
562*5d8e1881SLogan Gunthorpe 
563*5d8e1881SLogan Gunthorpe 	return sprintf(buf, "%d\n", rev);
564*5d8e1881SLogan Gunthorpe }
565*5d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(component_revision);
566*5d8e1881SLogan Gunthorpe 
567*5d8e1881SLogan Gunthorpe static ssize_t partition_show(struct device *dev,
568*5d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
569*5d8e1881SLogan Gunthorpe {
570*5d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
571*5d8e1881SLogan Gunthorpe 
572*5d8e1881SLogan Gunthorpe 	return sprintf(buf, "%d\n", stdev->partition);
573*5d8e1881SLogan Gunthorpe }
574*5d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(partition);
575*5d8e1881SLogan Gunthorpe 
576*5d8e1881SLogan Gunthorpe static ssize_t partition_count_show(struct device *dev,
577*5d8e1881SLogan Gunthorpe 	struct device_attribute *attr, char *buf)
578*5d8e1881SLogan Gunthorpe {
579*5d8e1881SLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
580*5d8e1881SLogan Gunthorpe 
581*5d8e1881SLogan Gunthorpe 	return sprintf(buf, "%d\n", stdev->partition_count);
582*5d8e1881SLogan Gunthorpe }
583*5d8e1881SLogan Gunthorpe static DEVICE_ATTR_RO(partition_count);
584*5d8e1881SLogan Gunthorpe 
585*5d8e1881SLogan Gunthorpe static struct attribute *switchtec_device_attrs[] = {
586*5d8e1881SLogan Gunthorpe 	&dev_attr_device_version.attr,
587*5d8e1881SLogan Gunthorpe 	&dev_attr_fw_version.attr,
588*5d8e1881SLogan Gunthorpe 	&dev_attr_vendor_id.attr,
589*5d8e1881SLogan Gunthorpe 	&dev_attr_product_id.attr,
590*5d8e1881SLogan Gunthorpe 	&dev_attr_product_revision.attr,
591*5d8e1881SLogan Gunthorpe 	&dev_attr_component_vendor.attr,
592*5d8e1881SLogan Gunthorpe 	&dev_attr_component_id.attr,
593*5d8e1881SLogan Gunthorpe 	&dev_attr_component_revision.attr,
594*5d8e1881SLogan Gunthorpe 	&dev_attr_partition.attr,
595*5d8e1881SLogan Gunthorpe 	&dev_attr_partition_count.attr,
596*5d8e1881SLogan Gunthorpe 	NULL,
597*5d8e1881SLogan Gunthorpe };
598*5d8e1881SLogan Gunthorpe 
599*5d8e1881SLogan Gunthorpe ATTRIBUTE_GROUPS(switchtec_device);
600*5d8e1881SLogan Gunthorpe 
601080b47deSLogan Gunthorpe static int switchtec_dev_open(struct inode *inode, struct file *filp)
602080b47deSLogan Gunthorpe {
603080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
604080b47deSLogan Gunthorpe 	struct switchtec_user *stuser;
605080b47deSLogan Gunthorpe 
606080b47deSLogan Gunthorpe 	stdev = container_of(inode->i_cdev, struct switchtec_dev, cdev);
607080b47deSLogan Gunthorpe 
608080b47deSLogan Gunthorpe 	stuser = stuser_create(stdev);
609080b47deSLogan Gunthorpe 	if (IS_ERR(stuser))
610080b47deSLogan Gunthorpe 		return PTR_ERR(stuser);
611080b47deSLogan Gunthorpe 
612080b47deSLogan Gunthorpe 	filp->private_data = stuser;
613080b47deSLogan Gunthorpe 	nonseekable_open(inode, filp);
614080b47deSLogan Gunthorpe 
615080b47deSLogan Gunthorpe 	dev_dbg(&stdev->dev, "%s: %p\n", __func__, stuser);
616080b47deSLogan Gunthorpe 
617080b47deSLogan Gunthorpe 	return 0;
618080b47deSLogan Gunthorpe }
619080b47deSLogan Gunthorpe 
620080b47deSLogan Gunthorpe static int switchtec_dev_release(struct inode *inode, struct file *filp)
621080b47deSLogan Gunthorpe {
622080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
623080b47deSLogan Gunthorpe 
624080b47deSLogan Gunthorpe 	stuser_put(stuser);
625080b47deSLogan Gunthorpe 
626080b47deSLogan Gunthorpe 	return 0;
627080b47deSLogan Gunthorpe }
628080b47deSLogan Gunthorpe 
629080b47deSLogan Gunthorpe static int lock_mutex_and_test_alive(struct switchtec_dev *stdev)
630080b47deSLogan Gunthorpe {
631080b47deSLogan Gunthorpe 	if (mutex_lock_interruptible(&stdev->mrpc_mutex))
632080b47deSLogan Gunthorpe 		return -EINTR;
633080b47deSLogan Gunthorpe 
634080b47deSLogan Gunthorpe 	if (!stdev->alive) {
635080b47deSLogan Gunthorpe 		mutex_unlock(&stdev->mrpc_mutex);
636080b47deSLogan Gunthorpe 		return -ENODEV;
637080b47deSLogan Gunthorpe 	}
638080b47deSLogan Gunthorpe 
639080b47deSLogan Gunthorpe 	return 0;
640080b47deSLogan Gunthorpe }
641080b47deSLogan Gunthorpe 
642080b47deSLogan Gunthorpe static ssize_t switchtec_dev_write(struct file *filp, const char __user *data,
643080b47deSLogan Gunthorpe 				   size_t size, loff_t *off)
644080b47deSLogan Gunthorpe {
645080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
646080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
647080b47deSLogan Gunthorpe 	int rc;
648080b47deSLogan Gunthorpe 
649080b47deSLogan Gunthorpe 	if (size < sizeof(stuser->cmd) ||
650080b47deSLogan Gunthorpe 	    size > sizeof(stuser->cmd) + sizeof(stuser->data))
651080b47deSLogan Gunthorpe 		return -EINVAL;
652080b47deSLogan Gunthorpe 
653080b47deSLogan Gunthorpe 	stuser->data_len = size - sizeof(stuser->cmd);
654080b47deSLogan Gunthorpe 
655080b47deSLogan Gunthorpe 	rc = lock_mutex_and_test_alive(stdev);
656080b47deSLogan Gunthorpe 	if (rc)
657080b47deSLogan Gunthorpe 		return rc;
658080b47deSLogan Gunthorpe 
659080b47deSLogan Gunthorpe 	if (stuser->state != MRPC_IDLE) {
660080b47deSLogan Gunthorpe 		rc = -EBADE;
661080b47deSLogan Gunthorpe 		goto out;
662080b47deSLogan Gunthorpe 	}
663080b47deSLogan Gunthorpe 
664080b47deSLogan Gunthorpe 	rc = copy_from_user(&stuser->cmd, data, sizeof(stuser->cmd));
665080b47deSLogan Gunthorpe 	if (rc) {
666080b47deSLogan Gunthorpe 		rc = -EFAULT;
667080b47deSLogan Gunthorpe 		goto out;
668080b47deSLogan Gunthorpe 	}
669080b47deSLogan Gunthorpe 
670080b47deSLogan Gunthorpe 	data += sizeof(stuser->cmd);
671080b47deSLogan Gunthorpe 	rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd));
672080b47deSLogan Gunthorpe 	if (rc) {
673080b47deSLogan Gunthorpe 		rc = -EFAULT;
674080b47deSLogan Gunthorpe 		goto out;
675080b47deSLogan Gunthorpe 	}
676080b47deSLogan Gunthorpe 
677080b47deSLogan Gunthorpe 	rc = mrpc_queue_cmd(stuser);
678080b47deSLogan Gunthorpe 
679080b47deSLogan Gunthorpe out:
680080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
681080b47deSLogan Gunthorpe 
682080b47deSLogan Gunthorpe 	if (rc)
683080b47deSLogan Gunthorpe 		return rc;
684080b47deSLogan Gunthorpe 
685080b47deSLogan Gunthorpe 	return size;
686080b47deSLogan Gunthorpe }
687080b47deSLogan Gunthorpe 
688080b47deSLogan Gunthorpe static ssize_t switchtec_dev_read(struct file *filp, char __user *data,
689080b47deSLogan Gunthorpe 				  size_t size, loff_t *off)
690080b47deSLogan Gunthorpe {
691080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
692080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
693080b47deSLogan Gunthorpe 	int rc;
694080b47deSLogan Gunthorpe 
695080b47deSLogan Gunthorpe 	if (size < sizeof(stuser->cmd) ||
696080b47deSLogan Gunthorpe 	    size > sizeof(stuser->cmd) + sizeof(stuser->data))
697080b47deSLogan Gunthorpe 		return -EINVAL;
698080b47deSLogan Gunthorpe 
699080b47deSLogan Gunthorpe 	rc = lock_mutex_and_test_alive(stdev);
700080b47deSLogan Gunthorpe 	if (rc)
701080b47deSLogan Gunthorpe 		return rc;
702080b47deSLogan Gunthorpe 
703080b47deSLogan Gunthorpe 	if (stuser->state == MRPC_IDLE) {
704080b47deSLogan Gunthorpe 		mutex_unlock(&stdev->mrpc_mutex);
705080b47deSLogan Gunthorpe 		return -EBADE;
706080b47deSLogan Gunthorpe 	}
707080b47deSLogan Gunthorpe 
708080b47deSLogan Gunthorpe 	stuser->read_len = size - sizeof(stuser->return_code);
709080b47deSLogan Gunthorpe 
710080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
711080b47deSLogan Gunthorpe 
712080b47deSLogan Gunthorpe 	if (filp->f_flags & O_NONBLOCK) {
713080b47deSLogan Gunthorpe 		if (!try_wait_for_completion(&stuser->comp))
714080b47deSLogan Gunthorpe 			return -EAGAIN;
715080b47deSLogan Gunthorpe 	} else {
716080b47deSLogan Gunthorpe 		rc = wait_for_completion_interruptible(&stuser->comp);
717080b47deSLogan Gunthorpe 		if (rc < 0)
718080b47deSLogan Gunthorpe 			return rc;
719080b47deSLogan Gunthorpe 	}
720080b47deSLogan Gunthorpe 
721080b47deSLogan Gunthorpe 	rc = lock_mutex_and_test_alive(stdev);
722080b47deSLogan Gunthorpe 	if (rc)
723080b47deSLogan Gunthorpe 		return rc;
724080b47deSLogan Gunthorpe 
725080b47deSLogan Gunthorpe 	if (stuser->state != MRPC_DONE) {
726080b47deSLogan Gunthorpe 		mutex_unlock(&stdev->mrpc_mutex);
727080b47deSLogan Gunthorpe 		return -EBADE;
728080b47deSLogan Gunthorpe 	}
729080b47deSLogan Gunthorpe 
730080b47deSLogan Gunthorpe 	rc = copy_to_user(data, &stuser->return_code,
731080b47deSLogan Gunthorpe 			  sizeof(stuser->return_code));
732080b47deSLogan Gunthorpe 	if (rc) {
733080b47deSLogan Gunthorpe 		rc = -EFAULT;
734080b47deSLogan Gunthorpe 		goto out;
735080b47deSLogan Gunthorpe 	}
736080b47deSLogan Gunthorpe 
737080b47deSLogan Gunthorpe 	data += sizeof(stuser->return_code);
738080b47deSLogan Gunthorpe 	rc = copy_to_user(data, &stuser->data,
739080b47deSLogan Gunthorpe 			  size - sizeof(stuser->return_code));
740080b47deSLogan Gunthorpe 	if (rc) {
741080b47deSLogan Gunthorpe 		rc = -EFAULT;
742080b47deSLogan Gunthorpe 		goto out;
743080b47deSLogan Gunthorpe 	}
744080b47deSLogan Gunthorpe 
745080b47deSLogan Gunthorpe 	stuser_set_state(stuser, MRPC_IDLE);
746080b47deSLogan Gunthorpe 
747080b47deSLogan Gunthorpe out:
748080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
749080b47deSLogan Gunthorpe 
750080b47deSLogan Gunthorpe 	if (stuser->status == SWITCHTEC_MRPC_STATUS_DONE)
751080b47deSLogan Gunthorpe 		return size;
752080b47deSLogan Gunthorpe 	else if (stuser->status == SWITCHTEC_MRPC_STATUS_INTERRUPTED)
753080b47deSLogan Gunthorpe 		return -ENXIO;
754080b47deSLogan Gunthorpe 	else
755080b47deSLogan Gunthorpe 		return -EBADMSG;
756080b47deSLogan Gunthorpe }
757080b47deSLogan Gunthorpe 
758080b47deSLogan Gunthorpe static unsigned int switchtec_dev_poll(struct file *filp, poll_table *wait)
759080b47deSLogan Gunthorpe {
760080b47deSLogan Gunthorpe 	struct switchtec_user *stuser = filp->private_data;
761080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = stuser->stdev;
762080b47deSLogan Gunthorpe 	int ret = 0;
763080b47deSLogan Gunthorpe 
764080b47deSLogan Gunthorpe 	poll_wait(filp, &stuser->comp.wait, wait);
765080b47deSLogan Gunthorpe 	poll_wait(filp, &stdev->event_wq, wait);
766080b47deSLogan Gunthorpe 
767080b47deSLogan Gunthorpe 	if (lock_mutex_and_test_alive(stdev))
768080b47deSLogan Gunthorpe 		return POLLIN | POLLRDHUP | POLLOUT | POLLERR | POLLHUP;
769080b47deSLogan Gunthorpe 
770080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
771080b47deSLogan Gunthorpe 
772080b47deSLogan Gunthorpe 	if (try_wait_for_completion(&stuser->comp))
773080b47deSLogan Gunthorpe 		ret |= POLLIN | POLLRDNORM;
774080b47deSLogan Gunthorpe 
775080b47deSLogan Gunthorpe 	if (stuser->event_cnt != atomic_read(&stdev->event_cnt))
776080b47deSLogan Gunthorpe 		ret |= POLLPRI | POLLRDBAND;
777080b47deSLogan Gunthorpe 
778080b47deSLogan Gunthorpe 	return ret;
779080b47deSLogan Gunthorpe }
780080b47deSLogan Gunthorpe 
781080b47deSLogan Gunthorpe static const struct file_operations switchtec_fops = {
782080b47deSLogan Gunthorpe 	.owner = THIS_MODULE,
783080b47deSLogan Gunthorpe 	.open = switchtec_dev_open,
784080b47deSLogan Gunthorpe 	.release = switchtec_dev_release,
785080b47deSLogan Gunthorpe 	.write = switchtec_dev_write,
786080b47deSLogan Gunthorpe 	.read = switchtec_dev_read,
787080b47deSLogan Gunthorpe 	.poll = switchtec_dev_poll,
788080b47deSLogan Gunthorpe };
789080b47deSLogan Gunthorpe 
790080b47deSLogan Gunthorpe static void stdev_release(struct device *dev)
791080b47deSLogan Gunthorpe {
792080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = to_stdev(dev);
793080b47deSLogan Gunthorpe 
794080b47deSLogan Gunthorpe 	kfree(stdev);
795080b47deSLogan Gunthorpe }
796080b47deSLogan Gunthorpe 
797080b47deSLogan Gunthorpe static void stdev_kill(struct switchtec_dev *stdev)
798080b47deSLogan Gunthorpe {
799080b47deSLogan Gunthorpe 	struct switchtec_user *stuser, *tmpuser;
800080b47deSLogan Gunthorpe 
801080b47deSLogan Gunthorpe 	pci_clear_master(stdev->pdev);
802080b47deSLogan Gunthorpe 
803080b47deSLogan Gunthorpe 	cancel_delayed_work_sync(&stdev->mrpc_timeout);
804080b47deSLogan Gunthorpe 
805080b47deSLogan Gunthorpe 	/* Mark the hardware as unavailable and complete all completions */
806080b47deSLogan Gunthorpe 	mutex_lock(&stdev->mrpc_mutex);
807080b47deSLogan Gunthorpe 	stdev->alive = false;
808080b47deSLogan Gunthorpe 
809080b47deSLogan Gunthorpe 	/* Wake up and kill any users waiting on an MRPC request */
810080b47deSLogan Gunthorpe 	list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) {
811080b47deSLogan Gunthorpe 		complete_all(&stuser->comp);
812080b47deSLogan Gunthorpe 		list_del_init(&stuser->list);
813080b47deSLogan Gunthorpe 		stuser_put(stuser);
814080b47deSLogan Gunthorpe 	}
815080b47deSLogan Gunthorpe 
816080b47deSLogan Gunthorpe 	mutex_unlock(&stdev->mrpc_mutex);
817080b47deSLogan Gunthorpe 
818080b47deSLogan Gunthorpe 	/* Wake up any users waiting on event_wq */
819080b47deSLogan Gunthorpe 	wake_up_interruptible(&stdev->event_wq);
820080b47deSLogan Gunthorpe }
821080b47deSLogan Gunthorpe 
822080b47deSLogan Gunthorpe static struct switchtec_dev *stdev_create(struct pci_dev *pdev)
823080b47deSLogan Gunthorpe {
824080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
825080b47deSLogan Gunthorpe 	int minor;
826080b47deSLogan Gunthorpe 	struct device *dev;
827080b47deSLogan Gunthorpe 	struct cdev *cdev;
828080b47deSLogan Gunthorpe 	int rc;
829080b47deSLogan Gunthorpe 
830080b47deSLogan Gunthorpe 	stdev = kzalloc_node(sizeof(*stdev), GFP_KERNEL,
831080b47deSLogan Gunthorpe 			     dev_to_node(&pdev->dev));
832080b47deSLogan Gunthorpe 	if (!stdev)
833080b47deSLogan Gunthorpe 		return ERR_PTR(-ENOMEM);
834080b47deSLogan Gunthorpe 
835080b47deSLogan Gunthorpe 	stdev->alive = true;
836080b47deSLogan Gunthorpe 	stdev->pdev = pdev;
837080b47deSLogan Gunthorpe 	INIT_LIST_HEAD(&stdev->mrpc_queue);
838080b47deSLogan Gunthorpe 	mutex_init(&stdev->mrpc_mutex);
839080b47deSLogan Gunthorpe 	stdev->mrpc_busy = 0;
840080b47deSLogan Gunthorpe 	INIT_WORK(&stdev->mrpc_work, mrpc_event_work);
841080b47deSLogan Gunthorpe 	INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work);
842080b47deSLogan Gunthorpe 	init_waitqueue_head(&stdev->event_wq);
843080b47deSLogan Gunthorpe 	atomic_set(&stdev->event_cnt, 0);
844080b47deSLogan Gunthorpe 
845080b47deSLogan Gunthorpe 	dev = &stdev->dev;
846080b47deSLogan Gunthorpe 	device_initialize(dev);
847080b47deSLogan Gunthorpe 	dev->class = switchtec_class;
848080b47deSLogan Gunthorpe 	dev->parent = &pdev->dev;
849*5d8e1881SLogan Gunthorpe 	dev->groups = switchtec_device_groups;
850080b47deSLogan Gunthorpe 	dev->release = stdev_release;
851080b47deSLogan Gunthorpe 
852080b47deSLogan Gunthorpe 	minor = ida_simple_get(&switchtec_minor_ida, 0, 0,
853080b47deSLogan Gunthorpe 			       GFP_KERNEL);
854080b47deSLogan Gunthorpe 	if (minor < 0) {
855080b47deSLogan Gunthorpe 		rc = minor;
856080b47deSLogan Gunthorpe 		goto err_put;
857080b47deSLogan Gunthorpe 	}
858080b47deSLogan Gunthorpe 
859080b47deSLogan Gunthorpe 	dev->devt = MKDEV(MAJOR(switchtec_devt), minor);
860080b47deSLogan Gunthorpe 	dev_set_name(dev, "switchtec%d", minor);
861080b47deSLogan Gunthorpe 
862080b47deSLogan Gunthorpe 	cdev = &stdev->cdev;
863080b47deSLogan Gunthorpe 	cdev_init(cdev, &switchtec_fops);
864080b47deSLogan Gunthorpe 	cdev->owner = THIS_MODULE;
865080b47deSLogan Gunthorpe 	cdev->kobj.parent = &dev->kobj;
866080b47deSLogan Gunthorpe 
867080b47deSLogan Gunthorpe 	return stdev;
868080b47deSLogan Gunthorpe 
869080b47deSLogan Gunthorpe err_put:
870080b47deSLogan Gunthorpe 	put_device(&stdev->dev);
871080b47deSLogan Gunthorpe 	return ERR_PTR(rc);
872080b47deSLogan Gunthorpe }
873080b47deSLogan Gunthorpe 
874080b47deSLogan Gunthorpe static irqreturn_t switchtec_event_isr(int irq, void *dev)
875080b47deSLogan Gunthorpe {
876080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = dev;
877080b47deSLogan Gunthorpe 	u32 reg;
878080b47deSLogan Gunthorpe 	irqreturn_t ret = IRQ_NONE;
879080b47deSLogan Gunthorpe 
880080b47deSLogan Gunthorpe 	reg = ioread32(&stdev->mmio_part_cfg->mrpc_comp_hdr);
881080b47deSLogan Gunthorpe 	if (reg & SWITCHTEC_EVENT_OCCURRED) {
882080b47deSLogan Gunthorpe 		dev_dbg(&stdev->dev, "%s: mrpc comp\n", __func__);
883080b47deSLogan Gunthorpe 		ret = IRQ_HANDLED;
884080b47deSLogan Gunthorpe 		schedule_work(&stdev->mrpc_work);
885080b47deSLogan Gunthorpe 		iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr);
886080b47deSLogan Gunthorpe 	}
887080b47deSLogan Gunthorpe 
888080b47deSLogan Gunthorpe 	return ret;
889080b47deSLogan Gunthorpe }
890080b47deSLogan Gunthorpe 
891080b47deSLogan Gunthorpe static int switchtec_init_isr(struct switchtec_dev *stdev)
892080b47deSLogan Gunthorpe {
893080b47deSLogan Gunthorpe 	int nvecs;
894080b47deSLogan Gunthorpe 	int event_irq;
895080b47deSLogan Gunthorpe 
896080b47deSLogan Gunthorpe 	nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4,
897080b47deSLogan Gunthorpe 				      PCI_IRQ_MSIX | PCI_IRQ_MSI);
898080b47deSLogan Gunthorpe 	if (nvecs < 0)
899080b47deSLogan Gunthorpe 		return nvecs;
900080b47deSLogan Gunthorpe 
901080b47deSLogan Gunthorpe 	event_irq = ioread32(&stdev->mmio_part_cfg->vep_vector_number);
902080b47deSLogan Gunthorpe 	if (event_irq < 0 || event_irq >= nvecs)
903080b47deSLogan Gunthorpe 		return -EFAULT;
904080b47deSLogan Gunthorpe 
905080b47deSLogan Gunthorpe 	event_irq = pci_irq_vector(stdev->pdev, event_irq);
906080b47deSLogan Gunthorpe 	if (event_irq < 0)
907080b47deSLogan Gunthorpe 		return event_irq;
908080b47deSLogan Gunthorpe 
909080b47deSLogan Gunthorpe 	return devm_request_irq(&stdev->pdev->dev, event_irq,
910080b47deSLogan Gunthorpe 				switchtec_event_isr, 0,
911080b47deSLogan Gunthorpe 				KBUILD_MODNAME, stdev);
912080b47deSLogan Gunthorpe }
913080b47deSLogan Gunthorpe 
914080b47deSLogan Gunthorpe static void init_pff(struct switchtec_dev *stdev)
915080b47deSLogan Gunthorpe {
916080b47deSLogan Gunthorpe 	int i;
917080b47deSLogan Gunthorpe 	u32 reg;
918080b47deSLogan Gunthorpe 	struct part_cfg_regs *pcfg = stdev->mmio_part_cfg;
919080b47deSLogan Gunthorpe 
920080b47deSLogan Gunthorpe 	for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
921080b47deSLogan Gunthorpe 		reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id);
922080b47deSLogan Gunthorpe 		if (reg != MICROSEMI_VENDOR_ID)
923080b47deSLogan Gunthorpe 			break;
924080b47deSLogan Gunthorpe 	}
925080b47deSLogan Gunthorpe 
926080b47deSLogan Gunthorpe 	stdev->pff_csr_count = i;
927080b47deSLogan Gunthorpe 
928080b47deSLogan Gunthorpe 	reg = ioread32(&pcfg->usp_pff_inst_id);
929080b47deSLogan Gunthorpe 	if (reg < SWITCHTEC_MAX_PFF_CSR)
930080b47deSLogan Gunthorpe 		stdev->pff_local[reg] = 1;
931080b47deSLogan Gunthorpe 
932080b47deSLogan Gunthorpe 	reg = ioread32(&pcfg->vep_pff_inst_id);
933080b47deSLogan Gunthorpe 	if (reg < SWITCHTEC_MAX_PFF_CSR)
934080b47deSLogan Gunthorpe 		stdev->pff_local[reg] = 1;
935080b47deSLogan Gunthorpe 
936080b47deSLogan Gunthorpe 	for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) {
937080b47deSLogan Gunthorpe 		reg = ioread32(&pcfg->dsp_pff_inst_id[i]);
938080b47deSLogan Gunthorpe 		if (reg < SWITCHTEC_MAX_PFF_CSR)
939080b47deSLogan Gunthorpe 			stdev->pff_local[reg] = 1;
940080b47deSLogan Gunthorpe 	}
941080b47deSLogan Gunthorpe }
942080b47deSLogan Gunthorpe 
943080b47deSLogan Gunthorpe static int switchtec_init_pci(struct switchtec_dev *stdev,
944080b47deSLogan Gunthorpe 			      struct pci_dev *pdev)
945080b47deSLogan Gunthorpe {
946080b47deSLogan Gunthorpe 	int rc;
947080b47deSLogan Gunthorpe 
948080b47deSLogan Gunthorpe 	rc = pcim_enable_device(pdev);
949080b47deSLogan Gunthorpe 	if (rc)
950080b47deSLogan Gunthorpe 		return rc;
951080b47deSLogan Gunthorpe 
952080b47deSLogan Gunthorpe 	rc = pcim_iomap_regions(pdev, 0x1, KBUILD_MODNAME);
953080b47deSLogan Gunthorpe 	if (rc)
954080b47deSLogan Gunthorpe 		return rc;
955080b47deSLogan Gunthorpe 
956080b47deSLogan Gunthorpe 	pci_set_master(pdev);
957080b47deSLogan Gunthorpe 
958080b47deSLogan Gunthorpe 	stdev->mmio = pcim_iomap_table(pdev)[0];
959080b47deSLogan Gunthorpe 	stdev->mmio_mrpc = stdev->mmio + SWITCHTEC_GAS_MRPC_OFFSET;
960080b47deSLogan Gunthorpe 	stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET;
961080b47deSLogan Gunthorpe 	stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET;
962080b47deSLogan Gunthorpe 	stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET;
963080b47deSLogan Gunthorpe 	stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET;
964080b47deSLogan Gunthorpe 	stdev->partition = ioread8(&stdev->mmio_ntb->partition_id);
965080b47deSLogan Gunthorpe 	stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count);
966080b47deSLogan Gunthorpe 	stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET;
967080b47deSLogan Gunthorpe 	stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition];
968080b47deSLogan Gunthorpe 	stdev->mmio_pff_csr = stdev->mmio + SWITCHTEC_GAS_PFF_CSR_OFFSET;
969080b47deSLogan Gunthorpe 
970080b47deSLogan Gunthorpe 	init_pff(stdev);
971080b47deSLogan Gunthorpe 
972080b47deSLogan Gunthorpe 	pci_set_drvdata(pdev, stdev);
973080b47deSLogan Gunthorpe 
974080b47deSLogan Gunthorpe 	return 0;
975080b47deSLogan Gunthorpe }
976080b47deSLogan Gunthorpe 
977080b47deSLogan Gunthorpe static int switchtec_pci_probe(struct pci_dev *pdev,
978080b47deSLogan Gunthorpe 			       const struct pci_device_id *id)
979080b47deSLogan Gunthorpe {
980080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev;
981080b47deSLogan Gunthorpe 	int rc;
982080b47deSLogan Gunthorpe 
983080b47deSLogan Gunthorpe 	stdev = stdev_create(pdev);
984080b47deSLogan Gunthorpe 	if (IS_ERR(stdev))
985080b47deSLogan Gunthorpe 		return PTR_ERR(stdev);
986080b47deSLogan Gunthorpe 
987080b47deSLogan Gunthorpe 	rc = switchtec_init_pci(stdev, pdev);
988080b47deSLogan Gunthorpe 	if (rc)
989080b47deSLogan Gunthorpe 		goto err_put;
990080b47deSLogan Gunthorpe 
991080b47deSLogan Gunthorpe 	rc = switchtec_init_isr(stdev);
992080b47deSLogan Gunthorpe 	if (rc) {
993080b47deSLogan Gunthorpe 		dev_err(&stdev->dev, "failed to init isr.\n");
994080b47deSLogan Gunthorpe 		goto err_put;
995080b47deSLogan Gunthorpe 	}
996080b47deSLogan Gunthorpe 
997080b47deSLogan Gunthorpe 	iowrite32(SWITCHTEC_EVENT_CLEAR |
998080b47deSLogan Gunthorpe 		  SWITCHTEC_EVENT_EN_IRQ,
999080b47deSLogan Gunthorpe 		  &stdev->mmio_part_cfg->mrpc_comp_hdr);
1000080b47deSLogan Gunthorpe 
1001080b47deSLogan Gunthorpe 	rc = cdev_add(&stdev->cdev, stdev->dev.devt, 1);
1002080b47deSLogan Gunthorpe 	if (rc)
1003080b47deSLogan Gunthorpe 		goto err_put;
1004080b47deSLogan Gunthorpe 
1005080b47deSLogan Gunthorpe 	rc = device_add(&stdev->dev);
1006080b47deSLogan Gunthorpe 	if (rc)
1007080b47deSLogan Gunthorpe 		goto err_devadd;
1008080b47deSLogan Gunthorpe 
1009080b47deSLogan Gunthorpe 	dev_info(&stdev->dev, "Management device registered.\n");
1010080b47deSLogan Gunthorpe 
1011080b47deSLogan Gunthorpe 	return 0;
1012080b47deSLogan Gunthorpe 
1013080b47deSLogan Gunthorpe err_devadd:
1014080b47deSLogan Gunthorpe 	cdev_del(&stdev->cdev);
1015080b47deSLogan Gunthorpe 	stdev_kill(stdev);
1016080b47deSLogan Gunthorpe err_put:
1017080b47deSLogan Gunthorpe 	ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt));
1018080b47deSLogan Gunthorpe 	put_device(&stdev->dev);
1019080b47deSLogan Gunthorpe 	return rc;
1020080b47deSLogan Gunthorpe }
1021080b47deSLogan Gunthorpe 
1022080b47deSLogan Gunthorpe static void switchtec_pci_remove(struct pci_dev *pdev)
1023080b47deSLogan Gunthorpe {
1024080b47deSLogan Gunthorpe 	struct switchtec_dev *stdev = pci_get_drvdata(pdev);
1025080b47deSLogan Gunthorpe 
1026080b47deSLogan Gunthorpe 	pci_set_drvdata(pdev, NULL);
1027080b47deSLogan Gunthorpe 
1028080b47deSLogan Gunthorpe 	device_del(&stdev->dev);
1029080b47deSLogan Gunthorpe 	cdev_del(&stdev->cdev);
1030080b47deSLogan Gunthorpe 	ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt));
1031080b47deSLogan Gunthorpe 	dev_info(&stdev->dev, "unregistered.\n");
1032080b47deSLogan Gunthorpe 
1033080b47deSLogan Gunthorpe 	stdev_kill(stdev);
1034080b47deSLogan Gunthorpe 	put_device(&stdev->dev);
1035080b47deSLogan Gunthorpe }
1036080b47deSLogan Gunthorpe 
1037080b47deSLogan Gunthorpe #define SWITCHTEC_PCI_DEVICE(device_id) \
1038080b47deSLogan Gunthorpe 	{ \
1039080b47deSLogan Gunthorpe 		.vendor     = MICROSEMI_VENDOR_ID, \
1040080b47deSLogan Gunthorpe 		.device     = device_id, \
1041080b47deSLogan Gunthorpe 		.subvendor  = PCI_ANY_ID, \
1042080b47deSLogan Gunthorpe 		.subdevice  = PCI_ANY_ID, \
1043080b47deSLogan Gunthorpe 		.class      = MICROSEMI_MGMT_CLASSCODE, \
1044080b47deSLogan Gunthorpe 		.class_mask = 0xFFFFFFFF, \
1045080b47deSLogan Gunthorpe 	}, \
1046080b47deSLogan Gunthorpe 	{ \
1047080b47deSLogan Gunthorpe 		.vendor     = MICROSEMI_VENDOR_ID, \
1048080b47deSLogan Gunthorpe 		.device     = device_id, \
1049080b47deSLogan Gunthorpe 		.subvendor  = PCI_ANY_ID, \
1050080b47deSLogan Gunthorpe 		.subdevice  = PCI_ANY_ID, \
1051080b47deSLogan Gunthorpe 		.class      = MICROSEMI_NTB_CLASSCODE, \
1052080b47deSLogan Gunthorpe 		.class_mask = 0xFFFFFFFF, \
1053080b47deSLogan Gunthorpe 	}
1054080b47deSLogan Gunthorpe 
1055080b47deSLogan Gunthorpe static const struct pci_device_id switchtec_pci_tbl[] = {
1056080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8531),  //PFX 24xG3
1057080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8532),  //PFX 32xG3
1058080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8533),  //PFX 48xG3
1059080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8534),  //PFX 64xG3
1060080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8535),  //PFX 80xG3
1061080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8536),  //PFX 96xG3
1062080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8543),  //PSX 48xG3
1063080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8544),  //PSX 64xG3
1064080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8545),  //PSX 80xG3
1065080b47deSLogan Gunthorpe 	SWITCHTEC_PCI_DEVICE(0x8546),  //PSX 96xG3
1066080b47deSLogan Gunthorpe 	{0}
1067080b47deSLogan Gunthorpe };
1068080b47deSLogan Gunthorpe MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl);
1069080b47deSLogan Gunthorpe 
1070080b47deSLogan Gunthorpe static struct pci_driver switchtec_pci_driver = {
1071080b47deSLogan Gunthorpe 	.name		= KBUILD_MODNAME,
1072080b47deSLogan Gunthorpe 	.id_table	= switchtec_pci_tbl,
1073080b47deSLogan Gunthorpe 	.probe		= switchtec_pci_probe,
1074080b47deSLogan Gunthorpe 	.remove		= switchtec_pci_remove,
1075080b47deSLogan Gunthorpe };
1076080b47deSLogan Gunthorpe 
1077080b47deSLogan Gunthorpe static int __init switchtec_init(void)
1078080b47deSLogan Gunthorpe {
1079080b47deSLogan Gunthorpe 	int rc;
1080080b47deSLogan Gunthorpe 
1081080b47deSLogan Gunthorpe 	rc = alloc_chrdev_region(&switchtec_devt, 0, max_devices,
1082080b47deSLogan Gunthorpe 				 "switchtec");
1083080b47deSLogan Gunthorpe 	if (rc)
1084080b47deSLogan Gunthorpe 		return rc;
1085080b47deSLogan Gunthorpe 
1086080b47deSLogan Gunthorpe 	switchtec_class = class_create(THIS_MODULE, "switchtec");
1087080b47deSLogan Gunthorpe 	if (IS_ERR(switchtec_class)) {
1088080b47deSLogan Gunthorpe 		rc = PTR_ERR(switchtec_class);
1089080b47deSLogan Gunthorpe 		goto err_create_class;
1090080b47deSLogan Gunthorpe 	}
1091080b47deSLogan Gunthorpe 
1092080b47deSLogan Gunthorpe 	rc = pci_register_driver(&switchtec_pci_driver);
1093080b47deSLogan Gunthorpe 	if (rc)
1094080b47deSLogan Gunthorpe 		goto err_pci_register;
1095080b47deSLogan Gunthorpe 
1096080b47deSLogan Gunthorpe 	pr_info(KBUILD_MODNAME ": loaded.\n");
1097080b47deSLogan Gunthorpe 
1098080b47deSLogan Gunthorpe 	return 0;
1099080b47deSLogan Gunthorpe 
1100080b47deSLogan Gunthorpe err_pci_register:
1101080b47deSLogan Gunthorpe 	class_destroy(switchtec_class);
1102080b47deSLogan Gunthorpe 
1103080b47deSLogan Gunthorpe err_create_class:
1104080b47deSLogan Gunthorpe 	unregister_chrdev_region(switchtec_devt, max_devices);
1105080b47deSLogan Gunthorpe 
1106080b47deSLogan Gunthorpe 	return rc;
1107080b47deSLogan Gunthorpe }
1108080b47deSLogan Gunthorpe module_init(switchtec_init);
1109080b47deSLogan Gunthorpe 
1110080b47deSLogan Gunthorpe static void __exit switchtec_exit(void)
1111080b47deSLogan Gunthorpe {
1112080b47deSLogan Gunthorpe 	pci_unregister_driver(&switchtec_pci_driver);
1113080b47deSLogan Gunthorpe 	class_destroy(switchtec_class);
1114080b47deSLogan Gunthorpe 	unregister_chrdev_region(switchtec_devt, max_devices);
1115080b47deSLogan Gunthorpe 	ida_destroy(&switchtec_minor_ida);
1116080b47deSLogan Gunthorpe 
1117080b47deSLogan Gunthorpe 	pr_info(KBUILD_MODNAME ": unloaded.\n");
1118080b47deSLogan Gunthorpe }
1119080b47deSLogan Gunthorpe module_exit(switchtec_exit);
1120