1 // SPDX-License-Identifier: GPL-2.0
2 /* Marvell OcteonTx2 CGX driver
3  *
4  * Copyright (C) 2018 Marvell International Ltd.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 
11 #include <linux/acpi.h>
12 #include <linux/module.h>
13 #include <linux/interrupt.h>
14 #include <linux/pci.h>
15 #include <linux/netdevice.h>
16 #include <linux/etherdevice.h>
17 #include <linux/phy.h>
18 #include <linux/of.h>
19 #include <linux/of_mdio.h>
20 #include <linux/of_net.h>
21 
22 #include "cgx.h"
23 
24 #define DRV_NAME	"octeontx2-cgx"
25 #define DRV_STRING      "Marvell OcteonTX2 CGX/MAC Driver"
26 
27 /**
28  * struct lmac
29  * @wq_cmd_cmplt:	waitq to keep the process blocked until cmd completion
30  * @cmd_lock:		Lock to serialize the command interface
31  * @resp:		command response
32  * @link_info:		link related information
33  * @event_cb:		callback for linkchange events
34  * @event_cb_lock:	lock for serializing callback with unregister
35  * @cmd_pend:		flag set before new command is started
36  *			flag cleared after command response is received
37  * @cgx:		parent cgx port
38  * @lmac_id:		lmac port id
39  * @name:		lmac port name
40  */
41 struct lmac {
42 	wait_queue_head_t wq_cmd_cmplt;
43 	struct mutex cmd_lock;
44 	u64 resp;
45 	struct cgx_link_user_info link_info;
46 	struct cgx_event_cb event_cb;
47 	spinlock_t event_cb_lock;
48 	bool cmd_pend;
49 	struct cgx *cgx;
50 	u8 lmac_id;
51 	char *name;
52 };
53 
54 struct cgx {
55 	void __iomem		*reg_base;
56 	struct pci_dev		*pdev;
57 	u8			cgx_id;
58 	u8			lmac_count;
59 	struct lmac		*lmac_idmap[MAX_LMAC_PER_CGX];
60 	struct			work_struct cgx_cmd_work;
61 	struct			workqueue_struct *cgx_cmd_workq;
62 	struct list_head	cgx_list;
63 };
64 
65 static LIST_HEAD(cgx_list);
66 
67 /* Convert firmware speed encoding to user format(Mbps) */
68 static u32 cgx_speed_mbps[CGX_LINK_SPEED_MAX];
69 
70 /* Convert firmware lmac type encoding to string */
71 static char *cgx_lmactype_string[LMAC_MODE_MAX];
72 
73 /* CGX PHY management internal APIs */
74 static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en);
75 
76 /* Supported devices */
77 static const struct pci_device_id cgx_id_table[] = {
78 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) },
79 	{ 0, }  /* end of table */
80 };
81 
82 MODULE_DEVICE_TABLE(pci, cgx_id_table);
83 
84 static void cgx_write(struct cgx *cgx, u64 lmac, u64 offset, u64 val)
85 {
86 	writeq(val, cgx->reg_base + (lmac << 18) + offset);
87 }
88 
89 static u64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset)
90 {
91 	return readq(cgx->reg_base + (lmac << 18) + offset);
92 }
93 
94 static inline struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx)
95 {
96 	if (!cgx || lmac_id >= MAX_LMAC_PER_CGX)
97 		return NULL;
98 
99 	return cgx->lmac_idmap[lmac_id];
100 }
101 
102 int cgx_get_cgxcnt_max(void)
103 {
104 	struct cgx *cgx_dev;
105 	int idmax = -ENODEV;
106 
107 	list_for_each_entry(cgx_dev, &cgx_list, cgx_list)
108 		if (cgx_dev->cgx_id > idmax)
109 			idmax = cgx_dev->cgx_id;
110 
111 	if (idmax < 0)
112 		return 0;
113 
114 	return idmax + 1;
115 }
116 EXPORT_SYMBOL(cgx_get_cgxcnt_max);
117 
118 int cgx_get_lmac_cnt(void *cgxd)
119 {
120 	struct cgx *cgx = cgxd;
121 
122 	if (!cgx)
123 		return -ENODEV;
124 
125 	return cgx->lmac_count;
126 }
127 EXPORT_SYMBOL(cgx_get_lmac_cnt);
128 
129 void *cgx_get_pdata(int cgx_id)
130 {
131 	struct cgx *cgx_dev;
132 
133 	list_for_each_entry(cgx_dev, &cgx_list, cgx_list) {
134 		if (cgx_dev->cgx_id == cgx_id)
135 			return cgx_dev;
136 	}
137 	return NULL;
138 }
139 EXPORT_SYMBOL(cgx_get_pdata);
140 
141 int cgx_get_cgxid(void *cgxd)
142 {
143 	struct cgx *cgx = cgxd;
144 
145 	if (!cgx)
146 		return -EINVAL;
147 
148 	return cgx->cgx_id;
149 }
150 
151 /* Ensure the required lock for event queue(where asynchronous events are
152  * posted) is acquired before calling this API. Else an asynchronous event(with
153  * latest link status) can reach the destination before this function returns
154  * and could make the link status appear wrong.
155  */
156 int cgx_get_link_info(void *cgxd, int lmac_id,
157 		      struct cgx_link_user_info *linfo)
158 {
159 	struct lmac *lmac = lmac_pdata(lmac_id, cgxd);
160 
161 	if (!lmac)
162 		return -ENODEV;
163 
164 	*linfo = lmac->link_info;
165 	return 0;
166 }
167 EXPORT_SYMBOL(cgx_get_link_info);
168 
169 static u64 mac2u64 (u8 *mac_addr)
170 {
171 	u64 mac = 0;
172 	int index;
173 
174 	for (index = ETH_ALEN - 1; index >= 0; index--)
175 		mac |= ((u64)*mac_addr++) << (8 * index);
176 	return mac;
177 }
178 
179 int cgx_lmac_addr_set(u8 cgx_id, u8 lmac_id, u8 *mac_addr)
180 {
181 	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
182 	u64 cfg;
183 
184 	/* copy 6bytes from macaddr */
185 	/* memcpy(&cfg, mac_addr, 6); */
186 
187 	cfg = mac2u64 (mac_addr);
188 
189 	cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (lmac_id * 0x8)),
190 		  cfg | CGX_DMAC_CAM_ADDR_ENABLE | ((u64)lmac_id << 49));
191 
192 	cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
193 	cfg |= CGX_DMAC_CTL0_CAM_ENABLE;
194 	cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
195 
196 	return 0;
197 }
198 EXPORT_SYMBOL(cgx_lmac_addr_set);
199 
200 u64 cgx_lmac_addr_get(u8 cgx_id, u8 lmac_id)
201 {
202 	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
203 	u64 cfg;
204 
205 	cfg = cgx_read(cgx_dev, 0, CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8);
206 	return cfg & CGX_RX_DMAC_ADR_MASK;
207 }
208 EXPORT_SYMBOL(cgx_lmac_addr_get);
209 
210 int cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind)
211 {
212 	struct cgx *cgx = cgxd;
213 
214 	if (!cgx || lmac_id >= cgx->lmac_count)
215 		return -ENODEV;
216 
217 	cgx_write(cgx, lmac_id, CGXX_CMRX_RX_ID_MAP, (pkind & 0x3F));
218 	return 0;
219 }
220 EXPORT_SYMBOL(cgx_set_pkind);
221 
222 static inline u8 cgx_get_lmac_type(struct cgx *cgx, int lmac_id)
223 {
224 	u64 cfg;
225 
226 	cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG);
227 	return (cfg >> CGX_LMAC_TYPE_SHIFT) & CGX_LMAC_TYPE_MASK;
228 }
229 
230 /* Configure CGX LMAC in internal loopback mode */
231 int cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable)
232 {
233 	struct cgx *cgx = cgxd;
234 	u8 lmac_type;
235 	u64 cfg;
236 
237 	if (!cgx || lmac_id >= cgx->lmac_count)
238 		return -ENODEV;
239 
240 	lmac_type = cgx_get_lmac_type(cgx, lmac_id);
241 	if (lmac_type == LMAC_MODE_SGMII || lmac_type == LMAC_MODE_QSGMII) {
242 		cfg = cgx_read(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL);
243 		if (enable)
244 			cfg |= CGXX_GMP_PCS_MRX_CTL_LBK;
245 		else
246 			cfg &= ~CGXX_GMP_PCS_MRX_CTL_LBK;
247 		cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL, cfg);
248 	} else {
249 		cfg = cgx_read(cgx, lmac_id, CGXX_SPUX_CONTROL1);
250 		if (enable)
251 			cfg |= CGXX_SPUX_CONTROL1_LBK;
252 		else
253 			cfg &= ~CGXX_SPUX_CONTROL1_LBK;
254 		cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1, cfg);
255 	}
256 	return 0;
257 }
258 EXPORT_SYMBOL(cgx_lmac_internal_loopback);
259 
260 void cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable)
261 {
262 	struct cgx *cgx = cgx_get_pdata(cgx_id);
263 	u64 cfg = 0;
264 
265 	if (!cgx)
266 		return;
267 
268 	if (enable) {
269 		/* Enable promiscuous mode on LMAC */
270 		cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
271 		cfg &= ~(CGX_DMAC_CAM_ACCEPT | CGX_DMAC_MCAST_MODE);
272 		cfg |= CGX_DMAC_BCAST_MODE;
273 		cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
274 
275 		cfg = cgx_read(cgx, 0,
276 			       (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8));
277 		cfg &= ~CGX_DMAC_CAM_ADDR_ENABLE;
278 		cgx_write(cgx, 0,
279 			  (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8), cfg);
280 	} else {
281 		/* Disable promiscuous mode */
282 		cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
283 		cfg |= CGX_DMAC_CAM_ACCEPT | CGX_DMAC_MCAST_MODE;
284 		cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
285 		cfg = cgx_read(cgx, 0,
286 			       (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8));
287 		cfg |= CGX_DMAC_CAM_ADDR_ENABLE;
288 		cgx_write(cgx, 0,
289 			  (CGXX_CMRX_RX_DMAC_CAM0 + lmac_id * 0x8), cfg);
290 	}
291 }
292 EXPORT_SYMBOL(cgx_lmac_promisc_config);
293 
294 /* Enable or disable forwarding received pause frames to Tx block */
295 void cgx_lmac_enadis_rx_pause_fwding(void *cgxd, int lmac_id, bool enable)
296 {
297 	struct cgx *cgx = cgxd;
298 	u64 cfg;
299 
300 	if (!cgx)
301 		return;
302 
303 	if (enable) {
304 		cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
305 		cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK;
306 		cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);
307 
308 		cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
309 		cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK;
310 		cgx_write(cgx, lmac_id,	CGXX_SMUX_RX_FRM_CTL, cfg);
311 	} else {
312 		cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
313 		cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK;
314 		cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);
315 
316 		cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
317 		cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK;
318 		cgx_write(cgx, lmac_id,	CGXX_SMUX_RX_FRM_CTL, cfg);
319 	}
320 }
321 EXPORT_SYMBOL(cgx_lmac_enadis_rx_pause_fwding);
322 
323 int cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat)
324 {
325 	struct cgx *cgx = cgxd;
326 
327 	if (!cgx || lmac_id >= cgx->lmac_count)
328 		return -ENODEV;
329 	*rx_stat =  cgx_read(cgx, lmac_id, CGXX_CMRX_RX_STAT0 + (idx * 8));
330 	return 0;
331 }
332 EXPORT_SYMBOL(cgx_get_rx_stats);
333 
334 int cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat)
335 {
336 	struct cgx *cgx = cgxd;
337 
338 	if (!cgx || lmac_id >= cgx->lmac_count)
339 		return -ENODEV;
340 	*tx_stat = cgx_read(cgx, lmac_id, CGXX_CMRX_TX_STAT0 + (idx * 8));
341 	return 0;
342 }
343 EXPORT_SYMBOL(cgx_get_tx_stats);
344 
345 int cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable)
346 {
347 	struct cgx *cgx = cgxd;
348 	u64 cfg;
349 
350 	if (!cgx || lmac_id >= cgx->lmac_count)
351 		return -ENODEV;
352 
353 	cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG);
354 	if (enable)
355 		cfg |= CMR_EN | DATA_PKT_RX_EN | DATA_PKT_TX_EN;
356 	else
357 		cfg &= ~(CMR_EN | DATA_PKT_RX_EN | DATA_PKT_TX_EN);
358 	cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg);
359 	return 0;
360 }
361 EXPORT_SYMBOL(cgx_lmac_rx_tx_enable);
362 
363 int cgx_lmac_tx_enable(void *cgxd, int lmac_id, bool enable)
364 {
365 	struct cgx *cgx = cgxd;
366 	u64 cfg, last;
367 
368 	if (!cgx || lmac_id >= cgx->lmac_count)
369 		return -ENODEV;
370 
371 	cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG);
372 	last = cfg;
373 	if (enable)
374 		cfg |= DATA_PKT_TX_EN;
375 	else
376 		cfg &= ~DATA_PKT_TX_EN;
377 
378 	if (cfg != last)
379 		cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg);
380 	return !!(last & DATA_PKT_TX_EN);
381 }
382 EXPORT_SYMBOL(cgx_lmac_tx_enable);
383 
384 /* CGX Firmware interface low level support */
385 static int cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac)
386 {
387 	struct cgx *cgx = lmac->cgx;
388 	struct device *dev;
389 	int err = 0;
390 	u64 cmd;
391 
392 	/* Ensure no other command is in progress */
393 	err = mutex_lock_interruptible(&lmac->cmd_lock);
394 	if (err)
395 		return err;
396 
397 	/* Ensure command register is free */
398 	cmd = cgx_read(cgx, lmac->lmac_id,  CGX_COMMAND_REG);
399 	if (FIELD_GET(CMDREG_OWN, cmd) != CGX_CMD_OWN_NS) {
400 		err = -EBUSY;
401 		goto unlock;
402 	}
403 
404 	/* Update ownership in command request */
405 	req = FIELD_SET(CMDREG_OWN, CGX_CMD_OWN_FIRMWARE, req);
406 
407 	/* Mark this lmac as pending, before we start */
408 	lmac->cmd_pend = true;
409 
410 	/* Start command in hardware */
411 	cgx_write(cgx, lmac->lmac_id, CGX_COMMAND_REG, req);
412 
413 	/* Ensure command is completed without errors */
414 	if (!wait_event_timeout(lmac->wq_cmd_cmplt, !lmac->cmd_pend,
415 				msecs_to_jiffies(CGX_CMD_TIMEOUT))) {
416 		dev = &cgx->pdev->dev;
417 		dev_err(dev, "cgx port %d:%d cmd timeout\n",
418 			cgx->cgx_id, lmac->lmac_id);
419 		err = -EIO;
420 		goto unlock;
421 	}
422 
423 	/* we have a valid command response */
424 	smp_rmb(); /* Ensure the latest updates are visible */
425 	*resp = lmac->resp;
426 
427 unlock:
428 	mutex_unlock(&lmac->cmd_lock);
429 
430 	return err;
431 }
432 
433 static inline int cgx_fwi_cmd_generic(u64 req, u64 *resp,
434 				      struct cgx *cgx, int lmac_id)
435 {
436 	struct lmac *lmac;
437 	int err;
438 
439 	lmac = lmac_pdata(lmac_id, cgx);
440 	if (!lmac)
441 		return -ENODEV;
442 
443 	err = cgx_fwi_cmd_send(req, resp, lmac);
444 
445 	/* Check for valid response */
446 	if (!err) {
447 		if (FIELD_GET(EVTREG_STAT, *resp) == CGX_STAT_FAIL)
448 			return -EIO;
449 		else
450 			return 0;
451 	}
452 
453 	return err;
454 }
455 
456 static inline void cgx_link_usertable_init(void)
457 {
458 	cgx_speed_mbps[CGX_LINK_NONE] = 0;
459 	cgx_speed_mbps[CGX_LINK_10M] = 10;
460 	cgx_speed_mbps[CGX_LINK_100M] = 100;
461 	cgx_speed_mbps[CGX_LINK_1G] = 1000;
462 	cgx_speed_mbps[CGX_LINK_2HG] = 2500;
463 	cgx_speed_mbps[CGX_LINK_5G] = 5000;
464 	cgx_speed_mbps[CGX_LINK_10G] = 10000;
465 	cgx_speed_mbps[CGX_LINK_20G] = 20000;
466 	cgx_speed_mbps[CGX_LINK_25G] = 25000;
467 	cgx_speed_mbps[CGX_LINK_40G] = 40000;
468 	cgx_speed_mbps[CGX_LINK_50G] = 50000;
469 	cgx_speed_mbps[CGX_LINK_100G] = 100000;
470 
471 	cgx_lmactype_string[LMAC_MODE_SGMII] = "SGMII";
472 	cgx_lmactype_string[LMAC_MODE_XAUI] = "XAUI";
473 	cgx_lmactype_string[LMAC_MODE_RXAUI] = "RXAUI";
474 	cgx_lmactype_string[LMAC_MODE_10G_R] = "10G_R";
475 	cgx_lmactype_string[LMAC_MODE_40G_R] = "40G_R";
476 	cgx_lmactype_string[LMAC_MODE_QSGMII] = "QSGMII";
477 	cgx_lmactype_string[LMAC_MODE_25G_R] = "25G_R";
478 	cgx_lmactype_string[LMAC_MODE_50G_R] = "50G_R";
479 	cgx_lmactype_string[LMAC_MODE_100G_R] = "100G_R";
480 	cgx_lmactype_string[LMAC_MODE_USXGMII] = "USXGMII";
481 }
482 
483 static inline void link_status_user_format(u64 lstat,
484 					   struct cgx_link_user_info *linfo,
485 					   struct cgx *cgx, u8 lmac_id)
486 {
487 	char *lmac_string;
488 
489 	linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat);
490 	linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat);
491 	linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)];
492 	linfo->lmac_type_id = cgx_get_lmac_type(cgx, lmac_id);
493 	lmac_string = cgx_lmactype_string[linfo->lmac_type_id];
494 	strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1);
495 }
496 
497 /* Hardware event handlers */
498 static inline void cgx_link_change_handler(u64 lstat,
499 					   struct lmac *lmac)
500 {
501 	struct cgx_link_user_info *linfo;
502 	struct cgx *cgx = lmac->cgx;
503 	struct cgx_link_event event;
504 	struct device *dev;
505 	int err_type;
506 
507 	dev = &cgx->pdev->dev;
508 
509 	link_status_user_format(lstat, &event.link_uinfo, cgx, lmac->lmac_id);
510 	err_type = FIELD_GET(RESP_LINKSTAT_ERRTYPE, lstat);
511 
512 	event.cgx_id = cgx->cgx_id;
513 	event.lmac_id = lmac->lmac_id;
514 
515 	/* update the local copy of link status */
516 	lmac->link_info = event.link_uinfo;
517 	linfo = &lmac->link_info;
518 
519 	/* Ensure callback doesn't get unregistered until we finish it */
520 	spin_lock(&lmac->event_cb_lock);
521 
522 	if (!lmac->event_cb.notify_link_chg) {
523 		dev_dbg(dev, "cgx port %d:%d Link change handler null",
524 			cgx->cgx_id, lmac->lmac_id);
525 		if (err_type != CGX_ERR_NONE) {
526 			dev_err(dev, "cgx port %d:%d Link error %d\n",
527 				cgx->cgx_id, lmac->lmac_id, err_type);
528 		}
529 		dev_info(dev, "cgx port %d:%d Link is %s %d Mbps\n",
530 			 cgx->cgx_id, lmac->lmac_id,
531 			 linfo->link_up ? "UP" : "DOWN", linfo->speed);
532 		goto err;
533 	}
534 
535 	if (lmac->event_cb.notify_link_chg(&event, lmac->event_cb.data))
536 		dev_err(dev, "event notification failure\n");
537 err:
538 	spin_unlock(&lmac->event_cb_lock);
539 }
540 
541 static inline bool cgx_cmdresp_is_linkevent(u64 event)
542 {
543 	u8 id;
544 
545 	id = FIELD_GET(EVTREG_ID, event);
546 	if (id == CGX_CMD_LINK_BRING_UP ||
547 	    id == CGX_CMD_LINK_BRING_DOWN)
548 		return true;
549 	else
550 		return false;
551 }
552 
553 static inline bool cgx_event_is_linkevent(u64 event)
554 {
555 	if (FIELD_GET(EVTREG_ID, event) == CGX_EVT_LINK_CHANGE)
556 		return true;
557 	else
558 		return false;
559 }
560 
561 static inline int cgx_fwi_get_mkex_prfl_sz(u64 *prfl_sz,
562 					   struct cgx *cgx)
563 {
564 	u64 req = 0;
565 	u64 resp;
566 	int err;
567 
568 	req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_SIZE, req);
569 	err = cgx_fwi_cmd_generic(req, &resp, cgx, 0);
570 	if (!err)
571 		*prfl_sz = FIELD_GET(RESP_MKEX_PRFL_SIZE, resp);
572 
573 	return err;
574 }
575 
576 static inline int cgx_fwi_get_mkex_prfl_addr(u64 *prfl_addr,
577 					     struct cgx *cgx)
578 {
579 	u64 req = 0;
580 	u64 resp;
581 	int err;
582 
583 	req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_MKEX_PRFL_ADDR, req);
584 	err = cgx_fwi_cmd_generic(req, &resp, cgx, 0);
585 	if (!err)
586 		*prfl_addr = FIELD_GET(RESP_MKEX_PRFL_ADDR, resp);
587 
588 	return err;
589 }
590 
591 int cgx_get_mkex_prfl_info(u64 *addr, u64 *size)
592 {
593 	struct cgx *cgx_dev;
594 	int err;
595 
596 	if (!addr || !size)
597 		return -EINVAL;
598 
599 	cgx_dev = list_first_entry(&cgx_list, struct cgx, cgx_list);
600 	if (!cgx_dev)
601 		return -ENXIO;
602 
603 	err = cgx_fwi_get_mkex_prfl_sz(size, cgx_dev);
604 	if (err)
605 		return -EIO;
606 
607 	err = cgx_fwi_get_mkex_prfl_addr(addr, cgx_dev);
608 	if (err)
609 		return -EIO;
610 
611 	return 0;
612 }
613 EXPORT_SYMBOL(cgx_get_mkex_prfl_info);
614 
615 static irqreturn_t cgx_fwi_event_handler(int irq, void *data)
616 {
617 	struct lmac *lmac = data;
618 	struct cgx *cgx;
619 	u64 event;
620 
621 	cgx = lmac->cgx;
622 
623 	event = cgx_read(cgx, lmac->lmac_id, CGX_EVENT_REG);
624 
625 	if (!FIELD_GET(EVTREG_ACK, event))
626 		return IRQ_NONE;
627 
628 	switch (FIELD_GET(EVTREG_EVT_TYPE, event)) {
629 	case CGX_EVT_CMD_RESP:
630 		/* Copy the response. Since only one command is active at a
631 		 * time, there is no way a response can get overwritten
632 		 */
633 		lmac->resp = event;
634 		/* Ensure response is updated before thread context starts */
635 		smp_wmb();
636 
637 		/* There wont be separate events for link change initiated from
638 		 * software; Hence report the command responses as events
639 		 */
640 		if (cgx_cmdresp_is_linkevent(event))
641 			cgx_link_change_handler(event, lmac);
642 
643 		/* Release thread waiting for completion  */
644 		lmac->cmd_pend = false;
645 		wake_up_interruptible(&lmac->wq_cmd_cmplt);
646 		break;
647 	case CGX_EVT_ASYNC:
648 		if (cgx_event_is_linkevent(event))
649 			cgx_link_change_handler(event, lmac);
650 		break;
651 	}
652 
653 	/* Any new event or command response will be posted by firmware
654 	 * only after the current status is acked.
655 	 * Ack the interrupt register as well.
656 	 */
657 	cgx_write(lmac->cgx, lmac->lmac_id, CGX_EVENT_REG, 0);
658 	cgx_write(lmac->cgx, lmac->lmac_id, CGXX_CMRX_INT, FW_CGX_INT);
659 
660 	return IRQ_HANDLED;
661 }
662 
663 /* APIs for PHY management using CGX firmware interface */
664 
665 /* callback registration for hardware events like link change */
666 int cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id)
667 {
668 	struct cgx *cgx = cgxd;
669 	struct lmac *lmac;
670 
671 	lmac = lmac_pdata(lmac_id, cgx);
672 	if (!lmac)
673 		return -ENODEV;
674 
675 	lmac->event_cb = *cb;
676 
677 	return 0;
678 }
679 EXPORT_SYMBOL(cgx_lmac_evh_register);
680 
681 int cgx_lmac_evh_unregister(void *cgxd, int lmac_id)
682 {
683 	struct lmac *lmac;
684 	unsigned long flags;
685 	struct cgx *cgx = cgxd;
686 
687 	lmac = lmac_pdata(lmac_id, cgx);
688 	if (!lmac)
689 		return -ENODEV;
690 
691 	spin_lock_irqsave(&lmac->event_cb_lock, flags);
692 	lmac->event_cb.notify_link_chg = NULL;
693 	lmac->event_cb.data = NULL;
694 	spin_unlock_irqrestore(&lmac->event_cb_lock, flags);
695 
696 	return 0;
697 }
698 EXPORT_SYMBOL(cgx_lmac_evh_unregister);
699 
700 static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable)
701 {
702 	u64 req = 0;
703 	u64 resp;
704 
705 	if (enable)
706 		req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_UP, req);
707 	else
708 		req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req);
709 
710 	return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id);
711 }
712 
713 static inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx)
714 {
715 	u64 req = 0;
716 
717 	req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FW_VER, req);
718 	return cgx_fwi_cmd_generic(req, resp, cgx, 0);
719 }
720 
721 static int cgx_lmac_verify_fwi_version(struct cgx *cgx)
722 {
723 	struct device *dev = &cgx->pdev->dev;
724 	int major_ver, minor_ver;
725 	u64 resp;
726 	int err;
727 
728 	if (!cgx->lmac_count)
729 		return 0;
730 
731 	err = cgx_fwi_read_version(&resp, cgx);
732 	if (err)
733 		return err;
734 
735 	major_ver = FIELD_GET(RESP_MAJOR_VER, resp);
736 	minor_ver = FIELD_GET(RESP_MINOR_VER, resp);
737 	dev_dbg(dev, "Firmware command interface version = %d.%d\n",
738 		major_ver, minor_ver);
739 	if (major_ver != CGX_FIRMWARE_MAJOR_VER ||
740 	    minor_ver != CGX_FIRMWARE_MINOR_VER)
741 		return -EIO;
742 	else
743 		return 0;
744 }
745 
746 static void cgx_lmac_linkup_work(struct work_struct *work)
747 {
748 	struct cgx *cgx = container_of(work, struct cgx, cgx_cmd_work);
749 	struct device *dev = &cgx->pdev->dev;
750 	int i, err;
751 
752 	/* Do Link up for all the lmacs */
753 	for (i = 0; i < cgx->lmac_count; i++) {
754 		err = cgx_fwi_link_change(cgx, i, true);
755 		if (err)
756 			dev_info(dev, "cgx port %d:%d Link up command failed\n",
757 				 cgx->cgx_id, i);
758 	}
759 }
760 
761 int cgx_lmac_linkup_start(void *cgxd)
762 {
763 	struct cgx *cgx = cgxd;
764 
765 	if (!cgx)
766 		return -ENODEV;
767 
768 	queue_work(cgx->cgx_cmd_workq, &cgx->cgx_cmd_work);
769 
770 	return 0;
771 }
772 EXPORT_SYMBOL(cgx_lmac_linkup_start);
773 
774 static int cgx_lmac_init(struct cgx *cgx)
775 {
776 	struct lmac *lmac;
777 	int i, err;
778 
779 	cgx->lmac_count = cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0x7;
780 	if (cgx->lmac_count > MAX_LMAC_PER_CGX)
781 		cgx->lmac_count = MAX_LMAC_PER_CGX;
782 
783 	for (i = 0; i < cgx->lmac_count; i++) {
784 		lmac = kcalloc(1, sizeof(struct lmac), GFP_KERNEL);
785 		if (!lmac)
786 			return -ENOMEM;
787 		lmac->name = kcalloc(1, sizeof("cgx_fwi_xxx_yyy"), GFP_KERNEL);
788 		if (!lmac->name)
789 			return -ENOMEM;
790 		sprintf(lmac->name, "cgx_fwi_%d_%d", cgx->cgx_id, i);
791 		lmac->lmac_id = i;
792 		lmac->cgx = cgx;
793 		init_waitqueue_head(&lmac->wq_cmd_cmplt);
794 		mutex_init(&lmac->cmd_lock);
795 		spin_lock_init(&lmac->event_cb_lock);
796 		err = request_irq(pci_irq_vector(cgx->pdev,
797 						 CGX_LMAC_FWI + i * 9),
798 				   cgx_fwi_event_handler, 0, lmac->name, lmac);
799 		if (err)
800 			return err;
801 
802 		/* Enable interrupt */
803 		cgx_write(cgx, lmac->lmac_id, CGXX_CMRX_INT_ENA_W1S,
804 			  FW_CGX_INT);
805 
806 		/* Add reference */
807 		cgx->lmac_idmap[i] = lmac;
808 	}
809 
810 	return cgx_lmac_verify_fwi_version(cgx);
811 }
812 
813 static int cgx_lmac_exit(struct cgx *cgx)
814 {
815 	struct lmac *lmac;
816 	int i;
817 
818 	if (cgx->cgx_cmd_workq) {
819 		flush_workqueue(cgx->cgx_cmd_workq);
820 		destroy_workqueue(cgx->cgx_cmd_workq);
821 		cgx->cgx_cmd_workq = NULL;
822 	}
823 
824 	/* Free all lmac related resources */
825 	for (i = 0; i < cgx->lmac_count; i++) {
826 		lmac = cgx->lmac_idmap[i];
827 		if (!lmac)
828 			continue;
829 		free_irq(pci_irq_vector(cgx->pdev, CGX_LMAC_FWI + i * 9), lmac);
830 		kfree(lmac->name);
831 		kfree(lmac);
832 	}
833 
834 	return 0;
835 }
836 
837 static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id)
838 {
839 	struct device *dev = &pdev->dev;
840 	struct cgx *cgx;
841 	int err, nvec;
842 
843 	cgx = devm_kzalloc(dev, sizeof(*cgx), GFP_KERNEL);
844 	if (!cgx)
845 		return -ENOMEM;
846 	cgx->pdev = pdev;
847 
848 	pci_set_drvdata(pdev, cgx);
849 
850 	err = pci_enable_device(pdev);
851 	if (err) {
852 		dev_err(dev, "Failed to enable PCI device\n");
853 		pci_set_drvdata(pdev, NULL);
854 		return err;
855 	}
856 
857 	err = pci_request_regions(pdev, DRV_NAME);
858 	if (err) {
859 		dev_err(dev, "PCI request regions failed 0x%x\n", err);
860 		goto err_disable_device;
861 	}
862 
863 	/* MAP configuration registers */
864 	cgx->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
865 	if (!cgx->reg_base) {
866 		dev_err(dev, "CGX: Cannot map CSR memory space, aborting\n");
867 		err = -ENOMEM;
868 		goto err_release_regions;
869 	}
870 
871 	nvec = CGX_NVEC;
872 	err = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX);
873 	if (err < 0 || err != nvec) {
874 		dev_err(dev, "Request for %d msix vectors failed, err %d\n",
875 			nvec, err);
876 		goto err_release_regions;
877 	}
878 
879 	cgx->cgx_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24)
880 		& CGX_ID_MASK;
881 
882 	/* init wq for processing linkup requests */
883 	INIT_WORK(&cgx->cgx_cmd_work, cgx_lmac_linkup_work);
884 	cgx->cgx_cmd_workq = alloc_workqueue("cgx_cmd_workq", 0, 0);
885 	if (!cgx->cgx_cmd_workq) {
886 		dev_err(dev, "alloc workqueue failed for cgx cmd");
887 		err = -ENOMEM;
888 		goto err_free_irq_vectors;
889 	}
890 
891 	list_add(&cgx->cgx_list, &cgx_list);
892 
893 	cgx_link_usertable_init();
894 
895 	err = cgx_lmac_init(cgx);
896 	if (err)
897 		goto err_release_lmac;
898 
899 	return 0;
900 
901 err_release_lmac:
902 	cgx_lmac_exit(cgx);
903 	list_del(&cgx->cgx_list);
904 err_free_irq_vectors:
905 	pci_free_irq_vectors(pdev);
906 err_release_regions:
907 	pci_release_regions(pdev);
908 err_disable_device:
909 	pci_disable_device(pdev);
910 	pci_set_drvdata(pdev, NULL);
911 	return err;
912 }
913 
914 static void cgx_remove(struct pci_dev *pdev)
915 {
916 	struct cgx *cgx = pci_get_drvdata(pdev);
917 
918 	cgx_lmac_exit(cgx);
919 	list_del(&cgx->cgx_list);
920 	pci_free_irq_vectors(pdev);
921 	pci_release_regions(pdev);
922 	pci_disable_device(pdev);
923 	pci_set_drvdata(pdev, NULL);
924 }
925 
926 struct pci_driver cgx_driver = {
927 	.name = DRV_NAME,
928 	.id_table = cgx_id_table,
929 	.probe = cgx_probe,
930 	.remove = cgx_remove,
931 };
932