1// SPDX-License-Identifier:    GPL-2.0
2/*
3 * Copyright (C) 2018 Marvell International Ltd.
4 */
5
6#include <dm.h>
7#include <errno.h>
8#include <malloc.h>
9#include <misc.h>
10#include <net.h>
11
12#include <linux/bitops.h>
13#include <linux/delay.h>
14#include <linux/list.h>
15
16#include <asm/arch/board.h>
17#include <asm/io.h>
18
19#include "cgx_intf.h"
20#include "cgx.h"
21#include "nix.h"
22
23static u64 cgx_rd_scrx(u8 cgx, u8 lmac, u8 index)
24{
25	u64 addr;
26
27	addr = (index == 1) ? CGX_CMR_SCRATCH1 : CGX_CMR_SCRATCH0;
28	addr += CGX_SHIFT(cgx) + CMR_SHIFT(lmac);
29	return readq(addr);
30}
31
32static void cgx_wr_scrx(u8 cgx, u8 lmac, u8 index, u64 val)
33{
34	u64 addr;
35
36	addr = (index == 1) ? CGX_CMR_SCRATCH1 : CGX_CMR_SCRATCH0;
37	addr += CGX_SHIFT(cgx) + CMR_SHIFT(lmac);
38	writeq(val, addr);
39}
40
41static u64 cgx_rd_scr0(u8 cgx, u8 lmac)
42{
43	return cgx_rd_scrx(cgx, lmac, 0);
44}
45
46static u64 cgx_rd_scr1(u8 cgx, u8 lmac)
47{
48	return cgx_rd_scrx(cgx, lmac, 1);
49}
50
51static void cgx_wr_scr0(u8 cgx, u8 lmac, u64 val)
52{
53	return cgx_wr_scrx(cgx, lmac, 0, val);
54}
55
56static void cgx_wr_scr1(u8 cgx, u8 lmac, u64 val)
57{
58	return cgx_wr_scrx(cgx, lmac, 1, val);
59}
60
61static inline void set_ownership(u8 cgx, u8 lmac, u8 val)
62{
63	union cgx_scratchx1 scr1;
64
65	scr1.u = cgx_rd_scr1(cgx, lmac);
66	scr1.s.own_status = val;
67	cgx_wr_scr1(cgx, lmac, scr1.u);
68}
69
70static int wait_for_ownership(u8 cgx, u8 lmac)
71{
72	union cgx_scratchx1 scr1;
73	union cgx_scratchx0 scr0;
74	u64 cmrx_int;
75	int timeout = 5000;
76
77	do {
78		scr1.u = cgx_rd_scr1(cgx, lmac);
79		scr0.u = cgx_rd_scr0(cgx, lmac);
80		/* clear async events if any */
81		if (scr0.s.evt_sts.evt_type == CGX_EVT_ASYNC &&
82		    scr0.s.evt_sts.ack) {
83			/* clear interrupt */
84			cmrx_int = readq(CGX_CMR_INT +
85					 CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
86			cmrx_int |= 0x2; // Overflw bit
87			writeq(cmrx_int, CGX_CMR_INT +
88					 CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
89
90			/* clear ack */
91			scr0.s.evt_sts.ack = 0;
92			cgx_wr_scr0(cgx, lmac, scr0.u);
93		}
94
95		if (timeout-- < 0) {
96			debug("timeout waiting for ownership\n");
97			return -ETIMEDOUT;
98		}
99		mdelay(1);
100	} while ((scr1.s.own_status == CGX_OWN_FIRMWARE) &&
101		  scr0.s.evt_sts.ack);
102
103	return 0;
104}
105
106int cgx_intf_req(u8 cgx, u8 lmac, union cgx_cmd_s cmd_args, u64 *rsp,
107		 int use_cmd_id_only)
108{
109	union cgx_scratchx1 scr1;
110	union cgx_scratchx0 scr0;
111	u64 cmrx_int;
112	int timeout = 500;
113	int err = 0;
114	u8 cmd = cmd_args.cmd.id;
115
116	if (wait_for_ownership(cgx, lmac)) {
117		err = -ETIMEDOUT;
118		goto error;
119	}
120
121	/* send command */
122	scr1.u = cgx_rd_scr1(cgx, lmac);
123
124	if (use_cmd_id_only) {
125		scr1.s.cmd.id = cmd;
126	} else {
127		cmd_args.own_status = scr1.s.own_status;
128		scr1.s = cmd_args;
129	}
130	cgx_wr_scr1(cgx, lmac, scr1.u);
131
132	set_ownership(cgx, lmac, CGX_OWN_FIRMWARE);
133
134	/* wait for response and ownership */
135	do {
136		scr0.u = cgx_rd_scr0(cgx, lmac);
137		scr1.u = cgx_rd_scr1(cgx, lmac);
138		mdelay(10);
139	} while (timeout-- && (!scr0.s.evt_sts.ack) &&
140		 (scr1.s.own_status == CGX_OWN_FIRMWARE));
141	if (timeout < 0) {
142		debug("%s timeout waiting for ack\n", __func__);
143		err = -ETIMEDOUT;
144		goto error;
145	}
146
147	if (cmd == CGX_CMD_INTF_SHUTDOWN)
148		goto error;
149
150	if (scr0.s.evt_sts.evt_type != CGX_EVT_CMD_RESP) {
151		debug("%s received async event instead of cmd resp event\n",
152		      __func__);
153		err = -1;
154		goto error;
155	}
156	if (scr0.s.evt_sts.id != cmd) {
157		debug("%s received resp for cmd %d expected cmd %d\n",
158		      __func__, scr0.s.evt_sts.id, cmd);
159		err = -1;
160		goto error;
161	}
162	if (scr0.s.evt_sts.stat != CGX_STAT_SUCCESS) {
163		debug("%s cmd%d failed on cgx%u lmac%u with errcode %d\n",
164		      __func__, cmd, cgx, lmac, scr0.s.link_sts.err_type);
165		err = -1;
166	}
167
168error:
169	/* clear interrupt */
170	cmrx_int = readq(CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
171	cmrx_int |= 0x2; // Overflw bit
172	writeq(cmrx_int, CGX_CMR_INT + CGX_SHIFT(cgx) + CMR_SHIFT(lmac));
173
174	/* clear ownership and ack */
175	scr0.s.evt_sts.ack = 0;
176	cgx_wr_scr0(cgx, lmac, scr0.u);
177
178	*rsp = err ? 0 : scr0.u;
179
180	return err;
181}
182
183int cgx_intf_get_mac_addr(u8 cgx, u8 lmac, u8 *mac)
184{
185	union cgx_scratchx0 scr0;
186	int ret;
187	union cgx_cmd_s cmd;
188
189	cmd.cmd.id = CGX_CMD_GET_MAC_ADDR;
190
191	ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
192	if (ret)
193		return -1;
194
195	scr0.u >>= 9;
196	memcpy(mac, &scr0.u, 6);
197
198	return 0;
199}
200
201int cgx_intf_get_ver(u8 cgx, u8 lmac, u8 *ver)
202{
203	union cgx_scratchx0 scr0;
204	int ret;
205	union cgx_cmd_s cmd;
206
207	cmd.cmd.id = CGX_CMD_GET_FW_VER;
208
209	ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
210	if (ret)
211		return -1;
212
213	scr0.u >>= 9;
214	*ver = scr0.u & 0xFFFF;
215
216	return 0;
217}
218
219int cgx_intf_get_link_sts(u8 cgx, u8 lmac, u64 *lnk_sts)
220{
221	union cgx_scratchx0 scr0;
222	int ret;
223	union cgx_cmd_s cmd;
224
225	cmd.cmd.id = CGX_CMD_GET_LINK_STS;
226
227	ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
228	if (ret)
229		return -1;
230
231	scr0.u >>= 9;
232	/* pass the same format as cgx_lnk_sts_s
233	 * err_type:10, speed:4, full_duplex:1, link_up:1
234	 */
235	*lnk_sts = scr0.u & 0xFFFF;
236	return 0;
237}
238
239int cgx_intf_link_up_dwn(u8 cgx, u8 lmac, u8 up_dwn, u64 *lnk_sts)
240{
241	union cgx_scratchx0 scr0;
242	int ret;
243	union cgx_cmd_s cmd;
244
245	cmd.cmd.id = up_dwn ? CGX_CMD_LINK_BRING_UP : CGX_CMD_LINK_BRING_DOWN;
246
247	ret = cgx_intf_req(cgx, lmac, cmd, &scr0.u, 1);
248	if (ret)
249		return -1;
250
251	scr0.u >>= 9;
252	/* pass the same format as cgx_lnk_sts_s
253	 * err_type:10, speed:4, full_duplex:1, link_up:1
254	 */
255	*lnk_sts = scr0.u & 0xFFFF;
256	return 0;
257}
258
259void cgx_intf_shutdown(void)
260{
261	union cgx_scratchx0 scr0;
262	union cgx_cmd_s cmd;
263
264	cmd.cmd.id = CGX_CMD_INTF_SHUTDOWN;
265
266	cgx_intf_req(0, 0, cmd, &scr0.u, 1);
267}
268
269int cgx_intf_prbs(u8 qlm, u8 mode, u32 time, u8 lane)
270{
271	union cgx_scratchx0 scr0;
272	int ret;
273	union cgx_cmd_s cmd;
274
275	cmd.cmd.id = CGX_CMD_PRBS;
276
277	cmd.prbs_args.qlm = qlm;
278	cmd.prbs_args.mode = mode;
279	cmd.prbs_args.time = time;
280	cmd.prbs_args.lane = lane;
281
282	ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0);
283	if (ret)
284		return -1;
285
286	return 0;
287}
288
289enum cgx_mode {
290	MODE_10G_C2C,
291	MODE_10G_C2M,
292	MODE_10G_KR,
293	MODE_25G_C2C,
294	MODE_25G_2_C2C,
295	MODE_50G_C2C,
296	MODE_50G_4_C2C
297};
298
299static char intf_speed_to_str[][8] = {
300	"10M",
301	"100M",
302	"1G",
303	"2.5G",
304	"5G",
305	"10G",
306	"20G",
307	"25G",
308	"40G",
309	"50G",
310	"80G",
311	"100G",
312};
313
314static void mode_to_args(int mode, struct cgx_mode_change_args *args)
315{
316	args->an = 0;
317	args->duplex = 0;
318	args->port = 0;
319
320	switch (mode) {
321	case MODE_10G_C2C:
322		args->speed = CGX_LINK_10G;
323		args->mode = BIT_ULL(CGX_MODE_10G_C2C_BIT);
324		break;
325	case MODE_10G_C2M:
326		args->speed = CGX_LINK_10G;
327		args->mode = BIT_ULL(CGX_MODE_10G_C2M_BIT);
328		break;
329	case MODE_10G_KR:
330		args->speed = CGX_LINK_10G;
331		args->mode = BIT_ULL(CGX_MODE_10G_KR_BIT);
332		args->an = 1;
333		break;
334	case MODE_25G_C2C:
335		args->speed = CGX_LINK_25G;
336		args->mode = BIT_ULL(CGX_MODE_25G_C2C_BIT);
337		break;
338	case MODE_25G_2_C2C:
339		args->speed = CGX_LINK_25G;
340		args->mode = BIT_ULL(CGX_MODE_25G_2_C2C_BIT);
341		break;
342	case MODE_50G_C2C:
343		args->speed = CGX_LINK_50G;
344		args->mode = BIT_ULL(CGX_MODE_50G_C2C_BIT);
345		break;
346	case MODE_50G_4_C2C:
347		args->speed = CGX_LINK_50G;
348		args->mode = BIT_ULL(CGX_MODE_50G_4_C2C_BIT);
349	}
350}
351
352int cgx_intf_set_mode(struct udevice *ethdev, int mode)
353{
354	struct rvu_pf *rvu = dev_get_priv(ethdev);
355	struct nix *nix = rvu->nix;
356	union cgx_scratchx0 scr0;
357	int ret;
358	union cgx_cmd_s cmd;
359
360	cmd.cmd.id = CGX_CMD_MODE_CHANGE;
361
362	mode_to_args(mode, &cmd.mode_change_args);
363
364	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
365			   cmd, &scr0.u, 0);
366	if (ret) {
367		printf("Mode change command failed for %s\n", ethdev->name);
368		return -1;
369	}
370
371	cmd.cmd.id = CGX_CMD_GET_LINK_STS;
372	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
373			   cmd, &scr0.u, 1);
374	if (ret) {
375		printf("Get Link Status failed for %s\n", ethdev->name);
376		return -1;
377	}
378
379	printf("Current Link Status: ");
380	if (scr0.s.link_sts.speed) {
381		printf("%s\n", intf_speed_to_str[scr0.s.link_sts.speed]);
382		switch (scr0.s.link_sts.fec) {
383		case 0:
384			printf("FEC_NONE\n");
385			break;
386		case 1:
387			printf("FEC_BASE_R\n");
388			break;
389		case 2:
390			printf("FEC_RS\n");
391			break;
392		}
393		printf("Auto Negotiation %sabled\n",
394		       scr0.s.link_sts.an ? "En" : "Dis");
395		printf("%s Duplex\n",
396		       scr0.s.link_sts.full_duplex ? "Full" : "Half");
397	} else {
398		printf("Down\n");
399	}
400	return 0;
401}
402
403int cgx_intf_get_mode(struct udevice *ethdev)
404{
405	struct rvu_pf *rvu = dev_get_priv(ethdev);
406	struct nix *nix = rvu->nix;
407	union cgx_scratchx0 scr0;
408	int ret;
409	union cgx_cmd_s cmd;
410
411	cmd.cmd.id = CGX_CMD_GET_LINK_STS;
412	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
413			   cmd, &scr0.u, 1);
414	if (ret) {
415		printf("Get link status failed for %s\n", ethdev->name);
416		return -1;
417	}
418	printf("Current Interface Mode: ");
419	switch (scr0.s.link_sts.mode) {
420	case CGX_MODE_10G_C2C_BIT:
421		printf("10G_C2C\n");
422		break;
423	case CGX_MODE_10G_C2M_BIT:
424		printf("10G_C2M\n");
425		break;
426	case CGX_MODE_10G_KR_BIT:
427		printf("10G_KR\n");
428		break;
429	case CGX_MODE_25G_C2C_BIT:
430		printf("25G_C2C\n");
431		break;
432	case CGX_MODE_25G_2_C2C_BIT:
433		printf("25G_2_C2C\n");
434		break;
435	case CGX_MODE_50G_C2C_BIT:
436		printf("50G_C2C\n");
437		break;
438	case CGX_MODE_50G_4_C2C_BIT:
439		printf("50G_4_C2C\n");
440		break;
441	default:
442		printf("Unknown\n");
443		break;
444	}
445	return 0;
446}
447
448int cgx_intf_get_fec(struct udevice *ethdev)
449{
450	struct rvu_pf *rvu = dev_get_priv(ethdev);
451	struct nix *nix = rvu->nix;
452	union cgx_scratchx0 scr0;
453	int ret;
454	union cgx_cmd_s cmd;
455
456	cmd.cmd.id = CGX_CMD_GET_SUPPORTED_FEC;
457
458	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
459			   cmd, &scr0.u, 1);
460	if (ret) {
461		printf("Get supported FEC failed for %s\n", ethdev->name);
462		return -1;
463	}
464
465	printf("Supported FEC type: ");
466	switch (scr0.s.supported_fec.fec) {
467	case 0:
468		printf("FEC_NONE\n");
469		break;
470	case 1:
471		printf("FEC_BASE_R\n");
472		break;
473	case 2:
474		printf("FEC_RS\n");
475		break;
476	case 3:
477		printf("FEC_BASE_R FEC_RS\n");
478		break;
479	}
480
481	cmd.cmd.id = CGX_CMD_GET_LINK_STS;
482	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
483			   cmd, &scr0.u, 1);
484	if (ret) {
485		printf("Get active fec failed for %s\n", ethdev->name);
486		return -1;
487	}
488	printf("Active FEC type: ");
489	switch (scr0.s.link_sts.fec) {
490	case 0:
491		printf("FEC_NONE\n");
492		break;
493	case 1:
494		printf("FEC_BASE_R\n");
495		break;
496	case 2:
497		printf("FEC_RS\n");
498		break;
499	}
500	return 0;
501}
502
503int cgx_intf_set_fec(struct udevice *ethdev, int type)
504{
505	struct rvu_pf *rvu = dev_get_priv(ethdev);
506	struct nix *nix = rvu->nix;
507	union cgx_scratchx0 scr0;
508	int ret;
509	union cgx_cmd_s cmd;
510
511	cmd.cmd.id = CGX_CMD_SET_FEC;
512	cmd.fec_args.fec = type;
513
514	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
515			   cmd, &scr0.u, 0);
516	if (ret) {
517		printf("Set FEC type %d failed for %s\n", type, ethdev->name);
518		return -1;
519	}
520	return 0;
521}
522
523int cgx_intf_get_phy_mod_type(struct udevice *ethdev)
524{
525	struct rvu_pf *rvu = dev_get_priv(ethdev);
526	struct nix *nix = rvu->nix;
527	union cgx_scratchx0 scr0;
528	int ret;
529	union cgx_cmd_s cmd;
530
531	cmd.cmd.id = CGX_CMD_GET_PHY_MOD_TYPE;
532
533	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
534			   cmd, &scr0.u, 1);
535	if (ret) {
536		printf("Get PHYMOD type failed for %s\n", ethdev->name);
537		return -1;
538	}
539	printf("Current phy mod type %s\n",
540	       scr0.s.phy_mod_type.mod ? "PAM4" : "NRZ");
541	return 0;
542}
543
544int cgx_intf_set_phy_mod_type(struct udevice *ethdev, int type)
545{
546	struct rvu_pf *rvu = dev_get_priv(ethdev);
547	struct nix *nix = rvu->nix;
548	union cgx_scratchx0 scr0;
549	int ret;
550	union cgx_cmd_s cmd;
551
552	cmd.cmd.id = CGX_CMD_SET_PHY_MOD_TYPE;
553	cmd.phy_mod_args.mod = type;
554
555	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
556			   cmd, &scr0.u, 0);
557	if (ret) {
558		printf("Set PHYMOD type %d failed for %s\n", type,
559		       ethdev->name);
560		return -1;
561	}
562
563	return 0;
564}
565
566int cgx_intf_set_an_lbk(struct udevice *ethdev, int enable)
567{
568	struct rvu_pf *rvu = dev_get_priv(ethdev);
569	struct nix *nix = rvu->nix;
570	union cgx_scratchx0 scr0;
571	int ret;
572	union cgx_cmd_s cmd;
573
574	cmd.cmd.id = CGX_CMD_AN_LOOPBACK;
575	cmd.cmd_args.enable = enable;
576
577	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
578			   cmd, &scr0.u, 0);
579	if (ret) {
580		printf("Set AN loopback command failed on %s\n", ethdev->name);
581		return -1;
582	}
583	printf("AN loopback %s for %s\n", enable ? "set" : "clear",
584	       ethdev->name);
585
586	return 0;
587}
588
589int cgx_intf_get_ignore(struct udevice *ethdev, int cgx, int lmac)
590{
591	struct rvu_pf *rvu;
592	struct nix *nix;
593	union cgx_scratchx0 scr0;
594	int ret, cgx_id = cgx, lmac_id = lmac;
595	union cgx_cmd_s cmd;
596
597	if (ethdev) {
598		rvu = dev_get_priv(ethdev);
599		nix = rvu->nix;
600		cgx_id = nix->lmac->cgx->cgx_id;
601		lmac_id = nix->lmac->lmac_id;
602	}
603	cmd.cmd.id = CGX_CMD_GET_PERSIST_IGNORE;
604
605	ret = cgx_intf_req(cgx_id, lmac_id, cmd, &scr0.u, 1);
606	if (ret) {
607		if (ethdev)
608			printf("Get ignore command failed for %s\n",
609			       ethdev->name);
610		else
611			printf("Get ignore command failed for CGX%d LMAC%d\n",
612			       cgx_id, lmac_id);
613		return -1;
614	}
615	if (ethdev)
616		printf("Persist settings %signored for %s\n",
617		       scr0.s.persist.ignore ? "" : "not ", ethdev->name);
618	else
619		printf("Persist settings %signored for CGX%d LMAC%d\n",
620		       scr0.s.persist.ignore ? "" : "not ", cgx_id, lmac_id);
621
622	return 0;
623}
624
625int cgx_intf_set_ignore(struct udevice *ethdev, int cgx, int lmac, int ignore)
626{
627	struct rvu_pf *rvu;
628	struct nix *nix;
629	union cgx_scratchx0 scr0;
630	int ret, cgx_id = cgx, lmac_id = lmac;
631	union cgx_cmd_s cmd;
632
633	if (ethdev) {
634		rvu = dev_get_priv(ethdev);
635		nix = rvu->nix;
636		cgx_id = nix->lmac->cgx->cgx_id;
637		lmac_id = nix->lmac->lmac_id;
638	}
639	cmd.cmd.id = CGX_CMD_SET_PERSIST_IGNORE;
640	cmd.persist_args.ignore = ignore;
641
642	ret = cgx_intf_req(cgx_id, lmac_id, cmd, &scr0.u, 0);
643	if (ret) {
644		if (ethdev)
645			printf("Set ignore command failed for %s\n",
646			       ethdev->name);
647		else
648			printf("Set ignore command failed for CGX%d LMAC%d\n",
649			       cgx_id, lmac_id);
650		return -1;
651	}
652
653	return 0;
654}
655
656int cgx_intf_set_macaddr(struct udevice *ethdev)
657{
658	struct rvu_pf *rvu = dev_get_priv(ethdev);
659	struct nix *nix = rvu->nix;
660	union cgx_scratchx0 scr0;
661	int ret;
662	union cgx_cmd_s cmd;
663	u64 mac, tmp;
664
665	memcpy((void *)&tmp, nix->lmac->mac_addr, 6);
666	mac = swab64(tmp) >> 16;
667	cmd.cmd.id = CGX_CMD_SET_MAC_ADDR;
668	cmd.mac_args.addr = mac;
669	cmd.mac_args.pf_id = rvu->pfid;
670
671	ret = cgx_intf_req(nix->lmac->cgx->cgx_id, nix->lmac->lmac_id,
672			   cmd, &scr0.u, 0);
673	if (ret) {
674		printf("Set user mac addr failed for %s\n", ethdev->name);
675		return -1;
676	}
677
678	return 0;
679}
680
681int cgx_intf_display_eye(u8 qlm, u8 lane)
682{
683	union cgx_scratchx0 scr0;
684	int ret;
685	union cgx_cmd_s cmd;
686
687	cmd.cmd.id = CGX_CMD_DISPLAY_EYE;
688
689	cmd.dsp_eye_args.qlm = qlm;
690	cmd.dsp_eye_args.lane = lane;
691
692	ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0);
693	if (ret)
694		return -1;
695
696	return 0;
697}
698
699int cgx_intf_display_serdes(u8 qlm, u8 lane)
700{
701	union cgx_scratchx0 scr0;
702	int ret;
703	union cgx_cmd_s cmd;
704
705	cmd.cmd.id = CGX_CMD_DISPLAY_SERDES;
706
707	cmd.dsp_eye_args.qlm = qlm;
708	cmd.dsp_eye_args.lane = lane;
709
710	ret = cgx_intf_req(0, 0, cmd, &scr0.u, 0);
711	if (ret)
712		return -1;
713
714	return 0;
715}
716