drivers/net/phy: add helpers to get/set PLCA configuration

This patch adds support in phylib to read/write PLCA configuration for
Ethernet PHYs that support the OPEN Alliance "10BASE-T1S PLCA
Management Registers" specifications. These can be found at
https://www.opensig.org/about/specifications/

Signed-off-by: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Piergiorgio Beruto 2023-01-09 18:00:23 +01:00 committed by David S. Miller
parent a23a1e57a6
commit 493323416f
4 changed files with 246 additions and 0 deletions

View File

@ -16620,6 +16620,7 @@ 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

View File

@ -0,0 +1,46 @@
/* 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__ */

View File

@ -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,

View File

@ -1753,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;