Commit 49332341 authored by Piergiorgio Beruto's avatar Piergiorgio Beruto Committed by David S. Miller
Browse files

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: default avatarPiergiorgio Beruto <piergiorgio.beruto@gmail.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a23a1e57
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+46 −0
Original line number Diff line number Diff line
/* 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__ */
+193 −0
Original line number Diff line number Diff line
@@ -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,
+6 −0
Original line number Diff line number Diff line
@@ -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;