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#include <pci_ids.h>
12#include <linux/list.h>
13#include <asm/arch/board.h>
14#include <asm/arch/csrs/csrs-cgx.h>
15#include <asm/io.h>
16
17#include "cgx.h"
18
19char lmac_type_to_str[][8] = {
20	"SGMII",
21	"XAUI",
22	"RXAUI",
23	"10G_R",
24	"40G_R",
25	"RGMII",
26	"QSGMII",
27	"25G_R",
28	"50G_R",
29	"100G_R",
30	"USXGMII",
31};
32
33char lmac_speed_to_str[][8] = {
34	"0",
35	"10M",
36	"100M",
37	"1G",
38	"2.5G",
39	"5G",
40	"10G",
41	"20G",
42	"25G",
43	"40G",
44	"50G",
45	"80G",
46	"100G",
47};
48
49/**
50 * Given an LMAC/PF instance number, return the lmac
51 * Per design, each PF has only one LMAC mapped.
52 *
53 * @param instance	instance to find
54 *
55 * Return:	pointer to lmac data structure or NULL if not found
56 */
57struct lmac *nix_get_cgx_lmac(int lmac_instance)
58{
59	struct cgx *cgx;
60	struct udevice *dev;
61	int i, idx, err;
62
63	for (i = 0; i < CGX_PER_NODE; i++) {
64		err = dm_pci_find_device(PCI_VENDOR_ID_CAVIUM,
65					 PCI_DEVICE_ID_OCTEONTX2_CGX, i,
66					 &dev);
67		if (err)
68			continue;
69
70		cgx = dev_get_priv(dev);
71		debug("%s udev %p cgx %p instance %d\n", __func__, dev, cgx,
72		      lmac_instance);
73		for (idx = 0; idx < cgx->lmac_count; idx++) {
74			if (cgx->lmac[idx]->instance == lmac_instance)
75				return cgx->lmac[idx];
76		}
77	}
78	return NULL;
79}
80
81void cgx_lmac_mac_filter_clear(struct lmac *lmac)
82{
83	union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0;
84	union cgxx_cmr_rx_dmacx_cam0 dmac_cam0;
85	void *reg_addr;
86
87	dmac_cam0.u = 0x0;
88	reg_addr = lmac->cgx->reg_base +
89			CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8);
90	writeq(dmac_cam0.u, reg_addr);
91	debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u);
92
93	dmac_ctl0.u = 0x0;
94	dmac_ctl0.s.bcst_accept = 1;
95	dmac_ctl0.s.mcst_mode = 1;
96	dmac_ctl0.s.cam_accept = 0;
97	reg_addr = lmac->cgx->reg_base +
98			CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id);
99	writeq(dmac_ctl0.u, reg_addr);
100	debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u);
101}
102
103void cgx_lmac_mac_filter_setup(struct lmac *lmac)
104{
105	union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0;
106	union cgxx_cmr_rx_dmacx_cam0 dmac_cam0;
107	u64 mac, tmp;
108	void *reg_addr;
109
110	memcpy((void *)&tmp, lmac->mac_addr, 6);
111	debug("%s: tmp %llx\n", __func__, tmp);
112	debug("%s: swab tmp %llx\n", __func__, swab64(tmp));
113	mac = swab64(tmp) >> 16;
114	debug("%s: mac %llx\n", __func__, mac);
115	dmac_cam0.u = 0x0;
116	dmac_cam0.s.id = lmac->lmac_id;
117	dmac_cam0.s.adr = mac;
118	dmac_cam0.s.en = 1;
119	reg_addr = lmac->cgx->reg_base +
120			CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8);
121	writeq(dmac_cam0.u, reg_addr);
122	debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u);
123	dmac_ctl0.u = 0x0;
124	dmac_ctl0.s.bcst_accept = 1;
125	dmac_ctl0.s.mcst_mode = 0;
126	dmac_ctl0.s.cam_accept = 1;
127	reg_addr = lmac->cgx->reg_base +
128			CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id);
129	writeq(dmac_ctl0.u, reg_addr);
130	debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u);
131}
132
133int cgx_lmac_set_pkind(struct lmac *lmac, u8 lmac_id, int pkind)
134{
135	cgx_write(lmac->cgx, lmac_id, CGXX_CMRX_RX_ID_MAP(0),
136		  (pkind & 0x3f));
137	return 0;
138}
139
140int cgx_lmac_link_status(struct lmac *lmac, int lmac_id, u64 *status)
141{
142	int ret = 0;
143
144	ret = cgx_intf_get_link_sts(lmac->cgx->cgx_id, lmac_id, status);
145	if (ret) {
146		debug("%s request failed for cgx%d lmac%d\n",
147		      __func__, lmac->cgx->cgx_id, lmac->lmac_id);
148		ret = -1;
149	}
150	return ret;
151}
152
153int cgx_lmac_rx_tx_enable(struct lmac *lmac, int lmac_id, bool enable)
154{
155	struct cgx *cgx = lmac->cgx;
156	union cgxx_cmrx_config cmrx_config;
157
158	if (!cgx || lmac_id >= cgx->lmac_count)
159		return -ENODEV;
160
161	cmrx_config.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0));
162	cmrx_config.s.data_pkt_rx_en =
163	cmrx_config.s.data_pkt_tx_en = enable ? 1 : 0;
164	cgx_write(cgx, lmac_id, CGXX_CMRX_CONFIG(0), cmrx_config.u);
165	return 0;
166}
167
168int cgx_lmac_link_enable(struct lmac *lmac, int lmac_id, bool enable,
169			 u64 *status)
170{
171	int ret = 0;
172
173	ret = cgx_intf_link_up_dwn(lmac->cgx->cgx_id, lmac_id, enable,
174				   status);
175	if (ret) {
176		debug("%s request failed for cgx%d lmac%d\n",
177		      __func__, lmac->cgx->cgx_id, lmac->lmac_id);
178		ret = -1;
179	}
180	return ret;
181}
182
183int cgx_lmac_internal_loopback(struct lmac *lmac, int lmac_id, bool enable)
184{
185	struct cgx *cgx = lmac->cgx;
186	union cgxx_cmrx_config cmrx_cfg;
187	union cgxx_gmp_pcs_mrx_control mrx_control;
188	union cgxx_spux_control1 spux_control1;
189	enum lmac_type lmac_type;
190
191	if (!cgx || lmac_id >= cgx->lmac_count)
192		return -ENODEV;
193
194	cmrx_cfg.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0));
195	lmac_type = cmrx_cfg.s.lmac_type;
196	if (lmac_type == LMAC_MODE_SGMII || lmac_type == LMAC_MODE_QSGMII) {
197		mrx_control.u = cgx_read(cgx, lmac_id,
198					 CGXX_GMP_PCS_MRX_CONTROL(0));
199		mrx_control.s.loopbck1 = enable ? 1 : 0;
200		cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CONTROL(0),
201			  mrx_control.u);
202	} else {
203		spux_control1.u = cgx_read(cgx, lmac_id,
204					   CGXX_SPUX_CONTROL1(0));
205		spux_control1.s.loopbck = enable ? 1 : 0;
206		cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1(0),
207			  spux_control1.u);
208	}
209	return 0;
210}
211
212static int cgx_lmac_init(struct cgx *cgx)
213{
214	struct lmac *lmac;
215	union cgxx_cmrx_config cmrx_cfg;
216	static int instance = 1;
217	int i;
218
219	cgx->lmac_count = cgx_read(cgx, 0, CGXX_CMR_RX_LMACS());
220	debug("%s: Found %d lmacs for cgx %d@%p\n", __func__, cgx->lmac_count,
221	      cgx->cgx_id, cgx->reg_base);
222
223	for (i = 0; i < cgx->lmac_count; i++) {
224		lmac = calloc(1, sizeof(*lmac));
225		if (!lmac)
226			return -ENOMEM;
227		lmac->instance = instance++;
228		snprintf(lmac->name, sizeof(lmac->name), "cgx_fwi_%d_%d",
229			 cgx->cgx_id, i);
230		/* Get LMAC type */
231		cmrx_cfg.u = cgx_read(cgx, i, CGXX_CMRX_CONFIG(0));
232		lmac->lmac_type = cmrx_cfg.s.lmac_type;
233
234		lmac->lmac_id = i;
235		lmac->cgx = cgx;
236		cgx->lmac[i] = lmac;
237		debug("%s: map id %d to lmac %p (%s), type:%d instance %d\n",
238		      __func__, i, lmac, lmac->name, lmac->lmac_type,
239		      lmac->instance);
240		lmac->init_pend = 1;
241		printf("CGX%d LMAC%d [%s]\n", lmac->cgx->cgx_id,
242		       lmac->lmac_id, lmac_type_to_str[lmac->lmac_type]);
243		octeontx2_board_get_mac_addr((lmac->instance - 1),
244					     lmac->mac_addr);
245		debug("%s: MAC %pM\n", __func__, lmac->mac_addr);
246		cgx_lmac_mac_filter_setup(lmac);
247	}
248	return 0;
249}
250
251int cgx_probe(struct udevice *dev)
252{
253	struct cgx *cgx = dev_get_priv(dev);
254	int err;
255
256	cgx->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE,
257				       PCI_REGION_MEM);
258	cgx->dev = dev;
259	cgx->cgx_id = ((u64)(cgx->reg_base) >> 24) & 0x7;
260
261	debug("%s CGX BAR %p, id: %d\n", __func__, cgx->reg_base,
262	      cgx->cgx_id);
263	debug("%s CGX %p, udev: %p\n", __func__, cgx, dev);
264
265	err = cgx_lmac_init(cgx);
266
267	return err;
268}
269
270int cgx_remove(struct udevice *dev)
271{
272	struct cgx *cgx = dev_get_priv(dev);
273	int i;
274
275	debug("%s: cgx remove reg_base %p cgx_id %d",
276	      __func__, cgx->reg_base, cgx->cgx_id);
277	for (i = 0; i < cgx->lmac_count; i++)
278		cgx_lmac_mac_filter_clear(cgx->lmac[i]);
279
280	return 0;
281}
282
283U_BOOT_DRIVER(cgx) = {
284	.name	= "cgx",
285	.id	= UCLASS_MISC,
286	.probe	= cgx_probe,
287	.remove	= cgx_remove,
288	.priv_auto	= sizeof(struct cgx),
289};
290
291static struct pci_device_id cgx_supported[] = {
292	{PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_CGX) },
293	{}
294};
295
296U_BOOT_PCI_DEVICE(cgx, cgx_supported);
297