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