Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Alexander Duyck <alexanderduyck@fb.com>
Link: https://patch.msgid.link/176374324532.959489.15389723111560978054.stgit@ahduyck-xeon-server.home.arpa
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Alexander Duyck and committed by
Paolo Abeni
d0ce9fd7 1fe79783

+205
+1
drivers/net/ethernet/meta/fbnic/Makefile
··· 21 21 fbnic_pci.o \ 22 22 fbnic_phylink.o \ 23 23 fbnic_rpc.o \ 24 + fbnic_mdio.o \ 24 25 fbnic_time.o \ 25 26 fbnic_tlv.o \ 26 27 fbnic_txrx.o \
+5
drivers/net/ethernet/meta/fbnic/fbnic.h
··· 95 95 u64 prev_firmware_time; 96 96 97 97 struct fbnic_fw_log fw_log; 98 + 99 + /* MDIO bus for PHYs */ 100 + struct mii_bus *mdio_bus; 98 101 }; 99 102 100 103 /* Reserve entry 0 in the MSI-X "others" array until we have filled all ··· 206 203 void fbnic_dbg_exit(void); 207 204 208 205 void fbnic_rpc_reset_valid_entries(struct fbnic_dev *fbd); 206 + 207 + int fbnic_mdiobus_create(struct fbnic_dev *fbd); 209 208 210 209 void fbnic_csr_get_regs(struct fbnic_dev *fbd, u32 *data, u32 *regs_version); 211 210 int fbnic_csr_regs_len(struct fbnic_dev *fbd);
+1
drivers/net/ethernet/meta/fbnic/fbnic_mac.h
··· 56 56 FBNIC_AUI_50GAUI1 = 2, /* 53.125GBd 53.125 * 1 */ 57 57 FBNIC_AUI_100GAUI2 = 3, /* 106.25GBd 53.125 * 2 */ 58 58 FBNIC_AUI_UNKNOWN = 4, 59 + __FBNIC_AUI_MAX__ 59 60 }; 60 61 61 62 #define FBNIC_AUI_MODE_R2 (FBNIC_AUI_LAUI2)
+195
drivers/net/ethernet/meta/fbnic/fbnic_mdio.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 + 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 + }
+3
drivers/net/ethernet/meta/fbnic/fbnic_pci.c
··· 339 339 goto init_failure_mode; 340 340 } 341 341 342 + if (fbnic_mdiobus_create(fbd)) 343 + goto init_failure_mode; 344 + 342 345 netdev = fbnic_netdev_alloc(fbd); 343 346 if (!netdev) { 344 347 dev_err(&pdev->dev, "Netdev allocation failed\n");