1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
3 * Driver for the onsemi 10BASE-T1S NCN26000 PHYs family.
5 * Copyright 2022 onsemi
7 #include <linux/kernel.h>
8 #include <linux/bitfield.h>
9 #include <linux/errno.h>
10 #include <linux/init.h>
11 #include <linux/module.h>
12 #include <linux/mii.h>
13 #include <linux/phy.h>
15 #include "mdio-open-alliance.h"
17 #define PHY_ID_NCN26000 0x180FF5A1
19 #define NCN26000_REG_IRQ_CTL 16
20 #define NCN26000_REG_IRQ_STATUS 17
22 // the NCN26000 maps link_ctrl to BMCR_ANENABLE
23 #define NCN26000_BCMR_LINK_CTRL_BIT BMCR_ANENABLE
25 // the NCN26000 maps link_status to BMSR_ANEGCOMPLETE
26 #define NCN26000_BMSR_LINK_STATUS_BIT BMSR_ANEGCOMPLETE
28 #define NCN26000_IRQ_LINKST_BIT BIT(0)
29 #define NCN26000_IRQ_PLCAST_BIT BIT(1)
30 #define NCN26000_IRQ_LJABBER_BIT BIT(2)
31 #define NCN26000_IRQ_RJABBER_BIT BIT(3)
32 #define NCN26000_IRQ_PLCAREC_BIT BIT(4)
33 #define NCN26000_IRQ_PHYSCOL_BIT BIT(5)
34 #define NCN26000_IRQ_RESET_BIT BIT(15)
36 #define TO_TMR_DEFAULT 32
38 static int ncn26000_config_init(struct phy_device
*phydev
)
40 /* HW bug workaround: the default value of the PLCA TO_TIMER should be
41 * 32, where the current version of NCN26000 reports 24. This will be
42 * fixed in future PHY versions. For the time being, we force the
43 * correct default here.
45 return phy_write_mmd(phydev
, MDIO_MMD_VEND2
, MDIO_OATC14_PLCA_TOTMR
,
49 static int ncn26000_config_aneg(struct phy_device
*phydev
)
51 /* Note: the NCN26000 supports only P2MP link mode. Therefore, AN is not
52 * supported. However, this function is invoked by phylib to enable the
53 * PHY, regardless of the AN support.
55 phydev
->mdix_ctrl
= ETH_TP_MDI_AUTO
;
56 phydev
->mdix
= ETH_TP_MDI
;
59 return phy_write(phydev
, MII_BMCR
, NCN26000_BCMR_LINK_CTRL_BIT
);
62 static int ncn26000_read_status(struct phy_device
*phydev
)
64 /* The NCN26000 reports NCN26000_LINK_STATUS_BIT if the link status of
65 * the PHY is up. It further reports the logical AND of the link status
66 * and the PLCA status in the BMSR_LSTATUS bit.
70 /* The link state is latched low so that momentary link
71 * drops can be detected. Do not double-read the status
72 * in polling mode to detect such short link drops except
73 * the link was already down.
75 if (!phy_polling_mode(phydev
) || !phydev
->link
) {
76 ret
= phy_read(phydev
, MII_BMSR
);
79 else if (ret
& NCN26000_BMSR_LINK_STATUS_BIT
)
83 ret
= phy_read(phydev
, MII_BMSR
);
89 if (ret
& NCN26000_BMSR_LINK_STATUS_BIT
) {
92 phydev
->duplex
= DUPLEX_HALF
;
93 phydev
->speed
= SPEED_10
;
96 phydev
->duplex
= DUPLEX_UNKNOWN
;
97 phydev
->speed
= SPEED_UNKNOWN
;
103 static irqreturn_t
ncn26000_handle_interrupt(struct phy_device
*phydev
)
107 // read and aknowledge the IRQ status register
108 ret
= phy_read(phydev
, NCN26000_REG_IRQ_STATUS
);
110 // check only link status changes
111 if (ret
< 0 || (ret
& NCN26000_REG_IRQ_STATUS
) == 0)
114 phy_trigger_machine(phydev
);
118 static int ncn26000_config_intr(struct phy_device
*phydev
)
123 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
) {
125 ret
= phy_read(phydev
, NCN26000_REG_IRQ_STATUS
);
129 // get link status notifications
130 irqe
= NCN26000_IRQ_LINKST_BIT
;
136 ret
= phy_write(phydev
, NCN26000_REG_IRQ_CTL
, irqe
);
143 static struct phy_driver ncn26000_driver
[] = {
145 PHY_ID_MATCH_MODEL(PHY_ID_NCN26000
),
147 .features
= PHY_BASIC_T1S_P2MP_FEATURES
,
148 .config_init
= ncn26000_config_init
,
149 .config_intr
= ncn26000_config_intr
,
150 .config_aneg
= ncn26000_config_aneg
,
151 .read_status
= ncn26000_read_status
,
152 .handle_interrupt
= ncn26000_handle_interrupt
,
153 .get_plca_cfg
= genphy_c45_plca_get_cfg
,
154 .set_plca_cfg
= genphy_c45_plca_set_cfg
,
155 .get_plca_status
= genphy_c45_plca_get_status
,
156 .soft_reset
= genphy_soft_reset
,
160 module_phy_driver(ncn26000_driver
);
162 static struct mdio_device_id __maybe_unused ncn26000_tbl
[] = {
163 { PHY_ID_MATCH_MODEL(PHY_ID_NCN26000
) },
167 MODULE_DEVICE_TABLE(mdio
, ncn26000_tbl
);
169 MODULE_AUTHOR("Piergiorgio Beruto");
170 MODULE_DESCRIPTION("onsemi 10BASE-T1S PHY driver");
171 MODULE_LICENSE("Dual BSD/GPL");