Commit d0ce9fd7 authored by Alexander Duyck's avatar Alexander Duyck Committed by Paolo Abeni
Browse files

fbnic: Add SW shim for MDIO interface to PMD and PCS



In order for us to support a PCS device we need to add an MDIO bus to allow
the drivers to have access to the registers for the device.  This change
adds such an interface.

The interface will consist of 2 PHY addrs, the first one consisting of a
PMD and PCS, and the second just being a PCS. There is a need for 2 PHYs
addrs due to the fact that in order to support the 50GBase-CR2 mode we will
need to access and configure the PCS vendor registers and RSFEC registers
from the second lane identical to the first.

Signed-off-by: default avatarAlexander Duyck <alexanderduyck@fb.com>
Link: https://patch.msgid.link/176374324532.959489.15389723111560978054.stgit@ahduyck-xeon-server.home.arpa


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parent 1fe79783
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ fbnic-y := fbnic_csr.o \
	   fbnic_pci.o \
	   fbnic_phylink.o \
	   fbnic_rpc.o \
	   fbnic_mdio.o \
	   fbnic_time.o \
	   fbnic_tlv.o \
	   fbnic_txrx.o \
+5 −0
Original line number Diff line number Diff line
@@ -95,6 +95,9 @@ struct fbnic_dev {
	u64 prev_firmware_time;

	struct fbnic_fw_log fw_log;

	/* MDIO bus for PHYs */
	struct mii_bus *mdio_bus;
};

/* Reserve entry 0 in the MSI-X "others" array until we have filled all
@@ -204,6 +207,8 @@ void fbnic_dbg_exit(void);

void fbnic_rpc_reset_valid_entries(struct fbnic_dev *fbd);

int fbnic_mdiobus_create(struct fbnic_dev *fbd);

void fbnic_csr_get_regs(struct fbnic_dev *fbd, u32 *data, u32 *regs_version);
int fbnic_csr_regs_len(struct fbnic_dev *fbd);

+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ enum {
	FBNIC_AUI_50GAUI1	= 2,	/* 53.125GBd	53.125   * 1 */
	FBNIC_AUI_100GAUI2	= 3,	/* 106.25GBd	53.125   * 2 */
	FBNIC_AUI_UNKNOWN	= 4,
	__FBNIC_AUI_MAX__
};

#define FBNIC_AUI_MODE_R2	(FBNIC_AUI_LAUI2)
+195 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Meta Platforms, Inc. and affiliates. */

#include <linux/mdio.h>
#include <linux/pcs/pcs-xpcs.h>

#include "fbnic.h"
#include "fbnic_netdev.h"

#define DW_VENDOR		BIT(15)
#define FBNIC_PCS_VENDOR	BIT(9)
#define FBNIC_PCS_ZERO_MASK	(DW_VENDOR - FBNIC_PCS_VENDOR)

static int
fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum)
{
	u8 aui = FBNIC_AUI_UNKNOWN;
	struct fbnic_net *fbn;
	int ret = 0;

	/* We don't need a second PMD, just one can handle both lanes */
	if (addr)
		return 0;

	if (fbd->netdev) {
		fbn = netdev_priv(fbd->netdev);
		if (fbn->aui < FBNIC_AUI_UNKNOWN)
			aui = fbn->aui;
	}

	switch (regnum) {
	case MDIO_DEVID1:
		ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16;
		break;
	case MDIO_DEVID2:
		ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff;
		break;
	case MDIO_DEVS1:
		ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;
		break;
	case MDIO_STAT2:
		ret = MDIO_STAT2_DEVPRST_VAL;
		break;
	case MDIO_PMA_RXDET:
		/* If training isn't complete default to 0 */
		if (fbd->pmd_state != FBNIC_PMD_SEND_DATA)
			break;
		/* Report either 1 or 2 lanes detected depending on config */
		ret = (MDIO_PMD_RXDET_GLOBAL | MDIO_PMD_RXDET_0) |
		      ((aui & FBNIC_AUI_MODE_R2) *
		       (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2));
		break;
	default:
		break;
	}

	dev_dbg(fbd->dev,
		"SWMII PMD Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
		addr, regnum, ret);

	return ret;
}

static int
fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum)
{
	int ret, offset = 0;

	/* We will need access to both PCS instances to get config info */
	if (addr >= 2)
		return 0;

	/* Report 0 for reserved registers */
	if (regnum & FBNIC_PCS_ZERO_MASK)
		return 0;

	/* Intercept and return correct ID for PCS */
	if (regnum == MDIO_DEVID1)
		return DW_XPCS_ID >> 16;
	if (regnum == MDIO_DEVID2)
		return DW_XPCS_ID & 0xffff;
	if (regnum == MDIO_DEVS1)
		return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;

	/* Swap vendor page bit for FBNIC PCS vendor page bit */
	if (regnum & DW_VENDOR)
		offset ^= DW_VENDOR | FBNIC_PCS_VENDOR;

	ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset));

	dev_dbg(fbd->dev,
		"SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
		addr, regnum, ret);

	return ret;
}

static int
fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
{
	struct fbnic_dev *fbd = bus->priv;

	if (devnum == MDIO_MMD_PMAPMD)
		return fbnic_mdio_read_pmd(fbd, addr, regnum);

	if (devnum == MDIO_MMD_PCS)
		return fbnic_mdio_read_pcs(fbd, addr, regnum);

	return 0;
}

static void
fbnic_mdio_write_pmd(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
{
	dev_dbg(fbd->dev,
		"SWMII PMD Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
		addr, regnum, val);
}

static void
fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
{
	dev_dbg(fbd->dev,
		"SWMII PCS Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
		addr, regnum, val);

	/* Allow access to both halves of PCS for 50R2 config */
	if (addr > 2)
		return;

	/* Skip write for reserved registers */
	if (regnum & FBNIC_PCS_ZERO_MASK)
		return;

	/* Swap vendor page bit for FBNIC PCS vendor page bit */
	if (regnum & DW_VENDOR)
		regnum ^= DW_VENDOR | FBNIC_PCS_VENDOR;

	fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val);
}

static int
fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum,
		     int regnum, u16 val)
{
	struct fbnic_dev *fbd = bus->priv;

	if (devnum == MDIO_MMD_PMAPMD)
		fbnic_mdio_write_pmd(fbd, addr, regnum, val);

	if (devnum == MDIO_MMD_PCS)
		fbnic_mdio_write_pcs(fbd, addr, regnum, val);

	return 0;
}

/**
 * fbnic_mdiobus_create - Create an MDIO bus to allow interfacing w/ PHYs
 * @fbd: Pointer to FBNIC device structure to populate bus on
 *
 * Initialize an MDIO bus and place a pointer to it on the fbd struct. This bus
 * will be used to interface with the PMA/PMD and PCS.
 *
 * Return: 0 on success, negative on failure
 **/
int fbnic_mdiobus_create(struct fbnic_dev *fbd)
{
	struct mii_bus *bus;
	int err;

	bus = devm_mdiobus_alloc(fbd->dev);
	if (!bus)
		return -ENOMEM;

	bus->name = "fbnic_mii_bus";
	bus->read_c45 = &fbnic_mdio_read_c45;
	bus->write_c45 = &fbnic_mdio_write_c45;

	/* Disable PHY auto probing. We will add PCS manually */
	bus->phy_mask = ~0;

	bus->parent = fbd->dev;
	bus->priv = fbd;
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(fbd->dev));

	err = devm_mdiobus_register(fbd->dev, bus);
	if (err) {
		dev_err(fbd->dev, "Failed to create MDIO bus: %d\n", err);
		return err;
	}

	fbd->mdio_bus = bus;

	return 0;
}
+3 −0
Original line number Diff line number Diff line
@@ -339,6 +339,9 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
		goto init_failure_mode;
	}

	if (fbnic_mdiobus_create(fbd))
		goto init_failure_mode;

	netdev = fbnic_netdev_alloc(fbd);
	if (!netdev) {
		dev_err(&pdev->dev, "Netdev allocation failed\n");