xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
1*d0ce9fd7SAlexander Duyck // SPDX-License-Identifier: GPL-2.0
2*d0ce9fd7SAlexander Duyck /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3*d0ce9fd7SAlexander Duyck 
4*d0ce9fd7SAlexander Duyck #include <linux/mdio.h>
5*d0ce9fd7SAlexander Duyck #include <linux/pcs/pcs-xpcs.h>
6*d0ce9fd7SAlexander Duyck 
7*d0ce9fd7SAlexander Duyck #include "fbnic.h"
8*d0ce9fd7SAlexander Duyck #include "fbnic_netdev.h"
9*d0ce9fd7SAlexander Duyck 
10*d0ce9fd7SAlexander Duyck #define DW_VENDOR		BIT(15)
11*d0ce9fd7SAlexander Duyck #define FBNIC_PCS_VENDOR	BIT(9)
12*d0ce9fd7SAlexander Duyck #define FBNIC_PCS_ZERO_MASK	(DW_VENDOR - FBNIC_PCS_VENDOR)
13*d0ce9fd7SAlexander Duyck 
14*d0ce9fd7SAlexander Duyck static int
15*d0ce9fd7SAlexander Duyck fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum)
16*d0ce9fd7SAlexander Duyck {
17*d0ce9fd7SAlexander Duyck 	u8 aui = FBNIC_AUI_UNKNOWN;
18*d0ce9fd7SAlexander Duyck 	struct fbnic_net *fbn;
19*d0ce9fd7SAlexander Duyck 	int ret = 0;
20*d0ce9fd7SAlexander Duyck 
21*d0ce9fd7SAlexander Duyck 	/* We don't need a second PMD, just one can handle both lanes */
22*d0ce9fd7SAlexander Duyck 	if (addr)
23*d0ce9fd7SAlexander Duyck 		return 0;
24*d0ce9fd7SAlexander Duyck 
25*d0ce9fd7SAlexander Duyck 	if (fbd->netdev) {
26*d0ce9fd7SAlexander Duyck 		fbn = netdev_priv(fbd->netdev);
27*d0ce9fd7SAlexander Duyck 		if (fbn->aui < FBNIC_AUI_UNKNOWN)
28*d0ce9fd7SAlexander Duyck 			aui = fbn->aui;
29*d0ce9fd7SAlexander Duyck 	}
30*d0ce9fd7SAlexander Duyck 
31*d0ce9fd7SAlexander Duyck 	switch (regnum) {
32*d0ce9fd7SAlexander Duyck 	case MDIO_DEVID1:
33*d0ce9fd7SAlexander Duyck 		ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16;
34*d0ce9fd7SAlexander Duyck 		break;
35*d0ce9fd7SAlexander Duyck 	case MDIO_DEVID2:
36*d0ce9fd7SAlexander Duyck 		ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff;
37*d0ce9fd7SAlexander Duyck 		break;
38*d0ce9fd7SAlexander Duyck 	case MDIO_DEVS1:
39*d0ce9fd7SAlexander Duyck 		ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;
40*d0ce9fd7SAlexander Duyck 		break;
41*d0ce9fd7SAlexander Duyck 	case MDIO_STAT2:
42*d0ce9fd7SAlexander Duyck 		ret = MDIO_STAT2_DEVPRST_VAL;
43*d0ce9fd7SAlexander Duyck 		break;
44*d0ce9fd7SAlexander Duyck 	case MDIO_PMA_RXDET:
45*d0ce9fd7SAlexander Duyck 		/* If training isn't complete default to 0 */
46*d0ce9fd7SAlexander Duyck 		if (fbd->pmd_state != FBNIC_PMD_SEND_DATA)
47*d0ce9fd7SAlexander Duyck 			break;
48*d0ce9fd7SAlexander Duyck 		/* Report either 1 or 2 lanes detected depending on config */
49*d0ce9fd7SAlexander Duyck 		ret = (MDIO_PMD_RXDET_GLOBAL | MDIO_PMD_RXDET_0) |
50*d0ce9fd7SAlexander Duyck 		      ((aui & FBNIC_AUI_MODE_R2) *
51*d0ce9fd7SAlexander Duyck 		       (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2));
52*d0ce9fd7SAlexander Duyck 		break;
53*d0ce9fd7SAlexander Duyck 	default:
54*d0ce9fd7SAlexander Duyck 		break;
55*d0ce9fd7SAlexander Duyck 	}
56*d0ce9fd7SAlexander Duyck 
57*d0ce9fd7SAlexander Duyck 	dev_dbg(fbd->dev,
58*d0ce9fd7SAlexander Duyck 		"SWMII PMD Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
59*d0ce9fd7SAlexander Duyck 		addr, regnum, ret);
60*d0ce9fd7SAlexander Duyck 
61*d0ce9fd7SAlexander Duyck 	return ret;
62*d0ce9fd7SAlexander Duyck }
63*d0ce9fd7SAlexander Duyck 
64*d0ce9fd7SAlexander Duyck static int
65*d0ce9fd7SAlexander Duyck fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum)
66*d0ce9fd7SAlexander Duyck {
67*d0ce9fd7SAlexander Duyck 	int ret, offset = 0;
68*d0ce9fd7SAlexander Duyck 
69*d0ce9fd7SAlexander Duyck 	/* We will need access to both PCS instances to get config info */
70*d0ce9fd7SAlexander Duyck 	if (addr >= 2)
71*d0ce9fd7SAlexander Duyck 		return 0;
72*d0ce9fd7SAlexander Duyck 
73*d0ce9fd7SAlexander Duyck 	/* Report 0 for reserved registers */
74*d0ce9fd7SAlexander Duyck 	if (regnum & FBNIC_PCS_ZERO_MASK)
75*d0ce9fd7SAlexander Duyck 		return 0;
76*d0ce9fd7SAlexander Duyck 
77*d0ce9fd7SAlexander Duyck 	/* Intercept and return correct ID for PCS */
78*d0ce9fd7SAlexander Duyck 	if (regnum == MDIO_DEVID1)
79*d0ce9fd7SAlexander Duyck 		return DW_XPCS_ID >> 16;
80*d0ce9fd7SAlexander Duyck 	if (regnum == MDIO_DEVID2)
81*d0ce9fd7SAlexander Duyck 		return DW_XPCS_ID & 0xffff;
82*d0ce9fd7SAlexander Duyck 	if (regnum == MDIO_DEVS1)
83*d0ce9fd7SAlexander Duyck 		return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;
84*d0ce9fd7SAlexander Duyck 
85*d0ce9fd7SAlexander Duyck 	/* Swap vendor page bit for FBNIC PCS vendor page bit */
86*d0ce9fd7SAlexander Duyck 	if (regnum & DW_VENDOR)
87*d0ce9fd7SAlexander Duyck 		offset ^= DW_VENDOR | FBNIC_PCS_VENDOR;
88*d0ce9fd7SAlexander Duyck 
89*d0ce9fd7SAlexander Duyck 	ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset));
90*d0ce9fd7SAlexander Duyck 
91*d0ce9fd7SAlexander Duyck 	dev_dbg(fbd->dev,
92*d0ce9fd7SAlexander Duyck 		"SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
93*d0ce9fd7SAlexander Duyck 		addr, regnum, ret);
94*d0ce9fd7SAlexander Duyck 
95*d0ce9fd7SAlexander Duyck 	return ret;
96*d0ce9fd7SAlexander Duyck }
97*d0ce9fd7SAlexander Duyck 
98*d0ce9fd7SAlexander Duyck static int
99*d0ce9fd7SAlexander Duyck fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
100*d0ce9fd7SAlexander Duyck {
101*d0ce9fd7SAlexander Duyck 	struct fbnic_dev *fbd = bus->priv;
102*d0ce9fd7SAlexander Duyck 
103*d0ce9fd7SAlexander Duyck 	if (devnum == MDIO_MMD_PMAPMD)
104*d0ce9fd7SAlexander Duyck 		return fbnic_mdio_read_pmd(fbd, addr, regnum);
105*d0ce9fd7SAlexander Duyck 
106*d0ce9fd7SAlexander Duyck 	if (devnum == MDIO_MMD_PCS)
107*d0ce9fd7SAlexander Duyck 		return fbnic_mdio_read_pcs(fbd, addr, regnum);
108*d0ce9fd7SAlexander Duyck 
109*d0ce9fd7SAlexander Duyck 	return 0;
110*d0ce9fd7SAlexander Duyck }
111*d0ce9fd7SAlexander Duyck 
112*d0ce9fd7SAlexander Duyck static void
113*d0ce9fd7SAlexander Duyck fbnic_mdio_write_pmd(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
114*d0ce9fd7SAlexander Duyck {
115*d0ce9fd7SAlexander Duyck 	dev_dbg(fbd->dev,
116*d0ce9fd7SAlexander Duyck 		"SWMII PMD Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
117*d0ce9fd7SAlexander Duyck 		addr, regnum, val);
118*d0ce9fd7SAlexander Duyck }
119*d0ce9fd7SAlexander Duyck 
120*d0ce9fd7SAlexander Duyck static void
121*d0ce9fd7SAlexander Duyck fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
122*d0ce9fd7SAlexander Duyck {
123*d0ce9fd7SAlexander Duyck 	dev_dbg(fbd->dev,
124*d0ce9fd7SAlexander Duyck 		"SWMII PCS Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
125*d0ce9fd7SAlexander Duyck 		addr, regnum, val);
126*d0ce9fd7SAlexander Duyck 
127*d0ce9fd7SAlexander Duyck 	/* Allow access to both halves of PCS for 50R2 config */
128*d0ce9fd7SAlexander Duyck 	if (addr > 2)
129*d0ce9fd7SAlexander Duyck 		return;
130*d0ce9fd7SAlexander Duyck 
131*d0ce9fd7SAlexander Duyck 	/* Skip write for reserved registers */
132*d0ce9fd7SAlexander Duyck 	if (regnum & FBNIC_PCS_ZERO_MASK)
133*d0ce9fd7SAlexander Duyck 		return;
134*d0ce9fd7SAlexander Duyck 
135*d0ce9fd7SAlexander Duyck 	/* Swap vendor page bit for FBNIC PCS vendor page bit */
136*d0ce9fd7SAlexander Duyck 	if (regnum & DW_VENDOR)
137*d0ce9fd7SAlexander Duyck 		regnum ^= DW_VENDOR | FBNIC_PCS_VENDOR;
138*d0ce9fd7SAlexander Duyck 
139*d0ce9fd7SAlexander Duyck 	fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val);
140*d0ce9fd7SAlexander Duyck }
141*d0ce9fd7SAlexander Duyck 
142*d0ce9fd7SAlexander Duyck static int
143*d0ce9fd7SAlexander Duyck fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum,
144*d0ce9fd7SAlexander Duyck 		     int regnum, u16 val)
145*d0ce9fd7SAlexander Duyck {
146*d0ce9fd7SAlexander Duyck 	struct fbnic_dev *fbd = bus->priv;
147*d0ce9fd7SAlexander Duyck 
148*d0ce9fd7SAlexander Duyck 	if (devnum == MDIO_MMD_PMAPMD)
149*d0ce9fd7SAlexander Duyck 		fbnic_mdio_write_pmd(fbd, addr, regnum, val);
150*d0ce9fd7SAlexander Duyck 
151*d0ce9fd7SAlexander Duyck 	if (devnum == MDIO_MMD_PCS)
152*d0ce9fd7SAlexander Duyck 		fbnic_mdio_write_pcs(fbd, addr, regnum, val);
153*d0ce9fd7SAlexander Duyck 
154*d0ce9fd7SAlexander Duyck 	return 0;
155*d0ce9fd7SAlexander Duyck }
156*d0ce9fd7SAlexander Duyck 
157*d0ce9fd7SAlexander Duyck /**
158*d0ce9fd7SAlexander Duyck  * fbnic_mdiobus_create - Create an MDIO bus to allow interfacing w/ PHYs
159*d0ce9fd7SAlexander Duyck  * @fbd: Pointer to FBNIC device structure to populate bus on
160*d0ce9fd7SAlexander Duyck  *
161*d0ce9fd7SAlexander Duyck  * Initialize an MDIO bus and place a pointer to it on the fbd struct. This bus
162*d0ce9fd7SAlexander Duyck  * will be used to interface with the PMA/PMD and PCS.
163*d0ce9fd7SAlexander Duyck  *
164*d0ce9fd7SAlexander Duyck  * Return: 0 on success, negative on failure
165*d0ce9fd7SAlexander Duyck  **/
166*d0ce9fd7SAlexander Duyck int fbnic_mdiobus_create(struct fbnic_dev *fbd)
167*d0ce9fd7SAlexander Duyck {
168*d0ce9fd7SAlexander Duyck 	struct mii_bus *bus;
169*d0ce9fd7SAlexander Duyck 	int err;
170*d0ce9fd7SAlexander Duyck 
171*d0ce9fd7SAlexander Duyck 	bus = devm_mdiobus_alloc(fbd->dev);
172*d0ce9fd7SAlexander Duyck 	if (!bus)
173*d0ce9fd7SAlexander Duyck 		return -ENOMEM;
174*d0ce9fd7SAlexander Duyck 
175*d0ce9fd7SAlexander Duyck 	bus->name = "fbnic_mii_bus";
176*d0ce9fd7SAlexander Duyck 	bus->read_c45 = &fbnic_mdio_read_c45;
177*d0ce9fd7SAlexander Duyck 	bus->write_c45 = &fbnic_mdio_write_c45;
178*d0ce9fd7SAlexander Duyck 
179*d0ce9fd7SAlexander Duyck 	/* Disable PHY auto probing. We will add PCS manually */
180*d0ce9fd7SAlexander Duyck 	bus->phy_mask = ~0;
181*d0ce9fd7SAlexander Duyck 
182*d0ce9fd7SAlexander Duyck 	bus->parent = fbd->dev;
183*d0ce9fd7SAlexander Duyck 	bus->priv = fbd;
184*d0ce9fd7SAlexander Duyck 	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(fbd->dev));
185*d0ce9fd7SAlexander Duyck 
186*d0ce9fd7SAlexander Duyck 	err = devm_mdiobus_register(fbd->dev, bus);
187*d0ce9fd7SAlexander Duyck 	if (err) {
188*d0ce9fd7SAlexander Duyck 		dev_err(fbd->dev, "Failed to create MDIO bus: %d\n", err);
189*d0ce9fd7SAlexander Duyck 		return err;
190*d0ce9fd7SAlexander Duyck 	}
191*d0ce9fd7SAlexander Duyck 
192*d0ce9fd7SAlexander Duyck 	fbd->mdio_bus = bus;
193*d0ce9fd7SAlexander Duyck 
194*d0ce9fd7SAlexander Duyck 	return 0;
195*d0ce9fd7SAlexander Duyck }
196