From: Oleksij Rempel <o.rempel@pengutronix.de>
To: alexandru.tachici@analog.com
Cc: andrew@lunn.ch, davem@davemloft.net, devicetree@vger.kernel.org,
hkallweit1@gmail.com, kuba@kernel.org,
linux-kernel@vger.kernel.org, linux@armlinux.org.uk,
netdev@vger.kernel.org, robh+dt@kernel.org,
Alexandru Ardelean <alexandru.ardelean@analog.com>
Subject: Re: [PATCH v3 4/8] net: phy: adin1100: Add initial support for ADIN1100 industrial PHY
Date: Tue, 12 Oct 2021 10:29:08 +0200 [thread overview]
Message-ID: <20211012082908.GD938@pengutronix.de> (raw)
In-Reply-To: <20211011142215.9013-5-alexandru.tachici@analog.com>
On Mon, Oct 11, 2021 at 05:22:11PM +0300, alexandru.tachici@analog.com wrote:
> From: Alexandru Ardelean <alexandru.ardelean@analog.com>
>
> The ADIN1100 is a low power single port 10BASE-T1L transceiver designed for
> industrial Ethernet applications and is compliant with the IEEE 802.3cg
> Ethernet standard for long reach 10 Mb/s Single Pair Ethernet.
>
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> Signed-off-by: Alexandru Tachici <alexandru.tachici@analog.com>
> ---
> drivers/net/phy/Kconfig | 7 +
> drivers/net/phy/Makefile | 1 +
> drivers/net/phy/adin1100.c | 279 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 287 insertions(+)
> create mode 100644 drivers/net/phy/adin1100.c
>
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index 902495afcb38..2f65d39e0f2c 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -83,6 +83,13 @@ config ADIN_PHY
> - ADIN1300 - Robust,Industrial, Low Latency 10/100/1000 Gigabit
> Ethernet PHY
>
> +config ADIN1100_PHY
> + tristate "Analog Devices Industrial Ethernet T1L PHYs"
> + help
> + Adds support for the Analog Devices Industrial T1L Ethernet PHYs.
> + Currently supports the:
> + - ADIN1100 - Robust,Industrial, Low Power 10BASE-T1L Ethernet PHY
> +
> config AQUANTIA_PHY
> tristate "Aquantia PHYs"
> help
> diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
> index b2728d00fc9a..b82651b57043 100644
> --- a/drivers/net/phy/Makefile
> +++ b/drivers/net/phy/Makefile
> @@ -31,6 +31,7 @@ sfp-obj-$(CONFIG_SFP) += sfp-bus.o
> obj-y += $(sfp-obj-y) $(sfp-obj-m)
>
> obj-$(CONFIG_ADIN_PHY) += adin.o
> +obj-$(CONFIG_ADIN1100_PHY) += adin1100.o
> obj-$(CONFIG_AMD_PHY) += amd.o
> aquantia-objs += aquantia_main.o
> ifdef CONFIG_HWMON
> diff --git a/drivers/net/phy/adin1100.c b/drivers/net/phy/adin1100.c
> new file mode 100644
> index 000000000000..dc5c1987dc43
> --- /dev/null
> +++ b/drivers/net/phy/adin1100.c
> @@ -0,0 +1,279 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
> +/*
> + * Driver for Analog Devices Industrial Ethernet T1L PHYs
> + *
> + * Copyright 2020 Analog Devices Inc.
> + */
> +#include <linux/kernel.h>
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/mii.h>
> +#include <linux/phy.h>
> +#include <linux/property.h>
> +
> +#define PHY_ID_ADIN1100 0x0283bc81
> +
> +static const int phy_10_features_array[] = {
> + ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
> +};
> +
> +#define ADIN_CRSM_SFT_RST 0x8810
> +#define ADIN_CRSM_SFT_RST_EN BIT(0)
> +
> +#define ADIN_CRSM_SFT_PD_CNTRL 0x8812
> +#define ADIN_CRSM_SFT_PD_CNTRL_EN BIT(0)
> +
> +#define ADIN_CRSM_STAT 0x8818
> +#define ADIN_CRSM_SFT_PD_RDY BIT(1)
> +#define ADIN_CRSM_SYS_RDY BIT(0)
> +
> +/**
> + * struct adin_priv - ADIN PHY driver private data
> + * tx_level_2v4_able set if the PHY supports 2.4V TX levels (10BASE-T1L)
> + * tx_level_2v4 set if the PHY requests 2.4V TX levels (10BASE-T1L)
> + * tx_level_prop_present set if the TX level is specified in DT
> + */
> +struct adin_priv {
> + unsigned int tx_level_2v4_able:1;
> + unsigned int tx_level_2v4:1;
> + unsigned int tx_level_prop_present:1;
> +};
> +
> +static void adin_mii_adv_m_to_ethtool_adv_t(unsigned long *advertising, u32 adv)
> +{
> + if (adv & MDIO_AN_T1_ADV_M_B10L)
> + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, advertising);
> +}
Please extend genphy_c45_pma_read_abilities() to set 10baseT1L_Full_BIT.
It is already doing most of needed work:
..
if (val & MDIO_PMA_STAT2_EXTABLE) {
val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
// This is 45.2.1.10 PMA/PMD extended ability register (Register 1.11)
// You should test for bit 1.11.11 and read register register 1.18
// to set missing abilities.
> +static int adin_read_lpa(struct phy_device *phydev)
> +{
> + int val;
> +
> + linkmode_zero(phydev->lp_advertising);
> +
> + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_STAT);
> + if (val < 0)
> + return val;
> +
> + if (!(val & MDIO_AN_STAT1_COMPLETE)) {
> + phydev->pause = 0;
> + phydev->asym_pause = 0;
> +
> + return 0;
> + }
> +
> + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
> + phydev->lp_advertising);
> +
> + /* Read the link partner's base page advertisement */
> + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_L);
> + if (val < 0)
> + return val;
> +
> + phydev->pause = val & MDIO_AN_T1_LP_L_PAUSE_CAP ? 1 : 0;
> + phydev->asym_pause = val & MDIO_AN_T1_LP_L_PAUSE_ASYM ? 1 : 0;
> +
> + val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_M);
> + if (val < 0)
> + return val;
> +
> + adin_mii_adv_m_to_ethtool_adv_t(phydev->lp_advertising, val);
> +
> + return 0;
> +}
> +
> +static int adin_read_status(struct phy_device *phydev)
> +{
> + int ret;
> +
> + ret = genphy_c45_read_link(phydev);
> + if (ret)
> + return ret;
> +
> + phydev->speed = SPEED_UNKNOWN;
> + phydev->duplex = DUPLEX_UNKNOWN;
> + phydev->pause = 0;
> + phydev->asym_pause = 0;
> +
> + if (phydev->autoneg == AUTONEG_ENABLE) {
> + ret = adin_read_lpa(phydev);
> + if (ret)
> + return ret;
> +
> + phy_resolve_aneg_linkmode(phydev);
> + } else {
> + /* Only one mode & duplex supported */
> + linkmode_zero(phydev->lp_advertising);
> + phydev->speed = SPEED_10;
> + phydev->duplex = DUPLEX_FULL;
> + }
> +
> + return ret;
> +}
> +
> +static int adin_config_aneg(struct phy_device *phydev)
> +{
> + struct adin_priv *priv = phydev->priv;
> + int ret;
> +
> + /* No sense to continue if auto-neg is disabled,
> + * only one link-mode supported.
> + */
> + if (phydev->autoneg == AUTONEG_DISABLE)
> + return 0;
> +
> + /* Request increased transmit level from LP. */
> + if (priv->tx_level_prop_present && priv->tx_level_2v4) {
> + ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H,
> + MDIO_AN_T1_ADV_H_10L_TX_HI |
> + MDIO_AN_T1_ADV_H_10L_TX_HI_REQ);
> + if (ret < 0)
> + return ret;
> + }
> +
> + /* Disable 2.4 Vpp transmit level. */
> + if ((priv->tx_level_prop_present && !priv->tx_level_2v4) || !priv->tx_level_2v4_able) {
> + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_ADV_H,
> + MDIO_AN_T1_ADV_H_10L_TX_HI |
> + MDIO_AN_T1_ADV_H_10L_TX_HI_REQ);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_CTRL, BMCR_ANRESTART);
> +}
>
> +static int adin_set_powerdown_mode(struct phy_device *phydev, bool en)
> +{
> + int ret;
> + int val;
> +
> + if (en)
> + val = ADIN_CRSM_SFT_PD_CNTRL_EN;
> + else
> + val = 0;
> +
> + ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
> + ADIN_CRSM_SFT_PD_CNTRL, val);
> + if (ret < 0)
> + return ret;
> +
> + return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ADIN_CRSM_STAT, ret,
> + (ret & ADIN_CRSM_SFT_PD_RDY) == val,
> + 1000, 30000, true);
> +}
> +
> +static int adin_suspend(struct phy_device *phydev)
> +{
> + return adin_set_powerdown_mode(phydev, true);
> +}
> +
> +static int adin_resume(struct phy_device *phydev)
> +{
> + return adin_set_powerdown_mode(phydev, false);
> +}
> +
> +static int adin_set_loopback(struct phy_device *phydev, bool enable)
> +{
> + if (enable)
> + return phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL,
> + BMCR_LOOPBACK);
> +
> + /* PCS loopback (according to 10BASE-T1L spec) */
> + return phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_10T1L_CTRL,
> + BMCR_LOOPBACK);
> +}
> +
> +static int adin_soft_reset(struct phy_device *phydev)
> +{
> + int ret;
> +
> + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ADIN_CRSM_SFT_RST, ADIN_CRSM_SFT_RST_EN);
> + if (ret < 0)
> + return ret;
> +
> + return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ADIN_CRSM_STAT, ret,
> + (ret & ADIN_CRSM_SYS_RDY),
> + 10000, 30000, true);
> +}
> +
> +static int adin_get_features(struct phy_device *phydev)
> +{
> + struct adin_priv *priv = phydev->priv;
> + struct device *dev = &phydev->mdio.dev;
> + int ret;
> + u8 val;
> +
> + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10T1L_STAT);
> + if (ret < 0)
> + return ret;
> +
> + /* This depends on the voltage level from the power source */
> + priv->tx_level_2v4_able = !!(ret & MDIO_PMA_10T1L_STAT_2V4_ABLE);
> +
> + phydev_dbg(phydev, "PHY supports 2.4V TX level: %s\n",
> + priv->tx_level_2v4_able ? "yes" : "no");
> +
> + priv->tx_level_prop_present = device_property_present(dev, "10base-t1l-2.4vpp");
> + if (priv->tx_level_prop_present) {
> + ret = device_property_read_u8(dev, "10base-t1l-2.4vpp", &val);
> + if (ret < 0)
> + return ret;
> +
> + priv->tx_level_2v4 = val;
> + if (!priv->tx_level_2v4 && priv->tx_level_2v4_able)
> + phydev_info(phydev,
> + "PHY supports 2.4V TX level, but disabled via config\n");
> + }
> +
> + linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array),
> + phydev->supported);
> +
> + linkmode_set_bit_array(phy_10_features_array, ARRAY_SIZE(phy_10_features_array),
> + phydev->supported);
> +
> + return 0;
> +}
> +
> +static int adin_probe(struct phy_device *phydev)
> +{
> + struct device *dev = &phydev->mdio.dev;
> + struct adin_priv *priv;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + phydev->priv = priv;
> +
> + return 0;
> +}
> +
Without spending too much time on review right now, I would expect that
most of this code should got to the drivers/net/phy/phy-c45.c
> +static struct phy_driver adin_driver[] = {
> + {
> + PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100),
> + .name = "ADIN1100",
> + .get_features = adin_get_features,
> + .soft_reset = adin_soft_reset,
> + .probe = adin_probe,
> + .config_aneg = adin_config_aneg,
> + .read_status = adin_read_status,
> + .set_loopback = adin_set_loopback,
> + .suspend = adin_suspend,
> + .resume = adin_resume,
> + },
> +};
> +
> +module_phy_driver(adin_driver);
> +
> +static struct mdio_device_id __maybe_unused adin_tbl[] = {
> + { PHY_ID_MATCH_MODEL(PHY_ID_ADIN1100) },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(mdio, adin_tbl);
> +MODULE_DESCRIPTION("Analog Devices Industrial Ethernet T1L PHY driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> --
> 2.25.1
>
Regards,
Oleksij
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
next prev parent reply other threads:[~2021-10-12 8:29 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-11 14:22 [PATCH v3 0/8] net: phy: adin1100: Add initial support for ADIN1100 industrial PHY alexandru.tachici
2021-10-11 14:22 ` [PATCH v3 1/8] ethtool: Add 10base-T1L link mode entry alexandru.tachici
2021-10-12 7:15 ` Oleksij Rempel
2021-10-11 14:22 ` [PATCH v3 2/8] net: phy: Add 10-BaseT1L registers alexandru.tachici
2021-10-12 6:37 ` Oleksij Rempel
2021-10-11 14:22 ` [PATCH v3 3/8] net: phy: Add BaseT1 auto-negotiation registers alexandru.tachici
2021-10-12 7:14 ` Oleksij Rempel
2021-11-24 15:24 ` alexandru.tachici
2021-10-11 14:22 ` [PATCH v3 4/8] net: phy: adin1100: Add initial support for ADIN1100 industrial PHY alexandru.tachici
2021-10-11 15:24 ` Jakub Kicinski
2021-10-12 8:29 ` Oleksij Rempel [this message]
2021-10-11 14:22 ` [PATCH v3 5/8] net: phy: adin1100: Add ethtool master-slave support alexandru.tachici
2021-10-11 14:22 ` [PATCH v3 6/8] net: phy: adin1100: Add SQI support alexandru.tachici
2021-10-11 14:22 ` [PATCH v3 7/8] dt-bindings: net: phy: Add 10-baseT1L 2.4 Vpp alexandru.tachici
2021-10-18 19:06 ` Rob Herring
2021-10-19 5:48 ` Oleksij Rempel
2021-10-11 14:22 ` [PATCH v3 8/8] dt-bindings: adin1100: Add binding for ADIN1100 Ethernet PHY alexandru.tachici
2021-10-11 23:13 ` Rob Herring
2021-10-12 0:47 ` Rob Herring
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211012082908.GD938@pengutronix.de \
--to=o.rempel@pengutronix.de \
--cc=alexandru.ardelean@analog.com \
--cc=alexandru.tachici@analog.com \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=hkallweit1@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=robh+dt@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.