162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Marvell OcteonTx2 CGX driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2018 Marvell.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/acpi.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/pci.h>
1262306a36Sopenharmony_ci#include <linux/netdevice.h>
1362306a36Sopenharmony_ci#include <linux/etherdevice.h>
1462306a36Sopenharmony_ci#include <linux/ethtool.h>
1562306a36Sopenharmony_ci#include <linux/phy.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/of_mdio.h>
1862306a36Sopenharmony_ci#include <linux/of_net.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "cgx.h"
2162306a36Sopenharmony_ci#include "rvu.h"
2262306a36Sopenharmony_ci#include "lmac_common.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define DRV_NAME	"Marvell-CGX/RPM"
2562306a36Sopenharmony_ci#define DRV_STRING      "Marvell CGX/RPM Driver"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic LIST_HEAD(cgx_list);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* Convert firmware speed encoding to user format(Mbps) */
3062306a36Sopenharmony_cistatic const u32 cgx_speed_mbps[CGX_LINK_SPEED_MAX] = {
3162306a36Sopenharmony_ci	[CGX_LINK_NONE] = 0,
3262306a36Sopenharmony_ci	[CGX_LINK_10M] = 10,
3362306a36Sopenharmony_ci	[CGX_LINK_100M] = 100,
3462306a36Sopenharmony_ci	[CGX_LINK_1G] = 1000,
3562306a36Sopenharmony_ci	[CGX_LINK_2HG] = 2500,
3662306a36Sopenharmony_ci	[CGX_LINK_5G] = 5000,
3762306a36Sopenharmony_ci	[CGX_LINK_10G] = 10000,
3862306a36Sopenharmony_ci	[CGX_LINK_20G] = 20000,
3962306a36Sopenharmony_ci	[CGX_LINK_25G] = 25000,
4062306a36Sopenharmony_ci	[CGX_LINK_40G] = 40000,
4162306a36Sopenharmony_ci	[CGX_LINK_50G] = 50000,
4262306a36Sopenharmony_ci	[CGX_LINK_80G] = 80000,
4362306a36Sopenharmony_ci	[CGX_LINK_100G] = 100000,
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Convert firmware lmac type encoding to string */
4762306a36Sopenharmony_cistatic const char *cgx_lmactype_string[LMAC_MODE_MAX] = {
4862306a36Sopenharmony_ci	[LMAC_MODE_SGMII] = "SGMII",
4962306a36Sopenharmony_ci	[LMAC_MODE_XAUI] = "XAUI",
5062306a36Sopenharmony_ci	[LMAC_MODE_RXAUI] = "RXAUI",
5162306a36Sopenharmony_ci	[LMAC_MODE_10G_R] = "10G_R",
5262306a36Sopenharmony_ci	[LMAC_MODE_40G_R] = "40G_R",
5362306a36Sopenharmony_ci	[LMAC_MODE_QSGMII] = "QSGMII",
5462306a36Sopenharmony_ci	[LMAC_MODE_25G_R] = "25G_R",
5562306a36Sopenharmony_ci	[LMAC_MODE_50G_R] = "50G_R",
5662306a36Sopenharmony_ci	[LMAC_MODE_100G_R] = "100G_R",
5762306a36Sopenharmony_ci	[LMAC_MODE_USXGMII] = "USXGMII",
5862306a36Sopenharmony_ci	[LMAC_MODE_USGMII] = "USGMII",
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* CGX PHY management internal APIs */
6262306a36Sopenharmony_cistatic int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool en);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Supported devices */
6562306a36Sopenharmony_cistatic const struct pci_device_id cgx_id_table[] = {
6662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_CGX) },
6762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10K_RPM) },
6862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_CN10KB_RPM) },
6962306a36Sopenharmony_ci	{ 0, }  /* end of table */
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cgx_id_table);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic bool is_dev_rpm(void *cgxd)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return (cgx->pdev->device == PCI_DEVID_CN10K_RPM) ||
7962306a36Sopenharmony_ci	       (cgx->pdev->device == PCI_DEVID_CN10KB_RPM);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cibool is_lmac_valid(struct cgx *cgx, int lmac_id)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	if (!cgx || lmac_id < 0 || lmac_id >= cgx->max_lmac_per_mac)
8562306a36Sopenharmony_ci		return false;
8662306a36Sopenharmony_ci	return test_bit(lmac_id, &cgx->lmac_bmap);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/* Helper function to get sequential index
9062306a36Sopenharmony_ci * given the enabled LMAC of a CGX
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistatic int get_sequence_id_of_lmac(struct cgx *cgx, int lmac_id)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	int tmp, id = 0;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	for_each_set_bit(tmp, &cgx->lmac_bmap, cgx->max_lmac_per_mac) {
9762306a36Sopenharmony_ci		if (tmp == lmac_id)
9862306a36Sopenharmony_ci			break;
9962306a36Sopenharmony_ci		id++;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return id;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct mac_ops *get_mac_ops(void *cgxd)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	if (!cgxd)
10862306a36Sopenharmony_ci		return cgxd;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return ((struct cgx *)cgxd)->mac_ops;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_civoid cgx_write(struct cgx *cgx, u64 lmac, u64 offset, u64 val)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	writeq(val, cgx->reg_base + (lmac << cgx->mac_ops->lmac_offset) +
11662306a36Sopenharmony_ci	       offset);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ciu64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return readq(cgx->reg_base + (lmac << cgx->mac_ops->lmac_offset) +
12262306a36Sopenharmony_ci		     offset);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	if (!cgx || lmac_id >= cgx->max_lmac_per_mac)
12862306a36Sopenharmony_ci		return NULL;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return cgx->lmac_idmap[lmac_id];
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciint cgx_get_cgxcnt_max(void)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct cgx *cgx_dev;
13662306a36Sopenharmony_ci	int idmax = -ENODEV;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	list_for_each_entry(cgx_dev, &cgx_list, cgx_list)
13962306a36Sopenharmony_ci		if (cgx_dev->cgx_id > idmax)
14062306a36Sopenharmony_ci			idmax = cgx_dev->cgx_id;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (idmax < 0)
14362306a36Sopenharmony_ci		return 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return idmax + 1;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ciint cgx_get_lmac_cnt(void *cgxd)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (!cgx)
15362306a36Sopenharmony_ci		return -ENODEV;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return cgx->lmac_count;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_civoid *cgx_get_pdata(int cgx_id)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct cgx *cgx_dev;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	list_for_each_entry(cgx_dev, &cgx_list, cgx_list) {
16362306a36Sopenharmony_ci		if (cgx_dev->cgx_id == cgx_id)
16462306a36Sopenharmony_ci			return cgx_dev;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	return NULL;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid cgx_lmac_write(int cgx_id, int lmac_id, u64 offset, u64 val)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Software must not access disabled LMAC registers */
17462306a36Sopenharmony_ci	if (!is_lmac_valid(cgx_dev, lmac_id))
17562306a36Sopenharmony_ci		return;
17662306a36Sopenharmony_ci	cgx_write(cgx_dev, lmac_id, offset, val);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciu64 cgx_lmac_read(int cgx_id, int lmac_id, u64 offset)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Software must not access disabled LMAC registers */
18462306a36Sopenharmony_ci	if (!is_lmac_valid(cgx_dev, lmac_id))
18562306a36Sopenharmony_ci		return 0;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return cgx_read(cgx_dev, lmac_id, offset);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ciint cgx_get_cgxid(void *cgxd)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (!cgx)
19562306a36Sopenharmony_ci		return -EINVAL;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return cgx->cgx_id;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciu8 cgx_lmac_get_p2x(int cgx_id, int lmac_id)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
20362306a36Sopenharmony_ci	u64 cfg;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_CFG);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return (cfg & CMR_P2X_SEL_MASK) >> CMR_P2X_SEL_SHIFT;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/* Ensure the required lock for event queue(where asynchronous events are
21162306a36Sopenharmony_ci * posted) is acquired before calling this API. Else an asynchronous event(with
21262306a36Sopenharmony_ci * latest link status) can reach the destination before this function returns
21362306a36Sopenharmony_ci * and could make the link status appear wrong.
21462306a36Sopenharmony_ci */
21562306a36Sopenharmony_ciint cgx_get_link_info(void *cgxd, int lmac_id,
21662306a36Sopenharmony_ci		      struct cgx_link_user_info *linfo)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgxd);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (!lmac)
22162306a36Sopenharmony_ci		return -ENODEV;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	*linfo = lmac->link_info;
22462306a36Sopenharmony_ci	return 0;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ciint cgx_lmac_addr_set(u8 cgx_id, u8 lmac_id, u8 *mac_addr)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
23062306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev);
23162306a36Sopenharmony_ci	struct mac_ops *mac_ops;
23262306a36Sopenharmony_ci	int index, id;
23362306a36Sopenharmony_ci	u64 cfg;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!lmac)
23662306a36Sopenharmony_ci		return -ENODEV;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* access mac_ops to know csr_offset */
23962306a36Sopenharmony_ci	mac_ops = cgx_dev->mac_ops;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* copy 6bytes from macaddr */
24262306a36Sopenharmony_ci	/* memcpy(&cfg, mac_addr, 6); */
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	cfg = ether_addr_to_u64(mac_addr);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	id = get_sequence_id_of_lmac(cgx_dev, lmac_id);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	index = id * lmac->mac_to_index_bmap.max;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)),
25162306a36Sopenharmony_ci		  cfg | CGX_DMAC_CAM_ADDR_ENABLE | ((u64)lmac_id << 49));
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
25462306a36Sopenharmony_ci	cfg |= (CGX_DMAC_CTL0_CAM_ENABLE | CGX_DMAC_BCAST_MODE |
25562306a36Sopenharmony_ci		CGX_DMAC_MCAST_MODE);
25662306a36Sopenharmony_ci	cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return 0;
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ciu64 cgx_read_dmac_ctrl(void *cgxd, int lmac_id)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct mac_ops *mac_ops;
26462306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (!cgxd || !is_lmac_valid(cgxd, lmac_id))
26762306a36Sopenharmony_ci		return 0;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	cgx = cgxd;
27062306a36Sopenharmony_ci	/* Get mac_ops to know csr offset */
27162306a36Sopenharmony_ci	mac_ops = cgx->mac_ops;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return cgx_read(cgxd, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ciu64 cgx_read_dmac_entry(void *cgxd, int index)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct mac_ops *mac_ops;
27962306a36Sopenharmony_ci	struct cgx *cgx;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (!cgxd)
28262306a36Sopenharmony_ci		return 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	cgx = cgxd;
28562306a36Sopenharmony_ci	mac_ops = cgx->mac_ops;
28662306a36Sopenharmony_ci	return cgx_read(cgx, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 8)));
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ciint cgx_lmac_addr_add(u8 cgx_id, u8 lmac_id, u8 *mac_addr)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
29262306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev);
29362306a36Sopenharmony_ci	struct mac_ops *mac_ops;
29462306a36Sopenharmony_ci	int index, idx;
29562306a36Sopenharmony_ci	u64 cfg = 0;
29662306a36Sopenharmony_ci	int id;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (!lmac)
29962306a36Sopenharmony_ci		return -ENODEV;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	mac_ops = cgx_dev->mac_ops;
30262306a36Sopenharmony_ci	/* Get available index where entry is to be installed */
30362306a36Sopenharmony_ci	idx = rvu_alloc_rsrc(&lmac->mac_to_index_bmap);
30462306a36Sopenharmony_ci	if (idx < 0)
30562306a36Sopenharmony_ci		return idx;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	id = get_sequence_id_of_lmac(cgx_dev, lmac_id);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	index = id * lmac->mac_to_index_bmap.max + idx;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	cfg = ether_addr_to_u64(mac_addr);
31262306a36Sopenharmony_ci	cfg |= CGX_DMAC_CAM_ADDR_ENABLE;
31362306a36Sopenharmony_ci	cfg |= ((u64)lmac_id << 49);
31462306a36Sopenharmony_ci	cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), cfg);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
31762306a36Sopenharmony_ci	cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_CAM_ACCEPT);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (is_multicast_ether_addr(mac_addr)) {
32062306a36Sopenharmony_ci		cfg &= ~GENMASK_ULL(2, 1);
32162306a36Sopenharmony_ci		cfg |= CGX_DMAC_MCAST_MODE_CAM;
32262306a36Sopenharmony_ci		lmac->mcast_filters_count++;
32362306a36Sopenharmony_ci	} else if (!lmac->mcast_filters_count) {
32462306a36Sopenharmony_ci		cfg |= CGX_DMAC_MCAST_MODE;
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return idx;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ciint cgx_lmac_addr_reset(u8 cgx_id, u8 lmac_id)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
33562306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev);
33662306a36Sopenharmony_ci	struct mac_ops *mac_ops;
33762306a36Sopenharmony_ci	u8 index = 0, id;
33862306a36Sopenharmony_ci	u64 cfg;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (!lmac)
34162306a36Sopenharmony_ci		return -ENODEV;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	mac_ops = cgx_dev->mac_ops;
34462306a36Sopenharmony_ci	/* Restore index 0 to its default init value as done during
34562306a36Sopenharmony_ci	 * cgx_lmac_init
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	set_bit(0, lmac->mac_to_index_bmap.bmap);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	id = get_sequence_id_of_lmac(cgx_dev, lmac_id);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	index = id * lmac->mac_to_index_bmap.max + index;
35262306a36Sopenharmony_ci	cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), 0);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* Reset CGXX_CMRX_RX_DMAC_CTL0 register to default state */
35562306a36Sopenharmony_ci	cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
35662306a36Sopenharmony_ci	cfg &= ~CGX_DMAC_CAM_ACCEPT;
35762306a36Sopenharmony_ci	cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_MCAST_MODE);
35862306a36Sopenharmony_ci	cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/* Allows caller to change macaddress associated with index
36462306a36Sopenharmony_ci * in dmac filter table including index 0 reserved for
36562306a36Sopenharmony_ci * interface mac address
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_ciint cgx_lmac_addr_update(u8 cgx_id, u8 lmac_id, u8 *mac_addr, u8 index)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
37062306a36Sopenharmony_ci	struct mac_ops *mac_ops;
37162306a36Sopenharmony_ci	struct lmac *lmac;
37262306a36Sopenharmony_ci	u64 cfg;
37362306a36Sopenharmony_ci	int id;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	lmac = lmac_pdata(lmac_id, cgx_dev);
37662306a36Sopenharmony_ci	if (!lmac)
37762306a36Sopenharmony_ci		return -ENODEV;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	mac_ops = cgx_dev->mac_ops;
38062306a36Sopenharmony_ci	/* Validate the index */
38162306a36Sopenharmony_ci	if (index >= lmac->mac_to_index_bmap.max)
38262306a36Sopenharmony_ci		return -EINVAL;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/* ensure index is already set */
38562306a36Sopenharmony_ci	if (!test_bit(index, lmac->mac_to_index_bmap.bmap))
38662306a36Sopenharmony_ci		return -EINVAL;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	id = get_sequence_id_of_lmac(cgx_dev, lmac_id);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	index = id * lmac->mac_to_index_bmap.max + index;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	cfg = cgx_read(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)));
39362306a36Sopenharmony_ci	cfg &= ~CGX_RX_DMAC_ADR_MASK;
39462306a36Sopenharmony_ci	cfg |= ether_addr_to_u64(mac_addr);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), cfg);
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ciint cgx_lmac_addr_del(u8 cgx_id, u8 lmac_id, u8 index)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
40362306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev);
40462306a36Sopenharmony_ci	struct mac_ops *mac_ops;
40562306a36Sopenharmony_ci	u8 mac[ETH_ALEN];
40662306a36Sopenharmony_ci	u64 cfg;
40762306a36Sopenharmony_ci	int id;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (!lmac)
41062306a36Sopenharmony_ci		return -ENODEV;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	mac_ops = cgx_dev->mac_ops;
41362306a36Sopenharmony_ci	/* Validate the index */
41462306a36Sopenharmony_ci	if (index >= lmac->mac_to_index_bmap.max)
41562306a36Sopenharmony_ci		return -EINVAL;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* Skip deletion for reserved index i.e. index 0 */
41862306a36Sopenharmony_ci	if (index == 0)
41962306a36Sopenharmony_ci		return 0;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	rvu_free_rsrc(&lmac->mac_to_index_bmap, index);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	id = get_sequence_id_of_lmac(cgx_dev, lmac_id);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	index = id * lmac->mac_to_index_bmap.max + index;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* Read MAC address to check whether it is ucast or mcast */
42862306a36Sopenharmony_ci	cfg = cgx_read(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)));
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	u64_to_ether_addr(cfg, mac);
43162306a36Sopenharmony_ci	if (is_multicast_ether_addr(mac))
43262306a36Sopenharmony_ci		lmac->mcast_filters_count--;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (!lmac->mcast_filters_count) {
43562306a36Sopenharmony_ci		cfg = cgx_read(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
43662306a36Sopenharmony_ci		cfg &= ~GENMASK_ULL(2, 1);
43762306a36Sopenharmony_ci		cfg |= CGX_DMAC_MCAST_MODE;
43862306a36Sopenharmony_ci		cgx_write(cgx_dev, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	cgx_write(cgx_dev, 0, (CGXX_CMRX_RX_DMAC_CAM0 + (index * 0x8)), 0);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	return 0;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ciint cgx_lmac_addr_max_entries_get(u8 cgx_id, u8 lmac_id)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
44962306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (lmac)
45262306a36Sopenharmony_ci		return lmac->mac_to_index_bmap.max;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ciu64 cgx_lmac_addr_get(u8 cgx_id, u8 lmac_id)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct cgx *cgx_dev = cgx_get_pdata(cgx_id);
46062306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgx_dev);
46162306a36Sopenharmony_ci	struct mac_ops *mac_ops;
46262306a36Sopenharmony_ci	int index;
46362306a36Sopenharmony_ci	u64 cfg;
46462306a36Sopenharmony_ci	int id;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	mac_ops = cgx_dev->mac_ops;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	id = get_sequence_id_of_lmac(cgx_dev, lmac_id);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	index = id * lmac->mac_to_index_bmap.max;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	cfg = cgx_read(cgx_dev, 0, CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8);
47362306a36Sopenharmony_ci	return cfg & CGX_RX_DMAC_ADR_MASK;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ciint cgx_set_pkind(void *cgxd, u8 lmac_id, int pkind)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
48162306a36Sopenharmony_ci		return -ENODEV;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, cgx->mac_ops->rxid_map_offset, (pkind & 0x3F));
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic u8 cgx_get_lmac_type(void *cgxd, int lmac_id)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
49062306a36Sopenharmony_ci	u64 cfg;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG);
49362306a36Sopenharmony_ci	return (cfg >> CGX_LMAC_TYPE_SHIFT) & CGX_LMAC_TYPE_MASK;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic u32 cgx_get_lmac_fifo_len(void *cgxd, int lmac_id)
49762306a36Sopenharmony_ci{
49862306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
49962306a36Sopenharmony_ci	u8 num_lmacs;
50062306a36Sopenharmony_ci	u32 fifo_len;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	fifo_len = cgx->mac_ops->fifo_len;
50362306a36Sopenharmony_ci	num_lmacs = cgx->mac_ops->get_nr_lmacs(cgx);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	switch (num_lmacs) {
50662306a36Sopenharmony_ci	case 1:
50762306a36Sopenharmony_ci		return fifo_len;
50862306a36Sopenharmony_ci	case 2:
50962306a36Sopenharmony_ci		return fifo_len / 2;
51062306a36Sopenharmony_ci	case 3:
51162306a36Sopenharmony_ci		/* LMAC0 gets half of the FIFO, reset 1/4th */
51262306a36Sopenharmony_ci		if (lmac_id == 0)
51362306a36Sopenharmony_ci			return fifo_len / 2;
51462306a36Sopenharmony_ci		return fifo_len / 4;
51562306a36Sopenharmony_ci	case 4:
51662306a36Sopenharmony_ci	default:
51762306a36Sopenharmony_ci		return fifo_len / 4;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci	return 0;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci/* Configure CGX LMAC in internal loopback mode */
52362306a36Sopenharmony_ciint cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
52662306a36Sopenharmony_ci	struct lmac *lmac;
52762306a36Sopenharmony_ci	u64 cfg;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
53062306a36Sopenharmony_ci		return -ENODEV;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	lmac = lmac_pdata(lmac_id, cgx);
53362306a36Sopenharmony_ci	if (lmac->lmac_type == LMAC_MODE_SGMII ||
53462306a36Sopenharmony_ci	    lmac->lmac_type == LMAC_MODE_QSGMII) {
53562306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL);
53662306a36Sopenharmony_ci		if (enable)
53762306a36Sopenharmony_ci			cfg |= CGXX_GMP_PCS_MRX_CTL_LBK;
53862306a36Sopenharmony_ci		else
53962306a36Sopenharmony_ci			cfg &= ~CGXX_GMP_PCS_MRX_CTL_LBK;
54062306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CTL, cfg);
54162306a36Sopenharmony_ci	} else {
54262306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_SPUX_CONTROL1);
54362306a36Sopenharmony_ci		if (enable)
54462306a36Sopenharmony_ci			cfg |= CGXX_SPUX_CONTROL1_LBK;
54562306a36Sopenharmony_ci		else
54662306a36Sopenharmony_ci			cfg &= ~CGXX_SPUX_CONTROL1_LBK;
54762306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1, cfg);
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_civoid cgx_lmac_promisc_config(int cgx_id, int lmac_id, bool enable)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct cgx *cgx = cgx_get_pdata(cgx_id);
55562306a36Sopenharmony_ci	struct lmac *lmac = lmac_pdata(lmac_id, cgx);
55662306a36Sopenharmony_ci	struct mac_ops *mac_ops;
55762306a36Sopenharmony_ci	u16 max_dmac;
55862306a36Sopenharmony_ci	int index, i;
55962306a36Sopenharmony_ci	u64 cfg = 0;
56062306a36Sopenharmony_ci	int id;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (!cgx || !lmac)
56362306a36Sopenharmony_ci		return;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	max_dmac = lmac->mac_to_index_bmap.max;
56662306a36Sopenharmony_ci	id = get_sequence_id_of_lmac(cgx, lmac_id);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	mac_ops = cgx->mac_ops;
56962306a36Sopenharmony_ci	if (enable) {
57062306a36Sopenharmony_ci		/* Enable promiscuous mode on LMAC */
57162306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
57262306a36Sopenharmony_ci		cfg &= ~CGX_DMAC_CAM_ACCEPT;
57362306a36Sopenharmony_ci		cfg |= (CGX_DMAC_BCAST_MODE | CGX_DMAC_MCAST_MODE);
57462306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci		for (i = 0; i < max_dmac; i++) {
57762306a36Sopenharmony_ci			index = id * max_dmac + i;
57862306a36Sopenharmony_ci			cfg = cgx_read(cgx, 0,
57962306a36Sopenharmony_ci				       (CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8));
58062306a36Sopenharmony_ci			cfg &= ~CGX_DMAC_CAM_ADDR_ENABLE;
58162306a36Sopenharmony_ci			cgx_write(cgx, 0,
58262306a36Sopenharmony_ci				  (CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8), cfg);
58362306a36Sopenharmony_ci		}
58462306a36Sopenharmony_ci	} else {
58562306a36Sopenharmony_ci		/* Disable promiscuous mode */
58662306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0);
58762306a36Sopenharmony_ci		cfg |= CGX_DMAC_CAM_ACCEPT | CGX_DMAC_MCAST_MODE;
58862306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_CMRX_RX_DMAC_CTL0, cfg);
58962306a36Sopenharmony_ci		for (i = 0; i < max_dmac; i++) {
59062306a36Sopenharmony_ci			index = id * max_dmac + i;
59162306a36Sopenharmony_ci			cfg = cgx_read(cgx, 0,
59262306a36Sopenharmony_ci				       (CGXX_CMRX_RX_DMAC_CAM0 + index * 0x8));
59362306a36Sopenharmony_ci			if ((cfg & CGX_RX_DMAC_ADR_MASK) != 0) {
59462306a36Sopenharmony_ci				cfg |= CGX_DMAC_CAM_ADDR_ENABLE;
59562306a36Sopenharmony_ci				cgx_write(cgx, 0,
59662306a36Sopenharmony_ci					  (CGXX_CMRX_RX_DMAC_CAM0 +
59762306a36Sopenharmony_ci					   index * 0x8),
59862306a36Sopenharmony_ci					  cfg);
59962306a36Sopenharmony_ci			}
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic int cgx_lmac_get_pause_frm_status(void *cgxd, int lmac_id,
60562306a36Sopenharmony_ci					 u8 *tx_pause, u8 *rx_pause)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
60862306a36Sopenharmony_ci	u64 cfg;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (is_dev_rpm(cgx))
61162306a36Sopenharmony_ci		return 0;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
61462306a36Sopenharmony_ci		return -ENODEV;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
61762306a36Sopenharmony_ci	*rx_pause = !!(cfg & CGX_SMUX_RX_FRM_CTL_CTL_BCK);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL);
62062306a36Sopenharmony_ci	*tx_pause = !!(cfg & CGX_SMUX_TX_CTL_L2P_BP_CONV);
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/* Enable or disable forwarding received pause frames to Tx block */
62562306a36Sopenharmony_civoid cgx_lmac_enadis_rx_pause_fwding(void *cgxd, int lmac_id, bool enable)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
62862306a36Sopenharmony_ci	u8 rx_pause, tx_pause;
62962306a36Sopenharmony_ci	bool is_pfc_enabled;
63062306a36Sopenharmony_ci	struct lmac *lmac;
63162306a36Sopenharmony_ci	u64 cfg;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!cgx)
63462306a36Sopenharmony_ci		return;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	lmac = lmac_pdata(lmac_id, cgx);
63762306a36Sopenharmony_ci	if (!lmac)
63862306a36Sopenharmony_ci		return;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/* Pause frames are not enabled just return */
64162306a36Sopenharmony_ci	if (!bitmap_weight(lmac->rx_fc_pfvf_bmap.bmap, lmac->rx_fc_pfvf_bmap.max))
64262306a36Sopenharmony_ci		return;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	cgx_lmac_get_pause_frm_status(cgx, lmac_id, &rx_pause, &tx_pause);
64562306a36Sopenharmony_ci	is_pfc_enabled = rx_pause ? false : true;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (enable) {
64862306a36Sopenharmony_ci		if (!is_pfc_enabled) {
64962306a36Sopenharmony_ci			cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
65062306a36Sopenharmony_ci			cfg |= CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK;
65162306a36Sopenharmony_ci			cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci			cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
65462306a36Sopenharmony_ci			cfg |= CGX_SMUX_RX_FRM_CTL_CTL_BCK;
65562306a36Sopenharmony_ci			cgx_write(cgx, lmac_id,	CGXX_SMUX_RX_FRM_CTL, cfg);
65662306a36Sopenharmony_ci		} else {
65762306a36Sopenharmony_ci			cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL);
65862306a36Sopenharmony_ci			cfg |= CGXX_SMUX_CBFC_CTL_BCK_EN;
65962306a36Sopenharmony_ci			cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg);
66062306a36Sopenharmony_ci		}
66162306a36Sopenharmony_ci	} else {
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		if (!is_pfc_enabled) {
66462306a36Sopenharmony_ci			cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
66562306a36Sopenharmony_ci			cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK;
66662306a36Sopenharmony_ci			cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci			cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
66962306a36Sopenharmony_ci			cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK;
67062306a36Sopenharmony_ci			cgx_write(cgx, lmac_id,	CGXX_SMUX_RX_FRM_CTL, cfg);
67162306a36Sopenharmony_ci		} else {
67262306a36Sopenharmony_ci			cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL);
67362306a36Sopenharmony_ci			cfg &= ~CGXX_SMUX_CBFC_CTL_BCK_EN;
67462306a36Sopenharmony_ci			cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg);
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ciint cgx_get_rx_stats(void *cgxd, int lmac_id, int idx, u64 *rx_stat)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
68462306a36Sopenharmony_ci		return -ENODEV;
68562306a36Sopenharmony_ci	*rx_stat =  cgx_read(cgx, lmac_id, CGXX_CMRX_RX_STAT0 + (idx * 8));
68662306a36Sopenharmony_ci	return 0;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ciint cgx_get_tx_stats(void *cgxd, int lmac_id, int idx, u64 *tx_stat)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
69462306a36Sopenharmony_ci		return -ENODEV;
69562306a36Sopenharmony_ci	*tx_stat = cgx_read(cgx, lmac_id, CGXX_CMRX_TX_STAT0 + (idx * 8));
69662306a36Sopenharmony_ci	return 0;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ciu64 cgx_features_get(void *cgxd)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	return ((struct cgx *)cgxd)->hw_features;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int cgx_set_fec_stats_count(struct cgx_link_user_info *linfo)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	if (!linfo->fec)
70762306a36Sopenharmony_ci		return 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	switch (linfo->lmac_type_id) {
71062306a36Sopenharmony_ci	case LMAC_MODE_SGMII:
71162306a36Sopenharmony_ci	case LMAC_MODE_XAUI:
71262306a36Sopenharmony_ci	case LMAC_MODE_RXAUI:
71362306a36Sopenharmony_ci	case LMAC_MODE_QSGMII:
71462306a36Sopenharmony_ci		return 0;
71562306a36Sopenharmony_ci	case LMAC_MODE_10G_R:
71662306a36Sopenharmony_ci	case LMAC_MODE_25G_R:
71762306a36Sopenharmony_ci	case LMAC_MODE_100G_R:
71862306a36Sopenharmony_ci	case LMAC_MODE_USXGMII:
71962306a36Sopenharmony_ci		return 1;
72062306a36Sopenharmony_ci	case LMAC_MODE_40G_R:
72162306a36Sopenharmony_ci		return 4;
72262306a36Sopenharmony_ci	case LMAC_MODE_50G_R:
72362306a36Sopenharmony_ci		if (linfo->fec == OTX2_FEC_BASER)
72462306a36Sopenharmony_ci			return 2;
72562306a36Sopenharmony_ci		else
72662306a36Sopenharmony_ci			return 1;
72762306a36Sopenharmony_ci	default:
72862306a36Sopenharmony_ci		return 0;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ciint cgx_get_fec_stats(void *cgxd, int lmac_id, struct cgx_fec_stats_rsp *rsp)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	int stats, fec_stats_count = 0;
73562306a36Sopenharmony_ci	int corr_reg, uncorr_reg;
73662306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
73962306a36Sopenharmony_ci		return -ENODEV;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (cgx->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_NONE)
74262306a36Sopenharmony_ci		return 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	fec_stats_count =
74562306a36Sopenharmony_ci		cgx_set_fec_stats_count(&cgx->lmac_idmap[lmac_id]->link_info);
74662306a36Sopenharmony_ci	if (cgx->lmac_idmap[lmac_id]->link_info.fec == OTX2_FEC_BASER) {
74762306a36Sopenharmony_ci		corr_reg = CGXX_SPUX_LNX_FEC_CORR_BLOCKS;
74862306a36Sopenharmony_ci		uncorr_reg = CGXX_SPUX_LNX_FEC_UNCORR_BLOCKS;
74962306a36Sopenharmony_ci	} else {
75062306a36Sopenharmony_ci		corr_reg = CGXX_SPUX_RSFEC_CORR;
75162306a36Sopenharmony_ci		uncorr_reg = CGXX_SPUX_RSFEC_UNCORR;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci	for (stats = 0; stats < fec_stats_count; stats++) {
75462306a36Sopenharmony_ci		rsp->fec_corr_blks +=
75562306a36Sopenharmony_ci			cgx_read(cgx, lmac_id, corr_reg + (stats * 8));
75662306a36Sopenharmony_ci		rsp->fec_uncorr_blks +=
75762306a36Sopenharmony_ci			cgx_read(cgx, lmac_id, uncorr_reg + (stats * 8));
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci	return 0;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ciint cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
76562306a36Sopenharmony_ci	u64 cfg;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
76862306a36Sopenharmony_ci		return -ENODEV;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG);
77162306a36Sopenharmony_ci	if (enable)
77262306a36Sopenharmony_ci		cfg |= DATA_PKT_RX_EN | DATA_PKT_TX_EN;
77362306a36Sopenharmony_ci	else
77462306a36Sopenharmony_ci		cfg &= ~(DATA_PKT_RX_EN | DATA_PKT_TX_EN);
77562306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg);
77662306a36Sopenharmony_ci	return 0;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ciint cgx_lmac_tx_enable(void *cgxd, int lmac_id, bool enable)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
78262306a36Sopenharmony_ci	u64 cfg, last;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
78562306a36Sopenharmony_ci		return -ENODEV;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG);
78862306a36Sopenharmony_ci	last = cfg;
78962306a36Sopenharmony_ci	if (enable)
79062306a36Sopenharmony_ci		cfg |= DATA_PKT_TX_EN;
79162306a36Sopenharmony_ci	else
79262306a36Sopenharmony_ci		cfg &= ~DATA_PKT_TX_EN;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (cfg != last)
79562306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg);
79662306a36Sopenharmony_ci	return !!(last & DATA_PKT_TX_EN);
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic int cgx_lmac_enadis_pause_frm(void *cgxd, int lmac_id,
80062306a36Sopenharmony_ci				     u8 tx_pause, u8 rx_pause)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
80362306a36Sopenharmony_ci	u64 cfg;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (is_dev_rpm(cgx))
80662306a36Sopenharmony_ci		return 0;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
80962306a36Sopenharmony_ci		return -ENODEV;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
81262306a36Sopenharmony_ci	cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK;
81362306a36Sopenharmony_ci	cfg |= rx_pause ? CGX_SMUX_RX_FRM_CTL_CTL_BCK : 0x0;
81462306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL);
81762306a36Sopenharmony_ci	cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV;
81862306a36Sopenharmony_ci	cfg |= tx_pause ? CGX_SMUX_TX_CTL_L2P_BP_CONV : 0x0;
81962306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	cfg = cgx_read(cgx, 0, CGXX_CMR_RX_OVR_BP);
82262306a36Sopenharmony_ci	if (tx_pause) {
82362306a36Sopenharmony_ci		cfg &= ~CGX_CMR_RX_OVR_BP_EN(lmac_id);
82462306a36Sopenharmony_ci	} else {
82562306a36Sopenharmony_ci		cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id);
82662306a36Sopenharmony_ci		cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id);
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci	cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg);
82962306a36Sopenharmony_ci	return 0;
83062306a36Sopenharmony_ci}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic void cgx_lmac_pause_frm_config(void *cgxd, int lmac_id, bool enable)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
83562306a36Sopenharmony_ci	u64 cfg;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
83862306a36Sopenharmony_ci		return;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (enable) {
84162306a36Sopenharmony_ci		/* Set pause time and interval */
84262306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_TIME,
84362306a36Sopenharmony_ci			  DEFAULT_PAUSE_TIME);
84462306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL);
84562306a36Sopenharmony_ci		cfg &= ~0xFFFFULL;
84662306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_SMUX_TX_PAUSE_PKT_INTERVAL,
84762306a36Sopenharmony_ci			  cfg | (DEFAULT_PAUSE_TIME / 2));
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_TIME,
85062306a36Sopenharmony_ci			  DEFAULT_PAUSE_TIME);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id,
85362306a36Sopenharmony_ci			       CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL);
85462306a36Sopenharmony_ci		cfg &= ~0xFFFFULL;
85562306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_GMP_GMI_TX_PAUSE_PKT_INTERVAL,
85662306a36Sopenharmony_ci			  cfg | (DEFAULT_PAUSE_TIME / 2));
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* ALL pause frames received are completely ignored */
86062306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
86162306a36Sopenharmony_ci	cfg &= ~CGX_SMUX_RX_FRM_CTL_CTL_BCK;
86262306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
86562306a36Sopenharmony_ci	cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK;
86662306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	/* Disable pause frames transmission */
86962306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_TX_CTL);
87062306a36Sopenharmony_ci	cfg &= ~CGX_SMUX_TX_CTL_L2P_BP_CONV;
87162306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_SMUX_TX_CTL, cfg);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	cfg = cgx_read(cgx, 0, CGXX_CMR_RX_OVR_BP);
87462306a36Sopenharmony_ci	cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id);
87562306a36Sopenharmony_ci	cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id);
87662306a36Sopenharmony_ci	cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* Disable all PFC classes by default */
87962306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL);
88062306a36Sopenharmony_ci	cfg = FIELD_SET(CGX_PFC_CLASS_MASK, 0, cfg);
88162306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg);
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ciint verify_lmac_fc_cfg(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause,
88562306a36Sopenharmony_ci		       int pfvf_idx)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
88862306a36Sopenharmony_ci	struct lmac *lmac;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	lmac = lmac_pdata(lmac_id, cgx);
89162306a36Sopenharmony_ci	if (!lmac)
89262306a36Sopenharmony_ci		return -ENODEV;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (!rx_pause)
89562306a36Sopenharmony_ci		clear_bit(pfvf_idx, lmac->rx_fc_pfvf_bmap.bmap);
89662306a36Sopenharmony_ci	else
89762306a36Sopenharmony_ci		set_bit(pfvf_idx, lmac->rx_fc_pfvf_bmap.bmap);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	if (!tx_pause)
90062306a36Sopenharmony_ci		clear_bit(pfvf_idx, lmac->tx_fc_pfvf_bmap.bmap);
90162306a36Sopenharmony_ci	else
90262306a36Sopenharmony_ci		set_bit(pfvf_idx, lmac->tx_fc_pfvf_bmap.bmap);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/* check if other pfvfs are using flow control */
90562306a36Sopenharmony_ci	if (!rx_pause && bitmap_weight(lmac->rx_fc_pfvf_bmap.bmap, lmac->rx_fc_pfvf_bmap.max)) {
90662306a36Sopenharmony_ci		dev_warn(&cgx->pdev->dev,
90762306a36Sopenharmony_ci			 "Receive Flow control disable not permitted as its used by other PFVFs\n");
90862306a36Sopenharmony_ci		return -EPERM;
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if (!tx_pause && bitmap_weight(lmac->tx_fc_pfvf_bmap.bmap, lmac->tx_fc_pfvf_bmap.max)) {
91262306a36Sopenharmony_ci		dev_warn(&cgx->pdev->dev,
91362306a36Sopenharmony_ci			 "Transmit Flow control disable not permitted as its used by other PFVFs\n");
91462306a36Sopenharmony_ci		return -EPERM;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	return 0;
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ciint cgx_lmac_pfc_config(void *cgxd, int lmac_id, u8 tx_pause,
92162306a36Sopenharmony_ci			u8 rx_pause, u16 pfc_en)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
92462306a36Sopenharmony_ci	u64 cfg;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
92762306a36Sopenharmony_ci		return -ENODEV;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* Return as no traffic classes are requested */
93062306a36Sopenharmony_ci	if (tx_pause && !pfc_en)
93162306a36Sopenharmony_ci		return 0;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL);
93462306a36Sopenharmony_ci	pfc_en |= FIELD_GET(CGX_PFC_CLASS_MASK, cfg);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (rx_pause) {
93762306a36Sopenharmony_ci		cfg |= (CGXX_SMUX_CBFC_CTL_RX_EN |
93862306a36Sopenharmony_ci			CGXX_SMUX_CBFC_CTL_BCK_EN |
93962306a36Sopenharmony_ci			CGXX_SMUX_CBFC_CTL_DRP_EN);
94062306a36Sopenharmony_ci	} else {
94162306a36Sopenharmony_ci		cfg &= ~(CGXX_SMUX_CBFC_CTL_RX_EN |
94262306a36Sopenharmony_ci			CGXX_SMUX_CBFC_CTL_BCK_EN |
94362306a36Sopenharmony_ci			CGXX_SMUX_CBFC_CTL_DRP_EN);
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (tx_pause) {
94762306a36Sopenharmony_ci		cfg |= CGXX_SMUX_CBFC_CTL_TX_EN;
94862306a36Sopenharmony_ci		cfg = FIELD_SET(CGX_PFC_CLASS_MASK, pfc_en, cfg);
94962306a36Sopenharmony_ci	} else {
95062306a36Sopenharmony_ci		cfg &= ~CGXX_SMUX_CBFC_CTL_TX_EN;
95162306a36Sopenharmony_ci		cfg = FIELD_SET(CGX_PFC_CLASS_MASK, 0, cfg);
95262306a36Sopenharmony_ci	}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	/* Write source MAC address which will be filled into PFC packet */
95762306a36Sopenharmony_ci	cfg = cgx_lmac_addr_get(cgx->cgx_id, lmac_id);
95862306a36Sopenharmony_ci	cgx_write(cgx, lmac_id, CGXX_SMUX_SMAC, cfg);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	return 0;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ciint cgx_lmac_get_pfc_frm_cfg(void *cgxd, int lmac_id, u8 *tx_pause,
96462306a36Sopenharmony_ci			     u8 *rx_pause)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
96762306a36Sopenharmony_ci	u64 cfg;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
97062306a36Sopenharmony_ci		return -ENODEV;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	*rx_pause = !!(cfg & CGXX_SMUX_CBFC_CTL_RX_EN);
97562306a36Sopenharmony_ci	*tx_pause = !!(cfg & CGXX_SMUX_CBFC_CTL_TX_EN);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_civoid cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
98362306a36Sopenharmony_ci	u64 cfg;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	if (!cgx)
98662306a36Sopenharmony_ci		return;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (enable) {
98962306a36Sopenharmony_ci		/* Enable inbound PTP timestamping */
99062306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
99162306a36Sopenharmony_ci		cfg |= CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE;
99262306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
99562306a36Sopenharmony_ci		cfg |= CGX_SMUX_RX_FRM_CTL_PTP_MODE;
99662306a36Sopenharmony_ci		cgx_write(cgx, lmac_id,	CGXX_SMUX_RX_FRM_CTL, cfg);
99762306a36Sopenharmony_ci	} else {
99862306a36Sopenharmony_ci		/* Disable inbound PTP stamping */
99962306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
100062306a36Sopenharmony_ci		cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE;
100162306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
100462306a36Sopenharmony_ci		cfg &= ~CGX_SMUX_RX_FRM_CTL_PTP_MODE;
100562306a36Sopenharmony_ci		cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg);
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci/* CGX Firmware interface low level support */
101062306a36Sopenharmony_ciint cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct cgx *cgx = lmac->cgx;
101362306a36Sopenharmony_ci	struct device *dev;
101462306a36Sopenharmony_ci	int err = 0;
101562306a36Sopenharmony_ci	u64 cmd;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* Ensure no other command is in progress */
101862306a36Sopenharmony_ci	err = mutex_lock_interruptible(&lmac->cmd_lock);
101962306a36Sopenharmony_ci	if (err)
102062306a36Sopenharmony_ci		return err;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/* Ensure command register is free */
102362306a36Sopenharmony_ci	cmd = cgx_read(cgx, lmac->lmac_id,  CGX_COMMAND_REG);
102462306a36Sopenharmony_ci	if (FIELD_GET(CMDREG_OWN, cmd) != CGX_CMD_OWN_NS) {
102562306a36Sopenharmony_ci		err = -EBUSY;
102662306a36Sopenharmony_ci		goto unlock;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	/* Update ownership in command request */
103062306a36Sopenharmony_ci	req = FIELD_SET(CMDREG_OWN, CGX_CMD_OWN_FIRMWARE, req);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Mark this lmac as pending, before we start */
103362306a36Sopenharmony_ci	lmac->cmd_pend = true;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* Start command in hardware */
103662306a36Sopenharmony_ci	cgx_write(cgx, lmac->lmac_id, CGX_COMMAND_REG, req);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* Ensure command is completed without errors */
103962306a36Sopenharmony_ci	if (!wait_event_timeout(lmac->wq_cmd_cmplt, !lmac->cmd_pend,
104062306a36Sopenharmony_ci				msecs_to_jiffies(CGX_CMD_TIMEOUT))) {
104162306a36Sopenharmony_ci		dev = &cgx->pdev->dev;
104262306a36Sopenharmony_ci		dev_err(dev, "cgx port %d:%d cmd %lld timeout\n",
104362306a36Sopenharmony_ci			cgx->cgx_id, lmac->lmac_id, FIELD_GET(CMDREG_ID, req));
104462306a36Sopenharmony_ci		err = LMAC_AF_ERR_CMD_TIMEOUT;
104562306a36Sopenharmony_ci		goto unlock;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	/* we have a valid command response */
104962306a36Sopenharmony_ci	smp_rmb(); /* Ensure the latest updates are visible */
105062306a36Sopenharmony_ci	*resp = lmac->resp;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ciunlock:
105362306a36Sopenharmony_ci	mutex_unlock(&lmac->cmd_lock);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	return err;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ciint cgx_fwi_cmd_generic(u64 req, u64 *resp, struct cgx *cgx, int lmac_id)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	struct lmac *lmac;
106162306a36Sopenharmony_ci	int err;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	lmac = lmac_pdata(lmac_id, cgx);
106462306a36Sopenharmony_ci	if (!lmac)
106562306a36Sopenharmony_ci		return -ENODEV;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	err = cgx_fwi_cmd_send(req, resp, lmac);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Check for valid response */
107062306a36Sopenharmony_ci	if (!err) {
107162306a36Sopenharmony_ci		if (FIELD_GET(EVTREG_STAT, *resp) == CGX_STAT_FAIL)
107262306a36Sopenharmony_ci			return -EIO;
107362306a36Sopenharmony_ci		else
107462306a36Sopenharmony_ci			return 0;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	return err;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic int cgx_link_usertable_index_map(int speed)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	switch (speed) {
108362306a36Sopenharmony_ci	case SPEED_10:
108462306a36Sopenharmony_ci		return CGX_LINK_10M;
108562306a36Sopenharmony_ci	case SPEED_100:
108662306a36Sopenharmony_ci		return CGX_LINK_100M;
108762306a36Sopenharmony_ci	case SPEED_1000:
108862306a36Sopenharmony_ci		return CGX_LINK_1G;
108962306a36Sopenharmony_ci	case SPEED_2500:
109062306a36Sopenharmony_ci		return CGX_LINK_2HG;
109162306a36Sopenharmony_ci	case SPEED_5000:
109262306a36Sopenharmony_ci		return CGX_LINK_5G;
109362306a36Sopenharmony_ci	case SPEED_10000:
109462306a36Sopenharmony_ci		return CGX_LINK_10G;
109562306a36Sopenharmony_ci	case SPEED_20000:
109662306a36Sopenharmony_ci		return CGX_LINK_20G;
109762306a36Sopenharmony_ci	case SPEED_25000:
109862306a36Sopenharmony_ci		return CGX_LINK_25G;
109962306a36Sopenharmony_ci	case SPEED_40000:
110062306a36Sopenharmony_ci		return CGX_LINK_40G;
110162306a36Sopenharmony_ci	case SPEED_50000:
110262306a36Sopenharmony_ci		return CGX_LINK_50G;
110362306a36Sopenharmony_ci	case 80000:
110462306a36Sopenharmony_ci		return CGX_LINK_80G;
110562306a36Sopenharmony_ci	case SPEED_100000:
110662306a36Sopenharmony_ci		return CGX_LINK_100G;
110762306a36Sopenharmony_ci	case SPEED_UNKNOWN:
110862306a36Sopenharmony_ci		return CGX_LINK_NONE;
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci	return CGX_LINK_NONE;
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic void set_mod_args(struct cgx_set_link_mode_args *args,
111462306a36Sopenharmony_ci			 u32 speed, u8 duplex, u8 autoneg, u64 mode)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	/* Fill default values incase of user did not pass
111762306a36Sopenharmony_ci	 * valid parameters
111862306a36Sopenharmony_ci	 */
111962306a36Sopenharmony_ci	if (args->duplex == DUPLEX_UNKNOWN)
112062306a36Sopenharmony_ci		args->duplex = duplex;
112162306a36Sopenharmony_ci	if (args->speed == SPEED_UNKNOWN)
112262306a36Sopenharmony_ci		args->speed = speed;
112362306a36Sopenharmony_ci	if (args->an == AUTONEG_UNKNOWN)
112462306a36Sopenharmony_ci		args->an = autoneg;
112562306a36Sopenharmony_ci	args->mode = mode;
112662306a36Sopenharmony_ci	args->ports = 0;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_cistatic void otx2_map_ethtool_link_modes(u64 bitmask,
113062306a36Sopenharmony_ci					struct cgx_set_link_mode_args *args)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	switch (bitmask) {
113362306a36Sopenharmony_ci	case ETHTOOL_LINK_MODE_10baseT_Half_BIT:
113462306a36Sopenharmony_ci		set_mod_args(args, 10, 1, 1, BIT_ULL(CGX_MODE_SGMII));
113562306a36Sopenharmony_ci		break;
113662306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_10baseT_Full_BIT:
113762306a36Sopenharmony_ci		set_mod_args(args, 10, 0, 1, BIT_ULL(CGX_MODE_SGMII));
113862306a36Sopenharmony_ci		break;
113962306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_100baseT_Half_BIT:
114062306a36Sopenharmony_ci		set_mod_args(args, 100, 1, 1, BIT_ULL(CGX_MODE_SGMII));
114162306a36Sopenharmony_ci		break;
114262306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_100baseT_Full_BIT:
114362306a36Sopenharmony_ci		set_mod_args(args, 100, 0, 1, BIT_ULL(CGX_MODE_SGMII));
114462306a36Sopenharmony_ci		break;
114562306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_1000baseT_Half_BIT:
114662306a36Sopenharmony_ci		set_mod_args(args, 1000, 1, 1, BIT_ULL(CGX_MODE_SGMII));
114762306a36Sopenharmony_ci		break;
114862306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_1000baseT_Full_BIT:
114962306a36Sopenharmony_ci		set_mod_args(args, 1000, 0, 1, BIT_ULL(CGX_MODE_SGMII));
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_1000baseX_Full_BIT:
115262306a36Sopenharmony_ci		set_mod_args(args, 1000, 0, 0, BIT_ULL(CGX_MODE_1000_BASEX));
115362306a36Sopenharmony_ci		break;
115462306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_10000baseT_Full_BIT:
115562306a36Sopenharmony_ci		set_mod_args(args, 1000, 0, 1, BIT_ULL(CGX_MODE_QSGMII));
115662306a36Sopenharmony_ci		break;
115762306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_10000baseSR_Full_BIT:
115862306a36Sopenharmony_ci		set_mod_args(args, 10000, 0, 0, BIT_ULL(CGX_MODE_10G_C2C));
115962306a36Sopenharmony_ci		break;
116062306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_10000baseLR_Full_BIT:
116162306a36Sopenharmony_ci		set_mod_args(args, 10000, 0, 0, BIT_ULL(CGX_MODE_10G_C2M));
116262306a36Sopenharmony_ci		break;
116362306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_10000baseKR_Full_BIT:
116462306a36Sopenharmony_ci		set_mod_args(args, 10000, 0, 1, BIT_ULL(CGX_MODE_10G_KR));
116562306a36Sopenharmony_ci		break;
116662306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_25000baseSR_Full_BIT:
116762306a36Sopenharmony_ci		set_mod_args(args, 25000, 0, 0, BIT_ULL(CGX_MODE_25G_C2C));
116862306a36Sopenharmony_ci		break;
116962306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_25000baseCR_Full_BIT:
117062306a36Sopenharmony_ci		set_mod_args(args, 25000, 0, 1, BIT_ULL(CGX_MODE_25G_CR));
117162306a36Sopenharmony_ci		break;
117262306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_25000baseKR_Full_BIT:
117362306a36Sopenharmony_ci		set_mod_args(args, 25000, 0, 1, BIT_ULL(CGX_MODE_25G_KR));
117462306a36Sopenharmony_ci		break;
117562306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT:
117662306a36Sopenharmony_ci		set_mod_args(args, 40000, 0, 0, BIT_ULL(CGX_MODE_40G_C2C));
117762306a36Sopenharmony_ci		break;
117862306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT:
117962306a36Sopenharmony_ci		set_mod_args(args, 40000, 0, 0, BIT_ULL(CGX_MODE_40G_C2M));
118062306a36Sopenharmony_ci		break;
118162306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT:
118262306a36Sopenharmony_ci		set_mod_args(args, 40000, 0, 1, BIT_ULL(CGX_MODE_40G_CR4));
118362306a36Sopenharmony_ci		break;
118462306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT:
118562306a36Sopenharmony_ci		set_mod_args(args, 40000, 0, 1, BIT_ULL(CGX_MODE_40G_KR4));
118662306a36Sopenharmony_ci		break;
118762306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_50000baseSR_Full_BIT:
118862306a36Sopenharmony_ci		set_mod_args(args, 50000, 0, 0, BIT_ULL(CGX_MODE_50G_C2C));
118962306a36Sopenharmony_ci		break;
119062306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT:
119162306a36Sopenharmony_ci		set_mod_args(args, 50000, 0, 0, BIT_ULL(CGX_MODE_50G_C2M));
119262306a36Sopenharmony_ci		break;
119362306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_50000baseCR_Full_BIT:
119462306a36Sopenharmony_ci		set_mod_args(args, 50000, 0, 1, BIT_ULL(CGX_MODE_50G_CR));
119562306a36Sopenharmony_ci		break;
119662306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_50000baseKR_Full_BIT:
119762306a36Sopenharmony_ci		set_mod_args(args, 50000, 0, 1, BIT_ULL(CGX_MODE_50G_KR));
119862306a36Sopenharmony_ci		break;
119962306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT:
120062306a36Sopenharmony_ci		set_mod_args(args, 100000, 0, 0, BIT_ULL(CGX_MODE_100G_C2C));
120162306a36Sopenharmony_ci		break;
120262306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT:
120362306a36Sopenharmony_ci		set_mod_args(args, 100000, 0, 0, BIT_ULL(CGX_MODE_100G_C2M));
120462306a36Sopenharmony_ci		break;
120562306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT:
120662306a36Sopenharmony_ci		set_mod_args(args, 100000, 0, 1, BIT_ULL(CGX_MODE_100G_CR4));
120762306a36Sopenharmony_ci		break;
120862306a36Sopenharmony_ci	case  ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT:
120962306a36Sopenharmony_ci		set_mod_args(args, 100000, 0, 1, BIT_ULL(CGX_MODE_100G_KR4));
121062306a36Sopenharmony_ci		break;
121162306a36Sopenharmony_ci	default:
121262306a36Sopenharmony_ci		set_mod_args(args, 0, 1, 0, BIT_ULL(CGX_MODE_MAX));
121362306a36Sopenharmony_ci		break;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_cistatic inline void link_status_user_format(u64 lstat,
121862306a36Sopenharmony_ci					   struct cgx_link_user_info *linfo,
121962306a36Sopenharmony_ci					   struct cgx *cgx, u8 lmac_id)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	const char *lmac_string;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat);
122462306a36Sopenharmony_ci	linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat);
122562306a36Sopenharmony_ci	linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)];
122662306a36Sopenharmony_ci	linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat);
122762306a36Sopenharmony_ci	linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat);
122862306a36Sopenharmony_ci	linfo->lmac_type_id = FIELD_GET(RESP_LINKSTAT_LMAC_TYPE, lstat);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (linfo->lmac_type_id >= LMAC_MODE_MAX) {
123162306a36Sopenharmony_ci		dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d",
123262306a36Sopenharmony_ci			linfo->lmac_type_id, cgx->cgx_id, lmac_id);
123362306a36Sopenharmony_ci		strncpy(linfo->lmac_type, "Unknown", LMACTYPE_STR_LEN - 1);
123462306a36Sopenharmony_ci		return;
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	lmac_string = cgx_lmactype_string[linfo->lmac_type_id];
123862306a36Sopenharmony_ci	strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1);
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci/* Hardware event handlers */
124262306a36Sopenharmony_cistatic inline void cgx_link_change_handler(u64 lstat,
124362306a36Sopenharmony_ci					   struct lmac *lmac)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	struct cgx_link_user_info *linfo;
124662306a36Sopenharmony_ci	struct cgx *cgx = lmac->cgx;
124762306a36Sopenharmony_ci	struct cgx_link_event event;
124862306a36Sopenharmony_ci	struct device *dev;
124962306a36Sopenharmony_ci	int err_type;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	dev = &cgx->pdev->dev;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	link_status_user_format(lstat, &event.link_uinfo, cgx, lmac->lmac_id);
125462306a36Sopenharmony_ci	err_type = FIELD_GET(RESP_LINKSTAT_ERRTYPE, lstat);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	event.cgx_id = cgx->cgx_id;
125762306a36Sopenharmony_ci	event.lmac_id = lmac->lmac_id;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/* update the local copy of link status */
126062306a36Sopenharmony_ci	lmac->link_info = event.link_uinfo;
126162306a36Sopenharmony_ci	linfo = &lmac->link_info;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	if (err_type == CGX_ERR_SPEED_CHANGE_INVALID)
126462306a36Sopenharmony_ci		return;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	/* Ensure callback doesn't get unregistered until we finish it */
126762306a36Sopenharmony_ci	spin_lock(&lmac->event_cb_lock);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (!lmac->event_cb.notify_link_chg) {
127062306a36Sopenharmony_ci		dev_dbg(dev, "cgx port %d:%d Link change handler null",
127162306a36Sopenharmony_ci			cgx->cgx_id, lmac->lmac_id);
127262306a36Sopenharmony_ci		if (err_type != CGX_ERR_NONE) {
127362306a36Sopenharmony_ci			dev_err(dev, "cgx port %d:%d Link error %d\n",
127462306a36Sopenharmony_ci				cgx->cgx_id, lmac->lmac_id, err_type);
127562306a36Sopenharmony_ci		}
127662306a36Sopenharmony_ci		dev_info(dev, "cgx port %d:%d Link is %s %d Mbps\n",
127762306a36Sopenharmony_ci			 cgx->cgx_id, lmac->lmac_id,
127862306a36Sopenharmony_ci			 linfo->link_up ? "UP" : "DOWN", linfo->speed);
127962306a36Sopenharmony_ci		goto err;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (lmac->event_cb.notify_link_chg(&event, lmac->event_cb.data))
128362306a36Sopenharmony_ci		dev_err(dev, "event notification failure\n");
128462306a36Sopenharmony_cierr:
128562306a36Sopenharmony_ci	spin_unlock(&lmac->event_cb_lock);
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cistatic inline bool cgx_cmdresp_is_linkevent(u64 event)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	u8 id;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	id = FIELD_GET(EVTREG_ID, event);
129362306a36Sopenharmony_ci	if (id == CGX_CMD_LINK_BRING_UP ||
129462306a36Sopenharmony_ci	    id == CGX_CMD_LINK_BRING_DOWN ||
129562306a36Sopenharmony_ci	    id == CGX_CMD_MODE_CHANGE)
129662306a36Sopenharmony_ci		return true;
129762306a36Sopenharmony_ci	else
129862306a36Sopenharmony_ci		return false;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_cistatic inline bool cgx_event_is_linkevent(u64 event)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	if (FIELD_GET(EVTREG_ID, event) == CGX_EVT_LINK_CHANGE)
130462306a36Sopenharmony_ci		return true;
130562306a36Sopenharmony_ci	else
130662306a36Sopenharmony_ci		return false;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic irqreturn_t cgx_fwi_event_handler(int irq, void *data)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	u64 event, offset, clear_bit;
131262306a36Sopenharmony_ci	struct lmac *lmac = data;
131362306a36Sopenharmony_ci	struct cgx *cgx;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	cgx = lmac->cgx;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	/* Clear SW_INT for RPM and CMR_INT for CGX */
131862306a36Sopenharmony_ci	offset     = cgx->mac_ops->int_register;
131962306a36Sopenharmony_ci	clear_bit  = cgx->mac_ops->int_ena_bit;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	event = cgx_read(cgx, lmac->lmac_id, CGX_EVENT_REG);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (!FIELD_GET(EVTREG_ACK, event))
132462306a36Sopenharmony_ci		return IRQ_NONE;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	switch (FIELD_GET(EVTREG_EVT_TYPE, event)) {
132762306a36Sopenharmony_ci	case CGX_EVT_CMD_RESP:
132862306a36Sopenharmony_ci		/* Copy the response. Since only one command is active at a
132962306a36Sopenharmony_ci		 * time, there is no way a response can get overwritten
133062306a36Sopenharmony_ci		 */
133162306a36Sopenharmony_ci		lmac->resp = event;
133262306a36Sopenharmony_ci		/* Ensure response is updated before thread context starts */
133362306a36Sopenharmony_ci		smp_wmb();
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci		/* There wont be separate events for link change initiated from
133662306a36Sopenharmony_ci		 * software; Hence report the command responses as events
133762306a36Sopenharmony_ci		 */
133862306a36Sopenharmony_ci		if (cgx_cmdresp_is_linkevent(event))
133962306a36Sopenharmony_ci			cgx_link_change_handler(event, lmac);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci		/* Release thread waiting for completion  */
134262306a36Sopenharmony_ci		lmac->cmd_pend = false;
134362306a36Sopenharmony_ci		wake_up(&lmac->wq_cmd_cmplt);
134462306a36Sopenharmony_ci		break;
134562306a36Sopenharmony_ci	case CGX_EVT_ASYNC:
134662306a36Sopenharmony_ci		if (cgx_event_is_linkevent(event))
134762306a36Sopenharmony_ci			cgx_link_change_handler(event, lmac);
134862306a36Sopenharmony_ci		break;
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	/* Any new event or command response will be posted by firmware
135262306a36Sopenharmony_ci	 * only after the current status is acked.
135362306a36Sopenharmony_ci	 * Ack the interrupt register as well.
135462306a36Sopenharmony_ci	 */
135562306a36Sopenharmony_ci	cgx_write(lmac->cgx, lmac->lmac_id, CGX_EVENT_REG, 0);
135662306a36Sopenharmony_ci	cgx_write(lmac->cgx, lmac->lmac_id, offset, clear_bit);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	return IRQ_HANDLED;
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci/* APIs for PHY management using CGX firmware interface */
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci/* callback registration for hardware events like link change */
136462306a36Sopenharmony_ciint cgx_lmac_evh_register(struct cgx_event_cb *cb, void *cgxd, int lmac_id)
136562306a36Sopenharmony_ci{
136662306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
136762306a36Sopenharmony_ci	struct lmac *lmac;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	lmac = lmac_pdata(lmac_id, cgx);
137062306a36Sopenharmony_ci	if (!lmac)
137162306a36Sopenharmony_ci		return -ENODEV;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	lmac->event_cb = *cb;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	return 0;
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ciint cgx_lmac_evh_unregister(void *cgxd, int lmac_id)
137962306a36Sopenharmony_ci{
138062306a36Sopenharmony_ci	struct lmac *lmac;
138162306a36Sopenharmony_ci	unsigned long flags;
138262306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	lmac = lmac_pdata(lmac_id, cgx);
138562306a36Sopenharmony_ci	if (!lmac)
138662306a36Sopenharmony_ci		return -ENODEV;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	spin_lock_irqsave(&lmac->event_cb_lock, flags);
138962306a36Sopenharmony_ci	lmac->event_cb.notify_link_chg = NULL;
139062306a36Sopenharmony_ci	lmac->event_cb.data = NULL;
139162306a36Sopenharmony_ci	spin_unlock_irqrestore(&lmac->event_cb_lock, flags);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	return 0;
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ciint cgx_get_fwdata_base(u64 *base)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	u64 req = 0, resp;
139962306a36Sopenharmony_ci	struct cgx *cgx;
140062306a36Sopenharmony_ci	int first_lmac;
140162306a36Sopenharmony_ci	int err;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	cgx = list_first_entry_or_null(&cgx_list, struct cgx, cgx_list);
140462306a36Sopenharmony_ci	if (!cgx)
140562306a36Sopenharmony_ci		return -ENXIO;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac);
140862306a36Sopenharmony_ci	req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FWD_BASE, req);
140962306a36Sopenharmony_ci	err = cgx_fwi_cmd_generic(req, &resp, cgx, first_lmac);
141062306a36Sopenharmony_ci	if (!err)
141162306a36Sopenharmony_ci		*base = FIELD_GET(RESP_FWD_BASE, resp);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	return err;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ciint cgx_set_link_mode(void *cgxd, struct cgx_set_link_mode_args args,
141762306a36Sopenharmony_ci		      int cgx_id, int lmac_id)
141862306a36Sopenharmony_ci{
141962306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
142062306a36Sopenharmony_ci	u64 req = 0, resp;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	if (!cgx)
142362306a36Sopenharmony_ci		return -ENODEV;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (args.mode)
142662306a36Sopenharmony_ci		otx2_map_ethtool_link_modes(args.mode, &args);
142762306a36Sopenharmony_ci	if (!args.speed && args.duplex && !args.an)
142862306a36Sopenharmony_ci		return -EINVAL;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	req = FIELD_SET(CMDREG_ID, CGX_CMD_MODE_CHANGE, req);
143162306a36Sopenharmony_ci	req = FIELD_SET(CMDMODECHANGE_SPEED,
143262306a36Sopenharmony_ci			cgx_link_usertable_index_map(args.speed), req);
143362306a36Sopenharmony_ci	req = FIELD_SET(CMDMODECHANGE_DUPLEX, args.duplex, req);
143462306a36Sopenharmony_ci	req = FIELD_SET(CMDMODECHANGE_AN, args.an, req);
143562306a36Sopenharmony_ci	req = FIELD_SET(CMDMODECHANGE_PORT, args.ports, req);
143662306a36Sopenharmony_ci	req = FIELD_SET(CMDMODECHANGE_FLAGS, args.mode, req);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id);
143962306a36Sopenharmony_ci}
144062306a36Sopenharmony_ciint cgx_set_fec(u64 fec, int cgx_id, int lmac_id)
144162306a36Sopenharmony_ci{
144262306a36Sopenharmony_ci	u64 req = 0, resp;
144362306a36Sopenharmony_ci	struct cgx *cgx;
144462306a36Sopenharmony_ci	int err = 0;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	cgx = cgx_get_pdata(cgx_id);
144762306a36Sopenharmony_ci	if (!cgx)
144862306a36Sopenharmony_ci		return -ENXIO;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	req = FIELD_SET(CMDREG_ID, CGX_CMD_SET_FEC, req);
145162306a36Sopenharmony_ci	req = FIELD_SET(CMDSETFEC, fec, req);
145262306a36Sopenharmony_ci	err = cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id);
145362306a36Sopenharmony_ci	if (err)
145462306a36Sopenharmony_ci		return err;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	cgx->lmac_idmap[lmac_id]->link_info.fec =
145762306a36Sopenharmony_ci			FIELD_GET(RESP_LINKSTAT_FEC, resp);
145862306a36Sopenharmony_ci	return cgx->lmac_idmap[lmac_id]->link_info.fec;
145962306a36Sopenharmony_ci}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ciint cgx_get_phy_fec_stats(void *cgxd, int lmac_id)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
146462306a36Sopenharmony_ci	u64 req = 0, resp;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (!cgx)
146762306a36Sopenharmony_ci		return -ENODEV;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_PHY_FEC_STATS, req);
147062306a36Sopenharmony_ci	return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id);
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	u64 req = 0;
147662306a36Sopenharmony_ci	u64 resp;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	if (enable) {
147962306a36Sopenharmony_ci		req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_UP, req);
148062306a36Sopenharmony_ci		/* On CN10K firmware offloads link bring up/down operations to ECP
148162306a36Sopenharmony_ci		 * On Octeontx2 link operations are handled by firmware itself
148262306a36Sopenharmony_ci		 * which can cause mbox errors so configure maximum time firmware
148362306a36Sopenharmony_ci		 * poll for Link as 1000 ms
148462306a36Sopenharmony_ci		 */
148562306a36Sopenharmony_ci		if (!is_dev_rpm(cgx))
148662306a36Sopenharmony_ci			req = FIELD_SET(LINKCFG_TIMEOUT, 1000, req);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	} else {
148962306a36Sopenharmony_ci		req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req);
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci	return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id);
149262306a36Sopenharmony_ci}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_cistatic inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx)
149562306a36Sopenharmony_ci{
149662306a36Sopenharmony_ci	int first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac);
149762306a36Sopenharmony_ci	u64 req = 0;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FW_VER, req);
150062306a36Sopenharmony_ci	return cgx_fwi_cmd_generic(req, resp, cgx, first_lmac);
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic int cgx_lmac_verify_fwi_version(struct cgx *cgx)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct device *dev = &cgx->pdev->dev;
150662306a36Sopenharmony_ci	int major_ver, minor_ver;
150762306a36Sopenharmony_ci	u64 resp;
150862306a36Sopenharmony_ci	int err;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	if (!cgx->lmac_count)
151162306a36Sopenharmony_ci		return 0;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	err = cgx_fwi_read_version(&resp, cgx);
151462306a36Sopenharmony_ci	if (err)
151562306a36Sopenharmony_ci		return err;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	major_ver = FIELD_GET(RESP_MAJOR_VER, resp);
151862306a36Sopenharmony_ci	minor_ver = FIELD_GET(RESP_MINOR_VER, resp);
151962306a36Sopenharmony_ci	dev_dbg(dev, "Firmware command interface version = %d.%d\n",
152062306a36Sopenharmony_ci		major_ver, minor_ver);
152162306a36Sopenharmony_ci	if (major_ver != CGX_FIRMWARE_MAJOR_VER)
152262306a36Sopenharmony_ci		return -EIO;
152362306a36Sopenharmony_ci	else
152462306a36Sopenharmony_ci		return 0;
152562306a36Sopenharmony_ci}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_cistatic void cgx_lmac_linkup_work(struct work_struct *work)
152862306a36Sopenharmony_ci{
152962306a36Sopenharmony_ci	struct cgx *cgx = container_of(work, struct cgx, cgx_cmd_work);
153062306a36Sopenharmony_ci	struct device *dev = &cgx->pdev->dev;
153162306a36Sopenharmony_ci	int i, err;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	/* Do Link up for all the enabled lmacs */
153462306a36Sopenharmony_ci	for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) {
153562306a36Sopenharmony_ci		err = cgx_fwi_link_change(cgx, i, true);
153662306a36Sopenharmony_ci		if (err)
153762306a36Sopenharmony_ci			dev_info(dev, "cgx port %d:%d Link up command failed\n",
153862306a36Sopenharmony_ci				 cgx->cgx_id, i);
153962306a36Sopenharmony_ci	}
154062306a36Sopenharmony_ci}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ciint cgx_lmac_linkup_start(void *cgxd)
154362306a36Sopenharmony_ci{
154462306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	if (!cgx)
154762306a36Sopenharmony_ci		return -ENODEV;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	queue_work(cgx->cgx_cmd_workq, &cgx->cgx_cmd_work);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	return 0;
155262306a36Sopenharmony_ci}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ciint cgx_lmac_reset(void *cgxd, int lmac_id, u8 pf_req_flr)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
155762306a36Sopenharmony_ci	u64 cfg;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	if (!is_lmac_valid(cgx, lmac_id))
156062306a36Sopenharmony_ci		return -ENODEV;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	/* Resetting PFC related CSRs */
156362306a36Sopenharmony_ci	cfg = 0xff;
156462306a36Sopenharmony_ci	cgx_write(cgxd, lmac_id, CGXX_CMRX_RX_LOGL_XON, cfg);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	if (pf_req_flr)
156762306a36Sopenharmony_ci		cgx_lmac_internal_loopback(cgxd, lmac_id, false);
156862306a36Sopenharmony_ci	return 0;
156962306a36Sopenharmony_ci}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_cistatic int cgx_configure_interrupt(struct cgx *cgx, struct lmac *lmac,
157262306a36Sopenharmony_ci				   int cnt, bool req_free)
157362306a36Sopenharmony_ci{
157462306a36Sopenharmony_ci	struct mac_ops *mac_ops = cgx->mac_ops;
157562306a36Sopenharmony_ci	u64 offset, ena_bit;
157662306a36Sopenharmony_ci	unsigned int irq;
157762306a36Sopenharmony_ci	int err;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	irq      = pci_irq_vector(cgx->pdev, mac_ops->lmac_fwi +
158062306a36Sopenharmony_ci				  cnt * mac_ops->irq_offset);
158162306a36Sopenharmony_ci	offset   = mac_ops->int_set_reg;
158262306a36Sopenharmony_ci	ena_bit  = mac_ops->int_ena_bit;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	if (req_free) {
158562306a36Sopenharmony_ci		free_irq(irq, lmac);
158662306a36Sopenharmony_ci		return 0;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	err = request_irq(irq, cgx_fwi_event_handler, 0, lmac->name, lmac);
159062306a36Sopenharmony_ci	if (err)
159162306a36Sopenharmony_ci		return err;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	/* Enable interrupt */
159462306a36Sopenharmony_ci	cgx_write(cgx, lmac->lmac_id, offset, ena_bit);
159562306a36Sopenharmony_ci	return 0;
159662306a36Sopenharmony_ci}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ciint cgx_get_nr_lmacs(void *cgxd)
159962306a36Sopenharmony_ci{
160062306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0x7ULL;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ciu8 cgx_get_lmacid(void *cgxd, u8 lmac_index)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	return cgx->lmac_idmap[lmac_index]->lmac_id;
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ciunsigned long cgx_get_lmac_bmap(void *cgxd)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	struct cgx *cgx = cgxd;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	return cgx->lmac_bmap;
161762306a36Sopenharmony_ci}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_cistatic int cgx_lmac_init(struct cgx *cgx)
162062306a36Sopenharmony_ci{
162162306a36Sopenharmony_ci	struct lmac *lmac;
162262306a36Sopenharmony_ci	u64 lmac_list;
162362306a36Sopenharmony_ci	int i, err;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	/* lmac_list specifies which lmacs are enabled
162662306a36Sopenharmony_ci	 * when bit n is set to 1, LMAC[n] is enabled
162762306a36Sopenharmony_ci	 */
162862306a36Sopenharmony_ci	if (cgx->mac_ops->non_contiguous_serdes_lane) {
162962306a36Sopenharmony_ci		if (is_dev_rpm2(cgx))
163062306a36Sopenharmony_ci			lmac_list =
163162306a36Sopenharmony_ci				cgx_read(cgx, 0, RPM2_CMRX_RX_LMACS) & 0xFFULL;
163262306a36Sopenharmony_ci		else
163362306a36Sopenharmony_ci			lmac_list =
163462306a36Sopenharmony_ci				cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0xFULL;
163562306a36Sopenharmony_ci	}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	if (cgx->lmac_count > cgx->max_lmac_per_mac)
163862306a36Sopenharmony_ci		cgx->lmac_count = cgx->max_lmac_per_mac;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	for (i = 0; i < cgx->lmac_count; i++) {
164162306a36Sopenharmony_ci		lmac = kzalloc(sizeof(struct lmac), GFP_KERNEL);
164262306a36Sopenharmony_ci		if (!lmac)
164362306a36Sopenharmony_ci			return -ENOMEM;
164462306a36Sopenharmony_ci		lmac->name = kcalloc(1, sizeof("cgx_fwi_xxx_yyy"), GFP_KERNEL);
164562306a36Sopenharmony_ci		if (!lmac->name) {
164662306a36Sopenharmony_ci			err = -ENOMEM;
164762306a36Sopenharmony_ci			goto err_lmac_free;
164862306a36Sopenharmony_ci		}
164962306a36Sopenharmony_ci		sprintf(lmac->name, "cgx_fwi_%d_%d", cgx->cgx_id, i);
165062306a36Sopenharmony_ci		if (cgx->mac_ops->non_contiguous_serdes_lane) {
165162306a36Sopenharmony_ci			lmac->lmac_id = __ffs64(lmac_list);
165262306a36Sopenharmony_ci			lmac_list   &= ~BIT_ULL(lmac->lmac_id);
165362306a36Sopenharmony_ci		} else {
165462306a36Sopenharmony_ci			lmac->lmac_id = i;
165562306a36Sopenharmony_ci		}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci		lmac->cgx = cgx;
165862306a36Sopenharmony_ci		lmac->mac_to_index_bmap.max =
165962306a36Sopenharmony_ci				cgx->mac_ops->dmac_filter_count /
166062306a36Sopenharmony_ci				cgx->lmac_count;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci		err = rvu_alloc_bitmap(&lmac->mac_to_index_bmap);
166362306a36Sopenharmony_ci		if (err)
166462306a36Sopenharmony_ci			goto err_name_free;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci		/* Reserve first entry for default MAC address */
166762306a36Sopenharmony_ci		set_bit(0, lmac->mac_to_index_bmap.bmap);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci		lmac->rx_fc_pfvf_bmap.max = 128;
167062306a36Sopenharmony_ci		err = rvu_alloc_bitmap(&lmac->rx_fc_pfvf_bmap);
167162306a36Sopenharmony_ci		if (err)
167262306a36Sopenharmony_ci			goto err_dmac_bmap_free;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci		lmac->tx_fc_pfvf_bmap.max = 128;
167562306a36Sopenharmony_ci		err = rvu_alloc_bitmap(&lmac->tx_fc_pfvf_bmap);
167662306a36Sopenharmony_ci		if (err)
167762306a36Sopenharmony_ci			goto err_rx_fc_bmap_free;
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci		init_waitqueue_head(&lmac->wq_cmd_cmplt);
168062306a36Sopenharmony_ci		mutex_init(&lmac->cmd_lock);
168162306a36Sopenharmony_ci		spin_lock_init(&lmac->event_cb_lock);
168262306a36Sopenharmony_ci		err = cgx_configure_interrupt(cgx, lmac, lmac->lmac_id, false);
168362306a36Sopenharmony_ci		if (err)
168462306a36Sopenharmony_ci			goto err_bitmap_free;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci		/* Add reference */
168762306a36Sopenharmony_ci		cgx->lmac_idmap[lmac->lmac_id] = lmac;
168862306a36Sopenharmony_ci		set_bit(lmac->lmac_id, &cgx->lmac_bmap);
168962306a36Sopenharmony_ci		cgx->mac_ops->mac_pause_frm_config(cgx, lmac->lmac_id, true);
169062306a36Sopenharmony_ci		lmac->lmac_type = cgx->mac_ops->get_lmac_type(cgx, lmac->lmac_id);
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	return cgx_lmac_verify_fwi_version(cgx);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cierr_bitmap_free:
169662306a36Sopenharmony_ci	rvu_free_bitmap(&lmac->tx_fc_pfvf_bmap);
169762306a36Sopenharmony_cierr_rx_fc_bmap_free:
169862306a36Sopenharmony_ci	rvu_free_bitmap(&lmac->rx_fc_pfvf_bmap);
169962306a36Sopenharmony_cierr_dmac_bmap_free:
170062306a36Sopenharmony_ci	rvu_free_bitmap(&lmac->mac_to_index_bmap);
170162306a36Sopenharmony_cierr_name_free:
170262306a36Sopenharmony_ci	kfree(lmac->name);
170362306a36Sopenharmony_cierr_lmac_free:
170462306a36Sopenharmony_ci	kfree(lmac);
170562306a36Sopenharmony_ci	return err;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic int cgx_lmac_exit(struct cgx *cgx)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	struct lmac *lmac;
171162306a36Sopenharmony_ci	int i;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	if (cgx->cgx_cmd_workq) {
171462306a36Sopenharmony_ci		destroy_workqueue(cgx->cgx_cmd_workq);
171562306a36Sopenharmony_ci		cgx->cgx_cmd_workq = NULL;
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	/* Free all lmac related resources */
171962306a36Sopenharmony_ci	for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) {
172062306a36Sopenharmony_ci		lmac = cgx->lmac_idmap[i];
172162306a36Sopenharmony_ci		if (!lmac)
172262306a36Sopenharmony_ci			continue;
172362306a36Sopenharmony_ci		cgx->mac_ops->mac_pause_frm_config(cgx, lmac->lmac_id, false);
172462306a36Sopenharmony_ci		cgx_configure_interrupt(cgx, lmac, lmac->lmac_id, true);
172562306a36Sopenharmony_ci		kfree(lmac->mac_to_index_bmap.bmap);
172662306a36Sopenharmony_ci		kfree(lmac->name);
172762306a36Sopenharmony_ci		kfree(lmac);
172862306a36Sopenharmony_ci	}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	return 0;
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_cistatic void cgx_populate_features(struct cgx *cgx)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	u64 cfg;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	cfg = cgx_read(cgx, 0, CGX_CONST);
173862306a36Sopenharmony_ci	cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg);
173962306a36Sopenharmony_ci	cgx->max_lmac_per_mac = FIELD_GET(CGX_CONST_MAX_LMACS, cfg);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	if (is_dev_rpm(cgx))
174262306a36Sopenharmony_ci		cgx->hw_features = (RVU_LMAC_FEAT_DMACF | RVU_MAC_RPM |
174362306a36Sopenharmony_ci				    RVU_LMAC_FEAT_FC | RVU_LMAC_FEAT_PTP);
174462306a36Sopenharmony_ci	else
174562306a36Sopenharmony_ci		cgx->hw_features = (RVU_LMAC_FEAT_FC  | RVU_LMAC_FEAT_HIGIG2 |
174662306a36Sopenharmony_ci				    RVU_LMAC_FEAT_PTP | RVU_LMAC_FEAT_DMACF);
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_cistatic u8 cgx_get_rxid_mapoffset(struct cgx *cgx)
175062306a36Sopenharmony_ci{
175162306a36Sopenharmony_ci	if (cgx->pdev->subsystem_device == PCI_SUBSYS_DEVID_CNF10KB_RPM ||
175262306a36Sopenharmony_ci	    is_dev_rpm2(cgx))
175362306a36Sopenharmony_ci		return 0x80;
175462306a36Sopenharmony_ci	else
175562306a36Sopenharmony_ci		return 0x60;
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_cistatic struct mac_ops	cgx_mac_ops    = {
175962306a36Sopenharmony_ci	.name		=       "cgx",
176062306a36Sopenharmony_ci	.csr_offset	=       0,
176162306a36Sopenharmony_ci	.lmac_offset    =       18,
176262306a36Sopenharmony_ci	.int_register	=       CGXX_CMRX_INT,
176362306a36Sopenharmony_ci	.int_set_reg	=       CGXX_CMRX_INT_ENA_W1S,
176462306a36Sopenharmony_ci	.irq_offset	=       9,
176562306a36Sopenharmony_ci	.int_ena_bit    =       FW_CGX_INT,
176662306a36Sopenharmony_ci	.lmac_fwi	=	CGX_LMAC_FWI,
176762306a36Sopenharmony_ci	.non_contiguous_serdes_lane = false,
176862306a36Sopenharmony_ci	.rx_stats_cnt   =       9,
176962306a36Sopenharmony_ci	.tx_stats_cnt   =       18,
177062306a36Sopenharmony_ci	.dmac_filter_count =    32,
177162306a36Sopenharmony_ci	.get_nr_lmacs	=	cgx_get_nr_lmacs,
177262306a36Sopenharmony_ci	.get_lmac_type  =       cgx_get_lmac_type,
177362306a36Sopenharmony_ci	.lmac_fifo_len	=	cgx_get_lmac_fifo_len,
177462306a36Sopenharmony_ci	.mac_lmac_intl_lbk =    cgx_lmac_internal_loopback,
177562306a36Sopenharmony_ci	.mac_get_rx_stats  =	cgx_get_rx_stats,
177662306a36Sopenharmony_ci	.mac_get_tx_stats  =	cgx_get_tx_stats,
177762306a36Sopenharmony_ci	.get_fec_stats	   =	cgx_get_fec_stats,
177862306a36Sopenharmony_ci	.mac_enadis_rx_pause_fwding =	cgx_lmac_enadis_rx_pause_fwding,
177962306a36Sopenharmony_ci	.mac_get_pause_frm_status =	cgx_lmac_get_pause_frm_status,
178062306a36Sopenharmony_ci	.mac_enadis_pause_frm =		cgx_lmac_enadis_pause_frm,
178162306a36Sopenharmony_ci	.mac_pause_frm_config =		cgx_lmac_pause_frm_config,
178262306a36Sopenharmony_ci	.mac_enadis_ptp_config =	cgx_lmac_ptp_config,
178362306a36Sopenharmony_ci	.mac_rx_tx_enable =		cgx_lmac_rx_tx_enable,
178462306a36Sopenharmony_ci	.mac_tx_enable =		cgx_lmac_tx_enable,
178562306a36Sopenharmony_ci	.pfc_config =                   cgx_lmac_pfc_config,
178662306a36Sopenharmony_ci	.mac_get_pfc_frm_cfg   =        cgx_lmac_get_pfc_frm_cfg,
178762306a36Sopenharmony_ci	.mac_reset   =			cgx_lmac_reset,
178862306a36Sopenharmony_ci};
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_cistatic int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
179362306a36Sopenharmony_ci	struct cgx *cgx;
179462306a36Sopenharmony_ci	int err, nvec;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	cgx = devm_kzalloc(dev, sizeof(*cgx), GFP_KERNEL);
179762306a36Sopenharmony_ci	if (!cgx)
179862306a36Sopenharmony_ci		return -ENOMEM;
179962306a36Sopenharmony_ci	cgx->pdev = pdev;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	pci_set_drvdata(pdev, cgx);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	/* Use mac_ops to get MAC specific features */
180462306a36Sopenharmony_ci	if (is_dev_rpm(cgx))
180562306a36Sopenharmony_ci		cgx->mac_ops = rpm_get_mac_ops(cgx);
180662306a36Sopenharmony_ci	else
180762306a36Sopenharmony_ci		cgx->mac_ops = &cgx_mac_ops;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	cgx->mac_ops->rxid_map_offset = cgx_get_rxid_mapoffset(cgx);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	err = pci_enable_device(pdev);
181262306a36Sopenharmony_ci	if (err) {
181362306a36Sopenharmony_ci		dev_err(dev, "Failed to enable PCI device\n");
181462306a36Sopenharmony_ci		pci_set_drvdata(pdev, NULL);
181562306a36Sopenharmony_ci		return err;
181662306a36Sopenharmony_ci	}
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	err = pci_request_regions(pdev, DRV_NAME);
181962306a36Sopenharmony_ci	if (err) {
182062306a36Sopenharmony_ci		dev_err(dev, "PCI request regions failed 0x%x\n", err);
182162306a36Sopenharmony_ci		goto err_disable_device;
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	/* MAP configuration registers */
182562306a36Sopenharmony_ci	cgx->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0);
182662306a36Sopenharmony_ci	if (!cgx->reg_base) {
182762306a36Sopenharmony_ci		dev_err(dev, "CGX: Cannot map CSR memory space, aborting\n");
182862306a36Sopenharmony_ci		err = -ENOMEM;
182962306a36Sopenharmony_ci		goto err_release_regions;
183062306a36Sopenharmony_ci	}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	cgx->lmac_count = cgx->mac_ops->get_nr_lmacs(cgx);
183362306a36Sopenharmony_ci	if (!cgx->lmac_count) {
183462306a36Sopenharmony_ci		dev_notice(dev, "CGX %d LMAC count is zero, skipping probe\n", cgx->cgx_id);
183562306a36Sopenharmony_ci		err = -EOPNOTSUPP;
183662306a36Sopenharmony_ci		goto err_release_regions;
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	nvec = pci_msix_vec_count(cgx->pdev);
184062306a36Sopenharmony_ci	err = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX);
184162306a36Sopenharmony_ci	if (err < 0 || err != nvec) {
184262306a36Sopenharmony_ci		dev_err(dev, "Request for %d msix vectors failed, err %d\n",
184362306a36Sopenharmony_ci			nvec, err);
184462306a36Sopenharmony_ci		goto err_release_regions;
184562306a36Sopenharmony_ci	}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	cgx->cgx_id = (pci_resource_start(pdev, PCI_CFG_REG_BAR_NUM) >> 24)
184862306a36Sopenharmony_ci		& CGX_ID_MASK;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	/* init wq for processing linkup requests */
185162306a36Sopenharmony_ci	INIT_WORK(&cgx->cgx_cmd_work, cgx_lmac_linkup_work);
185262306a36Sopenharmony_ci	cgx->cgx_cmd_workq = alloc_workqueue("cgx_cmd_workq", 0, 0);
185362306a36Sopenharmony_ci	if (!cgx->cgx_cmd_workq) {
185462306a36Sopenharmony_ci		dev_err(dev, "alloc workqueue failed for cgx cmd");
185562306a36Sopenharmony_ci		err = -ENOMEM;
185662306a36Sopenharmony_ci		goto err_free_irq_vectors;
185762306a36Sopenharmony_ci	}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	list_add(&cgx->cgx_list, &cgx_list);
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	cgx_populate_features(cgx);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	mutex_init(&cgx->lock);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	err = cgx_lmac_init(cgx);
186762306a36Sopenharmony_ci	if (err)
186862306a36Sopenharmony_ci		goto err_release_lmac;
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	return 0;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_cierr_release_lmac:
187362306a36Sopenharmony_ci	cgx_lmac_exit(cgx);
187462306a36Sopenharmony_ci	list_del(&cgx->cgx_list);
187562306a36Sopenharmony_cierr_free_irq_vectors:
187662306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
187762306a36Sopenharmony_cierr_release_regions:
187862306a36Sopenharmony_ci	pci_release_regions(pdev);
187962306a36Sopenharmony_cierr_disable_device:
188062306a36Sopenharmony_ci	pci_disable_device(pdev);
188162306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
188262306a36Sopenharmony_ci	return err;
188362306a36Sopenharmony_ci}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_cistatic void cgx_remove(struct pci_dev *pdev)
188662306a36Sopenharmony_ci{
188762306a36Sopenharmony_ci	struct cgx *cgx = pci_get_drvdata(pdev);
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	if (cgx) {
189062306a36Sopenharmony_ci		cgx_lmac_exit(cgx);
189162306a36Sopenharmony_ci		list_del(&cgx->cgx_list);
189262306a36Sopenharmony_ci	}
189362306a36Sopenharmony_ci	pci_free_irq_vectors(pdev);
189462306a36Sopenharmony_ci	pci_release_regions(pdev);
189562306a36Sopenharmony_ci	pci_disable_device(pdev);
189662306a36Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
189762306a36Sopenharmony_ci}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_cistruct pci_driver cgx_driver = {
190062306a36Sopenharmony_ci	.name = DRV_NAME,
190162306a36Sopenharmony_ci	.id_table = cgx_id_table,
190262306a36Sopenharmony_ci	.probe = cgx_probe,
190362306a36Sopenharmony_ci	.remove = cgx_remove,
190462306a36Sopenharmony_ci};
1905