forked from torvalds/linux
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drivers/net/phy: add driver for the onsemi NCN26000 10BASE-T1S PHY
Add support for the onsemi NCN26000 10BASE-T1S industrial Ethernet PHY. The driver supports Point-to-Multipoint operation without auto-negotiation and with link control handling. The PLCA RS support will be included on a separate patch. Signed-off-by: Piergiorgio Beruto <piergiorgio.beruto@gmail.com>
- Loading branch information
1 parent
e3f89ea
commit 91aaf14
Showing
7 changed files
with
433 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* 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> | ||
|
||
/* MDIO Manageable Devices (MMDs). */ | ||
#define MDIO_MMD_OATC14 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 0x8000 /* PLCA enable */ | ||
#define MDIO_OATC14_PLCA_RST 0x4000 /* 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 0x8000 /* 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 | ||
|
||
#endif /* __MDIO_OPEN_ALLIANCE__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
// 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 TO_TMR_DEFAULT 32 | ||
|
||
struct ncn26000_priv { | ||
u16 enabled_irqs; | ||
}; | ||
|
||
// module parameter: if set, the link status is derived from the PLCA status | ||
// default: false | ||
static bool link_status_plca; | ||
module_param(link_status_plca, bool, 0644); | ||
|
||
// driver callbacks | ||
|
||
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_OATC14, 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_get_features(struct phy_device *phydev) | ||
{ | ||
linkmode_zero(phydev->supported); | ||
linkmode_set_bit(ETHTOOL_LINK_MODE_MII_BIT, phydev->supported); | ||
|
||
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT, | ||
phydev->supported); | ||
|
||
linkmode_copy(phydev->advertising, phydev->supported); | ||
return 0; | ||
} | ||
|
||
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. Thus, report the link | ||
// status by testing the appropriate BMSR bit according to the module's | ||
// parameter configuration. | ||
const int lstatus_flag = link_status_plca ? | ||
BMSR_LSTATUS : NCN26000_BMSR_LINK_STATUS_BIT; | ||
|
||
int ret; | ||
|
||
ret = phy_read(phydev, MII_BMSR); | ||
if (unlikely(ret < 0)) | ||
return ret; | ||
|
||
// update link status | ||
phydev->link = (ret & lstatus_flag) ? 1 : 0; | ||
|
||
// handle more IRQs here | ||
|
||
return 0; | ||
} | ||
|
||
static irqreturn_t ncn26000_handle_interrupt(struct phy_device *phydev) | ||
{ | ||
const struct ncn26000_priv *const priv = phydev->priv; | ||
int ret; | ||
|
||
// clear the latched bits in MII_BMSR | ||
phy_read(phydev, MII_BMSR); | ||
|
||
// read and aknowledge the IRQ status register | ||
ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS); | ||
|
||
if (unlikely(ret < 0) || (ret & priv->enabled_irqs) == 0) | ||
return IRQ_NONE; | ||
|
||
phy_trigger_machine(phydev); | ||
return IRQ_HANDLED; | ||
} | ||
|
||
static int ncn26000_config_intr(struct phy_device *phydev) | ||
{ | ||
int ret; | ||
struct ncn26000_priv *priv = phydev->priv; | ||
|
||
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 | ||
priv->enabled_irqs = NCN26000_IRQ_LINKST_BIT; | ||
} else { | ||
// disable all IRQs | ||
priv->enabled_irqs = 0; | ||
} | ||
|
||
ret = phy_write(phydev, NCN26000_REG_IRQ_CTL, priv->enabled_irqs); | ||
if (ret != 0) | ||
return ret; | ||
|
||
return 0; | ||
} | ||
|
||
static int ncn26000_probe(struct phy_device *phydev) | ||
{ | ||
struct device *dev = &phydev->mdio.dev; | ||
struct ncn26000_priv *priv; | ||
|
||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
if (!priv) | ||
return -ENOMEM; | ||
|
||
phydev->priv = priv; | ||
|
||
return 0; | ||
} | ||
|
||
static struct phy_driver ncn26000_driver[] = { | ||
{ | ||
PHY_ID_MATCH_MODEL(PHY_ID_NCN26000), | ||
.name = "NCN26000", | ||
.probe = ncn26000_probe, | ||
.get_features = ncn26000_get_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); | ||
|
||
MODULE_DEVICE_TABLE(mdio, ncn26000_tbl); | ||
MODULE_AUTHOR("Piergiorgio Beruto"); | ||
MODULE_DESCRIPTION("onsemi 10BASE-T1S PHY driver"); | ||
MODULE_LICENSE("Dual BSD/GPL"); |
Oops, something went wrong.