File Index
Files and directories
Sign in
FILE

fbnic_mdio.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c C
  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
 14static int
 15fbnic_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
 64static int
 65fbnic_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
 98static int
 99fbnic_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
112static void
113fbnic_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
120static void
121fbnic_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
142static int
143fbnic_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 **/
166int 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}
~/ Read-only file preview.