Commit 76c3a449 authored by David S. Miller's avatar David S. Miller

Merge branch 'NCN26000-PLCA-RS-support'

Piergiorgio Beruto says:

====================
net: add PLCA RS support and onsemi NCN26000

This patchset adds support for getting/setting the Physical Layer
Collision Avoidace (PLCA) Reconciliation Sublayer (RS) configuration and
status on Ethernet PHYs that supports it.

PLCA is a feature that provides improved media-access performance in terms
of throughput, latency and fairness for multi-drop (P2MP) half-duplex PHYs.
PLCA is defined in Clause 148 of the IEEE802.3 specifications as amended
by 802.3cg-2019. Currently, PLCA is supported by the 10BASE-T1S single-pair
Ethernet PHY defined in the same standard and related amendments. The OPEN
Alliance SIG TC14 defines additional specifications for the 10BASE-T1S PHY,
including a standard register map for PHYs that embeds the PLCA RS (see
PLCA management registers at https://www.opensig.org/about/specifications/).

The changes proposed herein add the appropriate ethtool netlink interface
for configuring the PLCA RS on PHYs that supports it. A separate patchset
further modifies the ethtool userspace program to show and modify the
configuration/status of the PLCA RS.

Additionally, this patchset adds support for the onsemi NCN26000
Industrial Ethernet 10BASE-T1S PHY that uses the newly added PLCA
infrastructure.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a6f53606 b53e7e8d
......@@ -1716,6 +1716,141 @@ being used. Current supported options are toeplitz, xor or crc32.
ETHTOOL_A_RSS_INDIR attribute returns RSS indrection table where each byte
indicates queue number.
PLCA_GET_CFG
============
Gets the IEEE 802.3cg-2019 Clause 148 Physical Layer Collision Avoidance
(PLCA) Reconciliation Sublayer (RS) attributes.
Request contents:
===================================== ====== ==========================
``ETHTOOL_A_PLCA_HEADER`` nested request header
===================================== ====== ==========================
Kernel response contents:
====================================== ====== =============================
``ETHTOOL_A_PLCA_HEADER`` nested reply header
``ETHTOOL_A_PLCA_VERSION`` u16 Supported PLCA management
interface standard/version
``ETHTOOL_A_PLCA_ENABLED`` u8 PLCA Admin State
``ETHTOOL_A_PLCA_NODE_ID`` u32 PLCA unique local node ID
``ETHTOOL_A_PLCA_NODE_CNT`` u32 Number of PLCA nodes on the
network, including the
coordinator
``ETHTOOL_A_PLCA_TO_TMR`` u32 Transmit Opportunity Timer
value in bit-times (BT)
``ETHTOOL_A_PLCA_BURST_CNT`` u32 Number of additional packets
the node is allowed to send
within a single TO
``ETHTOOL_A_PLCA_BURST_TMR`` u32 Time to wait for the MAC to
transmit a new frame before
terminating the burst
====================================== ====== =============================
When set, the optional ``ETHTOOL_A_PLCA_VERSION`` attribute indicates which
standard and version the PLCA management interface complies to. When not set,
the interface is vendor-specific and (possibly) supplied by the driver.
The OPEN Alliance SIG specifies a standard register map for 10BASE-T1S PHYs
embedding the PLCA Reconcialiation Sublayer. See "10BASE-T1S PLCA Management
Registers" at https://www.opensig.org/about/specifications/.
When set, the optional ``ETHTOOL_A_PLCA_ENABLED`` attribute indicates the
administrative state of the PLCA RS. When not set, the node operates in "plain"
CSMA/CD mode. This option is corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.1
aPLCAAdminState / 30.16.1.2.1 acPLCAAdminControl.
When set, the optional ``ETHTOOL_A_PLCA_NODE_ID`` attribute indicates the
configured local node ID of the PHY. This ID determines which transmit
opportunity (TO) is reserved for the node to transmit into. This option is
corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.4 aPLCALocalNodeID. The valid
range for this attribute is [0 .. 255] where 255 means "not configured".
When set, the optional ``ETHTOOL_A_PLCA_NODE_CNT`` attribute indicates the
configured maximum number of PLCA nodes on the mixing-segment. This number
determines the total number of transmit opportunities generated during a
PLCA cycle. This attribute is relevant only for the PLCA coordinator, which is
the node with aPLCALocalNodeID set to 0. Follower nodes ignore this setting.
This option is corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.3
aPLCANodeCount. The valid range for this attribute is [1 .. 255].
When set, the optional ``ETHTOOL_A_PLCA_TO_TMR`` attribute indicates the
configured value of the transmit opportunity timer in bit-times. This value
must be set equal across all nodes sharing the medium for PLCA to work
correctly. This option is corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.5
aPLCATransmitOpportunityTimer. The valid range for this attribute is
[0 .. 255].
When set, the optional ``ETHTOOL_A_PLCA_BURST_CNT`` attribute indicates the
configured number of extra packets that the node is allowed to send during a
single transmit opportunity. By default, this attribute is 0, meaning that
the node can only send a sigle frame per TO. When greater than 0, the PLCA RS
keeps the TO after any transmission, waiting for the MAC to send a new frame
for up to aPLCABurstTimer BTs. This can only happen a number of times per PLCA
cycle up to the value of this parameter. After that, the burst is over and the
normal counting of TOs resumes. This option is corresponding to
``IEEE 802.3cg-2019`` 30.16.1.1.6 aPLCAMaxBurstCount. The valid range for this
attribute is [0 .. 255].
When set, the optional ``ETHTOOL_A_PLCA_BURST_TMR`` attribute indicates how
many bit-times the PLCA RS waits for the MAC to initiate a new transmission
when aPLCAMaxBurstCount is greater than 0. If the MAC fails to send a new
frame within this time, the burst ends and the counting of TOs resumes.
Otherwise, the new frame is sent as part of the current burst. This option
is corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.7 aPLCABurstTimer. The
valid range for this attribute is [0 .. 255]. Although, the value should be
set greater than the Inter-Frame-Gap (IFG) time of the MAC (plus some margin)
for PLCA burst mode to work as intended.
PLCA_SET_CFG
============
Sets PLCA RS parameters.
Request contents:
====================================== ====== =============================
``ETHTOOL_A_PLCA_HEADER`` nested request header
``ETHTOOL_A_PLCA_ENABLED`` u8 PLCA Admin State
``ETHTOOL_A_PLCA_NODE_ID`` u8 PLCA unique local node ID
``ETHTOOL_A_PLCA_NODE_CNT`` u8 Number of PLCA nodes on the
netkork, including the
coordinator
``ETHTOOL_A_PLCA_TO_TMR`` u8 Transmit Opportunity Timer
value in bit-times (BT)
``ETHTOOL_A_PLCA_BURST_CNT`` u8 Number of additional packets
the node is allowed to send
within a single TO
``ETHTOOL_A_PLCA_BURST_TMR`` u8 Time to wait for the MAC to
transmit a new frame before
terminating the burst
====================================== ====== =============================
For a description of each attribute, see ``PLCA_GET_CFG``.
PLCA_GET_STATUS
===============
Gets PLCA RS status information.
Request contents:
===================================== ====== ==========================
``ETHTOOL_A_PLCA_HEADER`` nested request header
===================================== ====== ==========================
Kernel response contents:
====================================== ====== =============================
``ETHTOOL_A_PLCA_HEADER`` nested reply header
``ETHTOOL_A_PLCA_STATUS`` u8 PLCA RS operational status
====================================== ====== =============================
When set, the ``ETHTOOL_A_PLCA_STATUS`` attribute indicates whether the node is
detecting the presence of the BEACON on the network. This flag is
corresponding to ``IEEE 802.3cg-2019`` 30.16.1.1.2 aPLCAStatus.
Request translation
===================
......@@ -1817,4 +1952,7 @@ are netlink only.
n/a ``ETHTOOL_MSG_PHC_VCLOCKS_GET``
n/a ``ETHTOOL_MSG_MODULE_GET``
n/a ``ETHTOOL_MSG_MODULE_SET``
n/a ``ETHTOOL_MSG_PLCA_GET_CFG``
n/a ``ETHTOOL_MSG_PLCA_SET_CFG``
n/a ``ETHTOOL_MSG_PLCA_GET_STATUS``
=================================== =====================================
......@@ -15584,6 +15584,13 @@ L: linux-mips@vger.kernel.org
S: Maintained
F: arch/mips/boot/dts/ralink/omega2p.dts
ONSEMI ETHERNET PHY DRIVERS
M: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
L: netdev@vger.kernel.org
S: Supported
W: http://www.onsemi.com
F: drivers/net/phy/ncn*
OP-TEE DRIVER
M: Jens Wiklander <jens.wiklander@linaro.org>
L: op-tee@lists.trustedfirmware.org
......@@ -16616,6 +16623,13 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/chemical/plantower,pms7003.yaml
F: drivers/iio/chemical/pms7003.c
PLCA RECONCILIATION SUBLAYER (IEEE802.3 Clause 148)
M: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/phy/mdio-open-alliance.h
F: net/ethtool/plca.c
PLDMFW LIBRARY
M: Jacob Keller <jacob.e.keller@intel.com>
S: Maintained
......
......@@ -277,6 +277,13 @@ config NXP_TJA11XX_PHY
help
Currently supports the NXP TJA1100 and TJA1101 PHY.
config NCN26000_PHY
tristate "Onsemi 10BASE-T1S Ethernet PHY"
help
Adds support for the onsemi 10BASE-T1S Ethernet PHY.
Currently supports the NCN26000 10BASE-T1S Industrial PHY
with MII interface.
config AT803X_PHY
tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
depends on REGULATOR
......
......@@ -77,6 +77,7 @@ obj-$(CONFIG_MICROCHIP_T1_PHY) += microchip_t1.o
obj-$(CONFIG_MICROSEMI_PHY) += mscc/
obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o
obj-$(CONFIG_NATIONAL_PHY) += national.o
obj-$(CONFIG_NCN26000_PHY) += ncn26000.o
obj-$(CONFIG_NXP_C45_TJA11XX_PHY) += nxp-c45-tja11xx.o
obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* mdio-open-alliance.h - definition of OPEN Alliance SIG standard registers
*/
#ifndef __MDIO_OPEN_ALLIANCE__
#define __MDIO_OPEN_ALLIANCE__
#include <linux/mdio.h>
/* NOTE: all OATC14 registers are located in MDIO_MMD_VEND2 */
/* Open Alliance TC14 (10BASE-T1S) registers */
#define MDIO_OATC14_PLCA_IDVER 0xca00 /* PLCA ID and version */
#define MDIO_OATC14_PLCA_CTRL0 0xca01 /* PLCA Control register 0 */
#define MDIO_OATC14_PLCA_CTRL1 0xca02 /* PLCA Control register 1 */
#define MDIO_OATC14_PLCA_STATUS 0xca03 /* PLCA Status register */
#define MDIO_OATC14_PLCA_TOTMR 0xca04 /* PLCA TO Timer register */
#define MDIO_OATC14_PLCA_BURST 0xca05 /* PLCA BURST mode register */
/* Open Alliance TC14 PLCA IDVER register */
#define MDIO_OATC14_PLCA_IDM 0xff00 /* PLCA MAP ID */
#define MDIO_OATC14_PLCA_VER 0x00ff /* PLCA MAP version */
/* Open Alliance TC14 PLCA CTRL0 register */
#define MDIO_OATC14_PLCA_EN BIT(15) /* PLCA enable */
#define MDIO_OATC14_PLCA_RST BIT(14) /* PLCA reset */
/* Open Alliance TC14 PLCA CTRL1 register */
#define MDIO_OATC14_PLCA_NCNT 0xff00 /* PLCA node count */
#define MDIO_OATC14_PLCA_ID 0x00ff /* PLCA local node ID */
/* Open Alliance TC14 PLCA STATUS register */
#define MDIO_OATC14_PLCA_PST BIT(15) /* PLCA status indication */
/* Open Alliance TC14 PLCA TOTMR register */
#define MDIO_OATC14_PLCA_TOT 0x00ff
/* Open Alliance TC14 PLCA BURST register */
#define MDIO_OATC14_PLCA_MAXBC 0xff00
#define MDIO_OATC14_PLCA_BTMR 0x00ff
/* Version Identifiers */
#define OATC14_IDM 0x0a00
#endif /* __MDIO_OPEN_ALLIANCE__ */
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
* Driver for the onsemi 10BASE-T1S NCN26000 PHYs family.
*
* Copyright 2022 onsemi
*/
#include <linux/kernel.h>
#include <linux/bitfield.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include "mdio-open-alliance.h"
#define PHY_ID_NCN26000 0x180FF5A1
#define NCN26000_REG_IRQ_CTL 16
#define NCN26000_REG_IRQ_STATUS 17
// the NCN26000 maps link_ctrl to BMCR_ANENABLE
#define NCN26000_BCMR_LINK_CTRL_BIT BMCR_ANENABLE
// the NCN26000 maps link_status to BMSR_ANEGCOMPLETE
#define NCN26000_BMSR_LINK_STATUS_BIT BMSR_ANEGCOMPLETE
#define NCN26000_IRQ_LINKST_BIT BIT(0)
#define NCN26000_IRQ_PLCAST_BIT BIT(1)
#define NCN26000_IRQ_LJABBER_BIT BIT(2)
#define NCN26000_IRQ_RJABBER_BIT BIT(3)
#define NCN26000_IRQ_PLCAREC_BIT BIT(4)
#define NCN26000_IRQ_PHYSCOL_BIT BIT(5)
#define NCN26000_IRQ_RESET_BIT BIT(15)
#define TO_TMR_DEFAULT 32
static int ncn26000_config_init(struct phy_device *phydev)
{
/* HW bug workaround: the default value of the PLCA TO_TIMER should be
* 32, where the current version of NCN26000 reports 24. This will be
* fixed in future PHY versions. For the time being, we force the
* correct default here.
*/
return phy_write_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR,
TO_TMR_DEFAULT);
}
static int ncn26000_config_aneg(struct phy_device *phydev)
{
/* Note: the NCN26000 supports only P2MP link mode. Therefore, AN is not
* supported. However, this function is invoked by phylib to enable the
* PHY, regardless of the AN support.
*/
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
phydev->mdix = ETH_TP_MDI;
// bring up the link
return phy_write(phydev, MII_BMCR, NCN26000_BCMR_LINK_CTRL_BIT);
}
static int ncn26000_read_status(struct phy_device *phydev)
{
/* The NCN26000 reports NCN26000_LINK_STATUS_BIT if the link status of
* the PHY is up. It further reports the logical AND of the link status
* and the PLCA status in the BMSR_LSTATUS bit.
*/
int ret;
/* The link state is latched low so that momentary link
* drops can be detected. Do not double-read the status
* in polling mode to detect such short link drops except
* the link was already down.
*/
if (!phy_polling_mode(phydev) || !phydev->link) {
ret = phy_read(phydev, MII_BMSR);
if (ret < 0)
return ret;
else if (ret & NCN26000_BMSR_LINK_STATUS_BIT)
goto upd_link;
}
ret = phy_read(phydev, MII_BMSR);
if (ret < 0)
return ret;
upd_link:
// update link status
if (ret & NCN26000_BMSR_LINK_STATUS_BIT) {
phydev->link = 1;
phydev->pause = 0;
phydev->duplex = DUPLEX_HALF;
phydev->speed = SPEED_10;
} else {
phydev->link = 0;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->speed = SPEED_UNKNOWN;
}
return 0;
}
static irqreturn_t ncn26000_handle_interrupt(struct phy_device *phydev)
{
int ret;
// read and aknowledge the IRQ status register
ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
// check only link status changes
if (ret < 0 || (ret & NCN26000_REG_IRQ_STATUS) == 0)
return IRQ_NONE;
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int ncn26000_config_intr(struct phy_device *phydev)
{
int ret;
u16 irqe;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
// acknowledge IRQs
ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
if (ret < 0)
return ret;
// get link status notifications
irqe = NCN26000_IRQ_LINKST_BIT;
} else {
// disable all IRQs
irqe = 0;
}
ret = phy_write(phydev, NCN26000_REG_IRQ_CTL, irqe);
if (ret != 0)
return ret;
return 0;
}
static struct phy_driver ncn26000_driver[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_NCN26000),
.name = "NCN26000",
.features = PHY_BASIC_T1S_P2MP_FEATURES,
.config_init = ncn26000_config_init,
.config_intr = ncn26000_config_intr,
.config_aneg = ncn26000_config_aneg,
.read_status = ncn26000_read_status,
.handle_interrupt = ncn26000_handle_interrupt,
.get_plca_cfg = genphy_c45_plca_get_cfg,
.set_plca_cfg = genphy_c45_plca_set_cfg,
.get_plca_status = genphy_c45_plca_get_status,
.soft_reset = genphy_soft_reset,
},
};
module_phy_driver(ncn26000_driver);
static struct mdio_device_id __maybe_unused ncn26000_tbl[] = {
{ PHY_ID_MATCH_MODEL(PHY_ID_NCN26000) },
{ }
};
MODULE_DEVICE_TABLE(mdio, ncn26000_tbl);
MODULE_AUTHOR("Piergiorgio Beruto");
MODULE_DESCRIPTION("onsemi 10BASE-T1S PHY driver");
MODULE_LICENSE("Dual BSD/GPL");
......@@ -8,6 +8,8 @@
#include <linux/mii.h>
#include <linux/phy.h>
#include "mdio-open-alliance.h"
/**
* genphy_c45_baset1_able - checks if the PMA has BASE-T1 extended abilities
* @phydev: target phy_device struct
......@@ -931,6 +933,197 @@ int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable)
}
EXPORT_SYMBOL_GPL(genphy_c45_fast_retrain);
/**
* genphy_c45_plca_get_cfg - get PLCA configuration from standard registers
* @phydev: target phy_device struct
* @plca_cfg: output structure to store the PLCA configuration
*
* Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
* Management Registers specifications, this function can be used to retrieve
* the current PLCA configuration from the standard registers in MMD 31.
*/
int genphy_c45_plca_get_cfg(struct phy_device *phydev,
struct phy_plca_cfg *plca_cfg)
{
int ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_IDVER);
if (ret < 0)
return ret;
if ((ret & MDIO_OATC14_PLCA_IDM) != OATC14_IDM)
return -ENODEV;
plca_cfg->version = ret & ~MDIO_OATC14_PLCA_IDM;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_CTRL0);
if (ret < 0)
return ret;
plca_cfg->enabled = !!(ret & MDIO_OATC14_PLCA_EN);
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_CTRL1);
if (ret < 0)
return ret;
plca_cfg->node_cnt = (ret & MDIO_OATC14_PLCA_NCNT) >> 8;
plca_cfg->node_id = (ret & MDIO_OATC14_PLCA_ID);
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR);
if (ret < 0)
return ret;
plca_cfg->to_tmr = ret & MDIO_OATC14_PLCA_TOT;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_BURST);
if (ret < 0)
return ret;
plca_cfg->burst_cnt = (ret & MDIO_OATC14_PLCA_MAXBC) >> 8;
plca_cfg->burst_tmr = (ret & MDIO_OATC14_PLCA_BTMR);
return 0;
}
EXPORT_SYMBOL_GPL(genphy_c45_plca_get_cfg);
/**
* genphy_c45_plca_set_cfg - set PLCA configuration using standard registers
* @phydev: target phy_device struct
* @plca_cfg: structure containing the PLCA configuration. Fields set to -1 are
* not to be changed.
*
* Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
* Management Registers specifications, this function can be used to modify
* the PLCA configuration using the standard registers in MMD 31.
*/
int genphy_c45_plca_set_cfg(struct phy_device *phydev,
const struct phy_plca_cfg *plca_cfg)
{
int ret;
u16 val;
// PLCA IDVER is read-only
if (plca_cfg->version >= 0)
return -EINVAL;
// first of all, disable PLCA if required
if (plca_cfg->enabled == 0) {
ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
MDIO_OATC14_PLCA_CTRL0,
MDIO_OATC14_PLCA_EN);
if (ret < 0)
return ret;
}
// check if we need to set the PLCA node count, node ID, or both
if (plca_cfg->node_cnt >= 0 || plca_cfg->node_id >= 0) {
/* if one between node count and node ID is -not- to be
* changed, read the register to later perform merge/purge of
* the configuration as appropriate
*/
if (plca_cfg->node_cnt < 0 || plca_cfg->node_id < 0) {
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
MDIO_OATC14_PLCA_CTRL1);
if (ret < 0)
return ret;
val = ret;
}
if (plca_cfg->node_cnt >= 0)
val = (val & ~MDIO_OATC14_PLCA_NCNT) |
(plca_cfg->node_cnt << 8);
if (plca_cfg->node_id >= 0)
val = (val & ~MDIO_OATC14_PLCA_ID) |
(plca_cfg->node_id);
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
MDIO_OATC14_PLCA_CTRL1, val);
if (ret < 0)
return ret;
}
if (plca_cfg->to_tmr >= 0) {
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
MDIO_OATC14_PLCA_TOTMR,
plca_cfg->to_tmr);
if (ret < 0)
return ret;
}
// check if we need to set the PLCA burst count, burst timer, or both
if (plca_cfg->burst_cnt >= 0 || plca_cfg->burst_tmr >= 0) {
/* if one between burst count and burst timer is -not- to be
* changed, read the register to later perform merge/purge of
* the configuration as appropriate
*/
if (plca_cfg->burst_cnt < 0 || plca_cfg->burst_tmr < 0) {
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
MDIO_OATC14_PLCA_BURST);
if (ret < 0)
return ret;
val = ret;
}
if (plca_cfg->burst_cnt >= 0)
val = (val & ~MDIO_OATC14_PLCA_MAXBC) |
(plca_cfg->burst_cnt << 8);
if (plca_cfg->burst_tmr >= 0)
val = (val & ~MDIO_OATC14_PLCA_BTMR) |
(plca_cfg->burst_tmr);
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
MDIO_OATC14_PLCA_BURST, val);
if (ret < 0)
return ret;
}
// if we need to enable PLCA, do it at the end
if (plca_cfg->enabled > 0) {
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
MDIO_OATC14_PLCA_CTRL0,
MDIO_OATC14_PLCA_EN);
if (ret < 0)
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(genphy_c45_plca_set_cfg);
/**
* genphy_c45_plca_get_status - get PLCA status from standard registers
* @phydev: target phy_device struct
* @plca_st: output structure to store the PLCA status
*
* Description: if the PHY complies to the Open Alliance TC14 10BASE-T1S PLCA
* Management Registers specifications, this function can be used to retrieve
* the current PLCA status information from the standard registers in MMD 31.
*/
int genphy_c45_plca_get_status(struct phy_device *phydev,
struct phy_plca_status *plca_st)
{
int ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_STATUS);
if (ret < 0)
return ret;
plca_st->pst = !!(ret & MDIO_OATC14_PLCA_PST);
return 0;
}
EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);
struct phy_driver genphy_c45_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
......
......@@ -13,7 +13,7 @@
*/
const char *phy_speed_to_str(int speed)
{
BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 99,
BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 102,
"Enum ethtool_link_mode_bit_indices and phylib are out of sync. "
"If a speed or mode has been added please update phy_speed_to_str "
"and the PHY settings array.\n");
......@@ -260,6 +260,9 @@ static const struct phy_setting settings[] = {
PHY_SETTING( 10, FULL, 10baseT_Full ),
PHY_SETTING( 10, HALF, 10baseT_Half ),
PHY_SETTING( 10, FULL, 10baseT1L_Full ),
PHY_SETTING( 10, FULL, 10baseT1S_Full ),
PHY_SETTING( 10, HALF, 10baseT1S_Half ),
PHY_SETTING( 10, HALF, 10baseT1S_P2MP_Half ),
};
#undef PHY_SETTING
......
......@@ -543,6 +543,198 @@ int phy_ethtool_get_stats(struct phy_device *phydev,
}
EXPORT_SYMBOL(phy_ethtool_get_stats);
/**
* phy_ethtool_get_plca_cfg - Get PLCA RS configuration
* @phydev: the phy_device struct
* @plca_cfg: where to store the retrieved configuration
*
* Retrieve the PLCA configuration from the PHY. Return 0 on success or a
* negative value if an error occurred.
*/
int phy_ethtool_get_plca_cfg(struct phy_device *phydev,
struct phy_plca_cfg *plca_cfg)
{
int ret;
if (!phydev->drv) {
ret = -EIO;
goto out;
}
if (!phydev->drv->get_plca_cfg) {
ret = -EOPNOTSUPP;
goto out;
}
mutex_lock(&phydev->lock);
ret = phydev->drv->get_plca_cfg(phydev, plca_cfg);
mutex_unlock(&phydev->lock);
out:
return ret;
}
/**
* plca_check_valid - Check PLCA configuration before enabling
* @phydev: the phy_device struct
* @plca_cfg: current PLCA configuration
* @extack: extack for reporting useful error messages
*
* Checks whether the PLCA and PHY configuration are consistent and it is safe
* to enable PLCA. Returns 0 on success or a negative value if the PLCA or PHY
* configuration is not consistent.
*/
static int plca_check_valid(struct phy_device *phydev,
const struct phy_plca_cfg *plca_cfg,
struct netlink_ext_ack *extack)
{
int ret = 0;
if (!linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
phydev->advertising)) {
ret = -EOPNOTSUPP;
NL_SET_ERR_MSG(extack,
"Point to Multi-Point mode is not enabled");
} else if (plca_cfg->node_id >= 255) {
NL_SET_ERR_MSG(extack, "PLCA node ID is not set");
ret = -EINVAL;
}
return ret;
}
/**
* phy_ethtool_set_plca_cfg - Set PLCA RS configuration
* @phydev: the phy_device struct
* @plca_cfg: new PLCA configuration to apply
* @extack: extack for reporting useful error messages
*
* Sets the PLCA configuration in the PHY. Return 0 on success or a
* negative value if an error occurred.
*/
int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
const struct phy_plca_cfg *plca_cfg,
struct netlink_ext_ack *extack)
{
struct phy_plca_cfg *curr_plca_cfg;
int ret;
if (!phydev->drv) {
ret = -EIO;
goto out;
}
if (!phydev->drv->set_plca_cfg ||
!phydev->drv->get_plca_cfg) {
ret = -EOPNOTSUPP;
goto out;
}
curr_plca_cfg = kmalloc(sizeof(*curr_plca_cfg), GFP_KERNEL);
if (!curr_plca_cfg) {
ret = -ENOMEM;
goto out;
}
mutex_lock(&phydev->lock);
ret = phydev->drv->get_plca_cfg(phydev, curr_plca_cfg);
if (ret)
goto out_drv;
if (curr_plca_cfg->enabled < 0 && plca_cfg->enabled >= 0) {
NL_SET_ERR_MSG(extack,
"PHY does not support changing the PLCA 'enable' attribute");
ret = -EINVAL;
goto out_drv;
}
if (curr_plca_cfg->node_id < 0 && plca_cfg->node_id >= 0) {
NL_SET_ERR_MSG(extack,
"PHY does not support changing the PLCA 'local node ID' attribute");
ret = -EINVAL;
goto out_drv;
}
if (curr_plca_cfg->node_cnt < 0 && plca_cfg->node_cnt >= 0) {
NL_SET_ERR_MSG(extack,
"PHY does not support changing the PLCA 'node count' attribute");
ret = -EINVAL;
goto out_drv;
}
if (curr_plca_cfg->to_tmr < 0 && plca_cfg->to_tmr >= 0) {
NL_SET_ERR_MSG(extack,
"PHY does not support changing the PLCA 'TO timer' attribute");
ret = -EINVAL;
goto out_drv;
}
if (curr_plca_cfg->burst_cnt < 0 && plca_cfg->burst_cnt >= 0) {
NL_SET_ERR_MSG(extack,
"PHY does not support changing the PLCA 'burst count' attribute");
ret = -EINVAL;
goto out_drv;
}
if (curr_plca_cfg->burst_tmr < 0 && plca_cfg->burst_tmr >= 0) {
NL_SET_ERR_MSG(extack,
"PHY does not support changing the PLCA 'burst timer' attribute");
ret = -EINVAL;
goto out_drv;
}
// if enabling PLCA, perform a few sanity checks
if (plca_cfg->enabled > 0) {
// allow setting node_id concurrently with enabled
if (plca_cfg->node_id >= 0)
curr_plca_cfg->node_id = plca_cfg->node_id;
ret = plca_check_valid(phydev, curr_plca_cfg, extack);
if (ret)
goto out_drv;
}
ret = phydev->drv->set_plca_cfg(phydev, plca_cfg);
out_drv:
kfree(curr_plca_cfg);
mutex_unlock(&phydev->lock);
out:
return ret;
}
/**
* phy_ethtool_get_plca_status - Get PLCA RS status information
* @phydev: the phy_device struct
* @plca_st: where to store the retrieved status information
*
* Retrieve the PLCA status information from the PHY. Return 0 on success or a
* negative value if an error occurred.
*/
int phy_ethtool_get_plca_status(struct phy_device *phydev,
struct phy_plca_status *plca_st)
{
int ret;
if (!phydev->drv) {
ret = -EIO;
goto out;
}
if (!phydev->drv->get_plca_status) {
ret = -EOPNOTSUPP;
goto out;
}
mutex_lock(&phydev->lock);
ret = phydev->drv->get_plca_status(phydev, plca_st);
mutex_unlock(&phydev->lock);
out:
return ret;
}
/**
* phy_start_cable_test - Start a cable test
*
......
......@@ -45,6 +45,9 @@ EXPORT_SYMBOL_GPL(phy_basic_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_basic_t1_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1s_p2mp_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_basic_t1s_p2mp_features);
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_gbit_features);
......@@ -98,6 +101,12 @@ const int phy_basic_t1_features_array[3] = {
};
EXPORT_SYMBOL_GPL(phy_basic_t1_features_array);
const int phy_basic_t1s_p2mp_features_array[2] = {
ETHTOOL_LINK_MODE_TP_BIT,
ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
};
EXPORT_SYMBOL_GPL(phy_basic_t1s_p2mp_features_array);
const int phy_gbit_features_array[2] = {
ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
......@@ -138,6 +147,11 @@ static void features_init(void)
ARRAY_SIZE(phy_basic_t1_features_array),
phy_basic_t1_features);
/* 10 half, P2MP, TP */
linkmode_set_bit_array(phy_basic_t1s_p2mp_features_array,
ARRAY_SIZE(phy_basic_t1s_p2mp_features_array),
phy_basic_t1s_p2mp_features);
/* 10/100 half/full + 1000 half/full */
linkmode_set_bit_array(phy_basic_ports_array,
ARRAY_SIZE(phy_basic_ports_array),
......@@ -3269,6 +3283,9 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
.get_sset_count = phy_ethtool_get_sset_count,
.get_strings = phy_ethtool_get_strings,
.get_stats = phy_ethtool_get_stats,
.get_plca_cfg = phy_ethtool_get_plca_cfg,
.set_plca_cfg = phy_ethtool_set_plca_cfg,
.get_plca_status = phy_ethtool_get_plca_status,
.start_cable_test = phy_start_cable_test,
.start_cable_test_tdr = phy_start_cable_test_tdr,
};
......
......@@ -241,12 +241,16 @@ void phylink_caps_to_linkmodes(unsigned long *linkmodes, unsigned long caps)
if (caps & MAC_ASYM_PAUSE)
__set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, linkmodes);
if (caps & MAC_10HD)
if (caps & MAC_10HD) {
__set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1S_Half_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, linkmodes);
}
if (caps & MAC_10FD) {
__set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, linkmodes);
__set_bit(ETHTOOL_LINK_MODE_10baseT1S_Full_BIT, linkmodes);
}
if (caps & MAC_100HD) {
......
......@@ -802,12 +802,17 @@ int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
struct phy_device;
struct phy_tdr_config;
struct phy_plca_cfg;
struct phy_plca_status;
/**
* struct ethtool_phy_ops - Optional PHY device options
* @get_sset_count: Get number of strings that @get_strings will write.
* @get_strings: Return a set of strings that describe the requested objects
* @get_stats: Return extended statistics about the PHY device.
* @get_plca_cfg: Return PLCA configuration.
* @set_plca_cfg: Set PLCA configuration.
* @get_plca_status: Get PLCA configuration.
* @start_cable_test: Start a cable test
* @start_cable_test_tdr: Start a Time Domain Reflectometry cable test
*
......@@ -819,6 +824,13 @@ struct ethtool_phy_ops {
int (*get_strings)(struct phy_device *dev, u8 *data);
int (*get_stats)(struct phy_device *dev,
struct ethtool_stats *stats, u64 *data);
int (*get_plca_cfg)(struct phy_device *dev,
struct phy_plca_cfg *plca_cfg);
int (*set_plca_cfg)(struct phy_device *dev,
const struct phy_plca_cfg *plca_cfg,
struct netlink_ext_ack *extack);
int (*get_plca_status)(struct phy_device *dev,
struct phy_plca_status *plca_st);
int (*start_cable_test)(struct phy_device *phydev,
struct netlink_ext_ack *extack);
int (*start_cable_test_tdr)(struct phy_device *phydev,
......
......@@ -45,6 +45,7 @@
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_t1s_p2mp_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_fibre_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_all_ports_features) __ro_after_init;
......@@ -54,6 +55,7 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_ini
#define PHY_BASIC_FEATURES ((unsigned long *)&phy_basic_features)
#define PHY_BASIC_T1_FEATURES ((unsigned long *)&phy_basic_t1_features)
#define PHY_BASIC_T1S_P2MP_FEATURES ((unsigned long *)&phy_basic_t1s_p2mp_features)
#define PHY_GBIT_FEATURES ((unsigned long *)&phy_gbit_features)
#define PHY_GBIT_FIBRE_FEATURES ((unsigned long *)&phy_gbit_fibre_features)
#define PHY_GBIT_ALL_PORTS_FEATURES ((unsigned long *)&phy_gbit_all_ports_features)
......@@ -66,6 +68,7 @@ extern const int phy_fibre_port_array[1];
extern const int phy_all_ports_features_array[7];
extern const int phy_10_100_features_array[4];
extern const int phy_basic_t1_features_array[3];
extern const int phy_basic_t1s_p2mp_features_array[2];
extern const int phy_gbit_features_array[2];
extern const int phy_10gbit_features_array[1];
......@@ -773,6 +776,63 @@ struct phy_tdr_config {
};
#define PHY_PAIR_ALL -1
/**
* struct phy_plca_cfg - Configuration of the PLCA (Physical Layer Collision
* Avoidance) Reconciliation Sublayer.
*
* @version: read-only PLCA register map version. -1 = not available. Ignored
* when setting the configuration. Format is the same as reported by the PLCA
* IDVER register (31.CA00). -1 = not available.
* @enabled: PLCA configured mode (enabled/disabled). -1 = not available / don't
* set. 0 = disabled, anything else = enabled.
* @node_id: the PLCA local node identifier. -1 = not available / don't set.
* Allowed values [0 .. 254]. 255 = node disabled.
* @node_cnt: the PLCA node count (maximum number of nodes having a TO). Only
* meaningful for the coordinator (node_id = 0). -1 = not available / don't
* set. Allowed values [1 .. 255].
* @to_tmr: The value of the PLCA to_timer in bit-times, which determines the
* PLCA transmit opportunity window opening. See IEEE802.3 Clause 148 for
* more details. The to_timer shall be set equal over all nodes.
* -1 = not available / don't set. Allowed values [0 .. 255].
* @burst_cnt: controls how many additional frames a node is allowed to send in
* single transmit opportunity (TO). The default value of 0 means that the
* node is allowed exactly one frame per TO. A value of 1 allows two frames
* per TO, and so on. -1 = not available / don't set.
* Allowed values [0 .. 255].
* @burst_tmr: controls how many bit times to wait for the MAC to send a new
* frame before interrupting the burst. This value should be set to a value
* greater than the MAC inter-packet gap (which is typically 96 bits).
* -1 = not available / don't set. Allowed values [0 .. 255].
*
* A structure containing configuration parameters for setting/getting the PLCA
* RS configuration. The driver does not need to implement all the parameters,
* but should report what is actually used.
*/
struct phy_plca_cfg {
int version;
int enabled;
int node_id;
int node_cnt;
int to_tmr;
int burst_cnt;
int burst_tmr;
};
/**
* struct phy_plca_status - Status of the PLCA (Physical Layer Collision
* Avoidance) Reconciliation Sublayer.
*
* @pst: The PLCA status as reported by the PST bit in the PLCA STATUS
* register(31.CA03), indicating BEACON activity.
*
* A structure containing status information of the PLCA RS configuration.
* The driver does not need to implement all the parameters, but should report
* what is actually used.
*/
struct phy_plca_status {
bool pst;
};
/**
* struct phy_driver - Driver structure for a particular PHY type
*
......@@ -984,6 +1044,17 @@ struct phy_driver {
int (*get_sqi)(struct phy_device *dev);
/** @get_sqi_max: Get the maximum signal quality indication */
int (*get_sqi_max)(struct phy_device *dev);
/* PLCA RS interface */
/** @get_plca_cfg: Return the current PLCA configuration */
int (*get_plca_cfg)(struct phy_device *dev,
struct phy_plca_cfg *plca_cfg);
/** @set_plca_cfg: Set the PLCA configuration */
int (*set_plca_cfg)(struct phy_device *dev,
const struct phy_plca_cfg *plca_cfg);
/** @get_plca_status: Return the current PLCA status info */
int (*get_plca_status)(struct phy_device *dev,
struct phy_plca_status *plca_st);
};
#define to_phy_driver(d) container_of(to_mdio_common_driver(d), \
struct phy_driver, mdiodrv)
......@@ -1682,6 +1753,12 @@ int genphy_c45_loopback(struct phy_device *phydev, bool enable);
int genphy_c45_pma_resume(struct phy_device *phydev);
int genphy_c45_pma_suspend(struct phy_device *phydev);
int genphy_c45_fast_retrain(struct phy_device *phydev, bool enable);
int genphy_c45_plca_get_cfg(struct phy_device *phydev,
struct phy_plca_cfg *plca_cfg);
int genphy_c45_plca_set_cfg(struct phy_device *phydev,
const struct phy_plca_cfg *plca_cfg);
int genphy_c45_plca_get_status(struct phy_device *phydev,
struct phy_plca_status *plca_st);
/* Generic C45 PHY driver */
extern struct phy_driver genphy_c45_driver;
......@@ -1780,6 +1857,13 @@ int phy_ethtool_get_strings(struct phy_device *phydev, u8 *data);
int phy_ethtool_get_sset_count(struct phy_device *phydev);
int phy_ethtool_get_stats(struct phy_device *phydev,
struct ethtool_stats *stats, u64 *data);
int phy_ethtool_get_plca_cfg(struct phy_device *phydev,
struct phy_plca_cfg *plca_cfg);
int phy_ethtool_set_plca_cfg(struct phy_device *phydev,
const struct phy_plca_cfg *plca_cfg,
struct netlink_ext_ack *extack);
int phy_ethtool_get_plca_status(struct phy_device *phydev,
struct phy_plca_status *plca_st);
static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
{
......
......@@ -1741,6 +1741,9 @@ enum ethtool_link_mode_bit_indices {
ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT = 96,
ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT = 97,
ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT = 98,
ETHTOOL_LINK_MODE_10baseT1S_Full_BIT = 99,
ETHTOOL_LINK_MODE_10baseT1S_Half_BIT = 100,
ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT = 101,
/* must be last entry */
__ETHTOOL_LINK_MODE_MASK_NBITS
......
......@@ -52,6 +52,9 @@ enum {
ETHTOOL_MSG_PSE_GET,
ETHTOOL_MSG_PSE_SET,
ETHTOOL_MSG_RSS_GET,
ETHTOOL_MSG_PLCA_GET_CFG,
ETHTOOL_MSG_PLCA_SET_CFG,
ETHTOOL_MSG_PLCA_GET_STATUS,
/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
......@@ -99,6 +102,9 @@ enum {
ETHTOOL_MSG_MODULE_NTF,
ETHTOOL_MSG_PSE_GET_REPLY,
ETHTOOL_MSG_RSS_GET_REPLY,
ETHTOOL_MSG_PLCA_GET_CFG_REPLY,
ETHTOOL_MSG_PLCA_GET_STATUS_REPLY,
ETHTOOL_MSG_PLCA_NTF,
/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
......@@ -894,6 +900,25 @@ enum {
ETHTOOL_A_RSS_MAX = (__ETHTOOL_A_RSS_CNT - 1),
};
/* PLCA */
enum {
ETHTOOL_A_PLCA_UNSPEC,
ETHTOOL_A_PLCA_HEADER, /* nest - _A_HEADER_* */
ETHTOOL_A_PLCA_VERSION, /* u16 */
ETHTOOL_A_PLCA_ENABLED, /* u8 */
ETHTOOL_A_PLCA_STATUS, /* u8 */
ETHTOOL_A_PLCA_NODE_CNT, /* u32 */
ETHTOOL_A_PLCA_NODE_ID, /* u32 */
ETHTOOL_A_PLCA_TO_TMR, /* u32 */
ETHTOOL_A_PLCA_BURST_CNT, /* u32 */
ETHTOOL_A_PLCA_BURST_TMR, /* u32 */
/* add new constants above here */
__ETHTOOL_A_PLCA_CNT,
ETHTOOL_A_PLCA_MAX = (__ETHTOOL_A_PLCA_CNT - 1)
};
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
......
......@@ -8,4 +8,4 @@ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
linkstate.o debug.o wol.o features.o privflags.o rings.o \
channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
tunnels.o fec.o eeprom.o stats.o phc_vclocks.o module.o \
pse-pd.o
pse-pd.o plca.o
......@@ -208,6 +208,9 @@ const char link_mode_names[][ETH_GSTRING_LEN] = {
__DEFINE_LINK_MODE_NAME(800000, DR8_2, Full),
__DEFINE_LINK_MODE_NAME(800000, SR8, Full),
__DEFINE_LINK_MODE_NAME(800000, VR8, Full),
__DEFINE_LINK_MODE_NAME(10, T1S, Full),
__DEFINE_LINK_MODE_NAME(10, T1S, Half),
__DEFINE_LINK_MODE_NAME(10, T1S_P2MP, Half),
};
static_assert(ARRAY_SIZE(link_mode_names) == __ETHTOOL_LINK_MODE_MASK_NBITS);
......@@ -244,6 +247,8 @@ static_assert(ARRAY_SIZE(link_mode_names) == __ETHTOOL_LINK_MODE_MASK_NBITS);
#define __LINK_MODE_LANES_X 1
#define __LINK_MODE_LANES_FX 1
#define __LINK_MODE_LANES_T1L 1
#define __LINK_MODE_LANES_T1S 1
#define __LINK_MODE_LANES_T1S_P2MP 1
#define __LINK_MODE_LANES_VR8 8
#define __LINK_MODE_LANES_DR8_2 8
......@@ -366,6 +371,9 @@ const struct link_mode_info link_mode_params[] = {
__DEFINE_LINK_MODE_PARAMS(800000, DR8_2, Full),
__DEFINE_LINK_MODE_PARAMS(800000, SR8, Full),
__DEFINE_LINK_MODE_PARAMS(800000, VR8, Full),
__DEFINE_LINK_MODE_PARAMS(10, T1S, Full),
__DEFINE_LINK_MODE_PARAMS(10, T1S, Half),
__DEFINE_LINK_MODE_PARAMS(10, T1S_P2MP, Half),
};
static_assert(ARRAY_SIZE(link_mode_params) == __ETHTOOL_LINK_MODE_MASK_NBITS);
......
......@@ -288,6 +288,8 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_MODULE_GET] = &ethnl_module_request_ops,
[ETHTOOL_MSG_PSE_GET] = &ethnl_pse_request_ops,
[ETHTOOL_MSG_RSS_GET] = &ethnl_rss_request_ops,
[ETHTOOL_MSG_PLCA_GET_CFG] = &ethnl_plca_cfg_request_ops,
[ETHTOOL_MSG_PLCA_GET_STATUS] = &ethnl_plca_status_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
......@@ -603,6 +605,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = {
[ETHTOOL_MSG_EEE_NTF] = &ethnl_eee_request_ops,
[ETHTOOL_MSG_FEC_NTF] = &ethnl_fec_request_ops,
[ETHTOOL_MSG_MODULE_NTF] = &ethnl_module_request_ops,
[ETHTOOL_MSG_PLCA_NTF] = &ethnl_plca_cfg_request_ops,
};
/* default notification handler */
......@@ -696,6 +699,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = {
[ETHTOOL_MSG_EEE_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_FEC_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_MODULE_NTF] = ethnl_default_notify,
[ETHTOOL_MSG_PLCA_NTF] = ethnl_default_notify,
};
void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data)
......@@ -1047,6 +1051,31 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_rss_get_policy,
.maxattr = ARRAY_SIZE(ethnl_rss_get_policy) - 1,
},
{
.cmd = ETHTOOL_MSG_PLCA_GET_CFG,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
.policy = ethnl_plca_get_cfg_policy,
.maxattr = ARRAY_SIZE(ethnl_plca_get_cfg_policy) - 1,
},
{
.cmd = ETHTOOL_MSG_PLCA_SET_CFG,
.flags = GENL_UNS_ADMIN_PERM,
.doit = ethnl_set_plca_cfg,
.policy = ethnl_plca_set_cfg_policy,
.maxattr = ARRAY_SIZE(ethnl_plca_set_cfg_policy) - 1,
},
{
.cmd = ETHTOOL_MSG_PLCA_GET_STATUS,
.doit = ethnl_default_doit,
.start = ethnl_default_start,
.dumpit = ethnl_default_dumpit,
.done = ethnl_default_done,
.policy = ethnl_plca_get_status_policy,
.maxattr = ARRAY_SIZE(ethnl_plca_get_status_policy) - 1,
},
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
......
......@@ -347,6 +347,8 @@ extern const struct ethnl_request_ops ethnl_phc_vclocks_request_ops;
extern const struct ethnl_request_ops ethnl_module_request_ops;
extern const struct ethnl_request_ops ethnl_pse_request_ops;
extern const struct ethnl_request_ops ethnl_rss_request_ops;
extern const struct ethnl_request_ops ethnl_plca_cfg_request_ops;
extern const struct ethnl_request_ops ethnl_plca_status_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
......@@ -388,6 +390,9 @@ extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MO
extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1];
extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1];
extern const struct nla_policy ethnl_rss_get_policy[ETHTOOL_A_RSS_CONTEXT + 1];
extern const struct nla_policy ethnl_plca_get_cfg_policy[ETHTOOL_A_PLCA_HEADER + 1];
extern const struct nla_policy ethnl_plca_set_cfg_policy[ETHTOOL_A_PLCA_MAX + 1];
extern const struct nla_policy ethnl_plca_get_status_policy[ETHTOOL_A_PLCA_HEADER + 1];
int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);
......@@ -408,6 +413,7 @@ int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_module(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_pse(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_plca_cfg(struct sk_buff *skb, struct genl_info *info);
extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN];
extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
......
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/phy.h>
#include <linux/ethtool_netlink.h>
#include "netlink.h"
#include "common.h"
struct plca_req_info {
struct ethnl_req_info base;
};
struct plca_reply_data {
struct ethnl_reply_data base;
struct phy_plca_cfg plca_cfg;
struct phy_plca_status plca_st;
};
// Helpers ------------------------------------------------------------------ //
#define PLCA_REPDATA(__reply_base) \
container_of(__reply_base, struct plca_reply_data, base)
static void plca_update_sint(int *dst, const struct nlattr *attr,
bool *mod)
{
if (!attr)
return;
*dst = nla_get_u32(attr);
*mod = true;
}
// PLCA get configuration message ------------------------------------------- //
const struct nla_policy ethnl_plca_get_cfg_policy[] = {
[ETHTOOL_A_PLCA_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy),
};
static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct plca_reply_data *data = PLCA_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
const struct ethtool_phy_ops *ops;
int ret;
// check that the PHY device is available and connected
if (!dev->phydev) {
ret = -EOPNOTSUPP;
goto out;
}
// note: rtnl_lock is held already by ethnl_default_doit
ops = ethtool_phy_ops;
if (!ops || !ops->get_plca_cfg) {
ret = -EOPNOTSUPP;
goto out;
}
ret = ethnl_ops_begin(dev);
if (!ret)
goto out;
memset(&data->plca_cfg, 0xff,
sizeof_field(struct plca_reply_data, plca_cfg));
ret = ops->get_plca_cfg(dev->phydev, &data->plca_cfg);
ethnl_ops_complete(dev);
out:
return ret;
}
static int plca_get_cfg_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
return nla_total_size(sizeof(u16)) + /* _VERSION */
nla_total_size(sizeof(u8)) + /* _ENABLED */
nla_total_size(sizeof(u32)) + /* _NODE_CNT */
nla_total_size(sizeof(u32)) + /* _NODE_ID */
nla_total_size(sizeof(u32)) + /* _TO_TIMER */
nla_total_size(sizeof(u32)) + /* _BURST_COUNT */
nla_total_size(sizeof(u32)); /* _BURST_TIMER */
}
static int plca_get_cfg_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct plca_reply_data *data = PLCA_REPDATA(reply_base);
const struct phy_plca_cfg *plca = &data->plca_cfg;
if ((plca->version >= 0 &&
nla_put_u16(skb, ETHTOOL_A_PLCA_VERSION, plca->version)) ||
(plca->enabled >= 0 &&
nla_put_u8(skb, ETHTOOL_A_PLCA_ENABLED, !!plca->enabled)) ||
(plca->node_id >= 0 &&
nla_put_u32(skb, ETHTOOL_A_PLCA_NODE_ID, plca->node_id)) ||
(plca->node_cnt >= 0 &&
nla_put_u32(skb, ETHTOOL_A_PLCA_NODE_CNT, plca->node_cnt)) ||
(plca->to_tmr >= 0 &&
nla_put_u32(skb, ETHTOOL_A_PLCA_TO_TMR, plca->to_tmr)) ||
(plca->burst_cnt >= 0 &&
nla_put_u32(skb, ETHTOOL_A_PLCA_BURST_CNT, plca->burst_cnt)) ||
(plca->burst_tmr >= 0 &&
nla_put_u32(skb, ETHTOOL_A_PLCA_BURST_TMR, plca->burst_tmr)))
return -EMSGSIZE;
return 0;
};
const struct ethnl_request_ops ethnl_plca_cfg_request_ops = {
.request_cmd = ETHTOOL_MSG_PLCA_GET_CFG,
.reply_cmd = ETHTOOL_MSG_PLCA_GET_CFG_REPLY,
.hdr_attr = ETHTOOL_A_PLCA_HEADER,
.req_info_size = sizeof(struct plca_req_info),
.reply_data_size = sizeof(struct plca_reply_data),
.prepare_data = plca_get_cfg_prepare_data,
.reply_size = plca_get_cfg_reply_size,
.fill_reply = plca_get_cfg_fill_reply,
};
// PLCA set configuration message ------------------------------------------- //
const struct nla_policy ethnl_plca_set_cfg_policy[] = {
[ETHTOOL_A_PLCA_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy),
[ETHTOOL_A_PLCA_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
[ETHTOOL_A_PLCA_NODE_ID] = NLA_POLICY_MAX(NLA_U32, 255),
[ETHTOOL_A_PLCA_NODE_CNT] = NLA_POLICY_RANGE(NLA_U32, 1, 255),
[ETHTOOL_A_PLCA_TO_TMR] = NLA_POLICY_MAX(NLA_U32, 255),
[ETHTOOL_A_PLCA_BURST_CNT] = NLA_POLICY_MAX(NLA_U32, 255),
[ETHTOOL_A_PLCA_BURST_TMR] = NLA_POLICY_MAX(NLA_U32, 255),
};
int ethnl_set_plca_cfg(struct sk_buff *skb, struct genl_info *info)
{
struct ethnl_req_info req_info = {};
struct nlattr **tb = info->attrs;
const struct ethtool_phy_ops *ops;
struct phy_plca_cfg plca_cfg;
struct net_device *dev;
bool mod = false;
int ret;
ret = ethnl_parse_header_dev_get(&req_info,
tb[ETHTOOL_A_PLCA_HEADER],
genl_info_net(info), info->extack,
true);
if (!ret)
return ret;
dev = req_info.dev;
rtnl_lock();
// check that the PHY device is available and connected
if (!dev->phydev) {
ret = -EOPNOTSUPP;
goto out_rtnl;
}
ops = ethtool_phy_ops;
if (!ops || !ops->set_plca_cfg) {
ret = -EOPNOTSUPP;
goto out_rtnl;
}
ret = ethnl_ops_begin(dev);
if (!ret)
goto out_rtnl;
memset(&plca_cfg, 0xff, sizeof(plca_cfg));
plca_update_sint(&plca_cfg.enabled, tb[ETHTOOL_A_PLCA_ENABLED], &mod);
plca_update_sint(&plca_cfg.node_id, tb[ETHTOOL_A_PLCA_NODE_ID], &mod);
plca_update_sint(&plca_cfg.node_cnt, tb[ETHTOOL_A_PLCA_NODE_CNT], &mod);
plca_update_sint(&plca_cfg.to_tmr, tb[ETHTOOL_A_PLCA_TO_TMR], &mod);
plca_update_sint(&plca_cfg.burst_cnt, tb[ETHTOOL_A_PLCA_BURST_CNT],
&mod);
plca_update_sint(&plca_cfg.burst_tmr, tb[ETHTOOL_A_PLCA_BURST_TMR],
&mod);
ret = 0;
if (!mod)
goto out_ops;
ret = ops->set_plca_cfg(dev->phydev, &plca_cfg, info->extack);
if (!ret)
goto out_ops;
ethtool_notify(dev, ETHTOOL_MSG_PLCA_NTF, NULL);
out_ops:
ethnl_ops_complete(dev);
out_rtnl:
rtnl_unlock();
ethnl_parse_header_dev_put(&req_info);
return ret;
}
// PLCA get status message -------------------------------------------------- //
const struct nla_policy ethnl_plca_get_status_policy[] = {
[ETHTOOL_A_PLCA_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy),
};
static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base,
struct ethnl_reply_data *reply_base,
struct genl_info *info)
{
struct plca_reply_data *data = PLCA_REPDATA(reply_base);
struct net_device *dev = reply_base->dev;
const struct ethtool_phy_ops *ops;
int ret;
// check that the PHY device is available and connected
if (!dev->phydev) {
ret = -EOPNOTSUPP;
goto out;
}
// note: rtnl_lock is held already by ethnl_default_doit
ops = ethtool_phy_ops;
if (!ops || !ops->get_plca_status) {
ret = -EOPNOTSUPP;
goto out;
}
ret = ethnl_ops_begin(dev);
if (!ret)
goto out;
memset(&data->plca_st, 0xff,
sizeof_field(struct plca_reply_data, plca_st));
ret = ops->get_plca_status(dev->phydev, &data->plca_st);
ethnl_ops_complete(dev);
out:
return ret;
}
static int plca_get_status_reply_size(const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
return nla_total_size(sizeof(u8)); /* _STATUS */
}
static int plca_get_status_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base)
{
const struct plca_reply_data *data = PLCA_REPDATA(reply_base);
const u8 status = data->plca_st.pst;
if (nla_put_u8(skb, ETHTOOL_A_PLCA_STATUS, !!status))
return -EMSGSIZE;
return 0;
};
const struct ethnl_request_ops ethnl_plca_status_request_ops = {
.request_cmd = ETHTOOL_MSG_PLCA_GET_STATUS,
.reply_cmd = ETHTOOL_MSG_PLCA_GET_STATUS_REPLY,
.hdr_attr = ETHTOOL_A_PLCA_HEADER,
.req_info_size = sizeof(struct plca_req_info),
.reply_data_size = sizeof(struct plca_reply_data),
.prepare_data = plca_get_status_prepare_data,
.reply_size = plca_get_status_reply_size,
.fill_reply = plca_get_status_fill_reply,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment