xref: /openbmc/linux/drivers/net/phy/ncn26000.c (revision b53e7e8d)
1*b53e7e8dSPiergiorgio Beruto // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2*b53e7e8dSPiergiorgio Beruto /*
3*b53e7e8dSPiergiorgio Beruto  *  Driver for the onsemi 10BASE-T1S NCN26000 PHYs family.
4*b53e7e8dSPiergiorgio Beruto  *
5*b53e7e8dSPiergiorgio Beruto  * Copyright 2022 onsemi
6*b53e7e8dSPiergiorgio Beruto  */
7*b53e7e8dSPiergiorgio Beruto #include <linux/kernel.h>
8*b53e7e8dSPiergiorgio Beruto #include <linux/bitfield.h>
9*b53e7e8dSPiergiorgio Beruto #include <linux/errno.h>
10*b53e7e8dSPiergiorgio Beruto #include <linux/init.h>
11*b53e7e8dSPiergiorgio Beruto #include <linux/module.h>
12*b53e7e8dSPiergiorgio Beruto #include <linux/mii.h>
13*b53e7e8dSPiergiorgio Beruto #include <linux/phy.h>
14*b53e7e8dSPiergiorgio Beruto 
15*b53e7e8dSPiergiorgio Beruto #include "mdio-open-alliance.h"
16*b53e7e8dSPiergiorgio Beruto 
17*b53e7e8dSPiergiorgio Beruto #define PHY_ID_NCN26000			0x180FF5A1
18*b53e7e8dSPiergiorgio Beruto 
19*b53e7e8dSPiergiorgio Beruto #define NCN26000_REG_IRQ_CTL            16
20*b53e7e8dSPiergiorgio Beruto #define NCN26000_REG_IRQ_STATUS         17
21*b53e7e8dSPiergiorgio Beruto 
22*b53e7e8dSPiergiorgio Beruto // the NCN26000 maps link_ctrl to BMCR_ANENABLE
23*b53e7e8dSPiergiorgio Beruto #define NCN26000_BCMR_LINK_CTRL_BIT	BMCR_ANENABLE
24*b53e7e8dSPiergiorgio Beruto 
25*b53e7e8dSPiergiorgio Beruto // the NCN26000 maps link_status to BMSR_ANEGCOMPLETE
26*b53e7e8dSPiergiorgio Beruto #define NCN26000_BMSR_LINK_STATUS_BIT	BMSR_ANEGCOMPLETE
27*b53e7e8dSPiergiorgio Beruto 
28*b53e7e8dSPiergiorgio Beruto #define NCN26000_IRQ_LINKST_BIT		BIT(0)
29*b53e7e8dSPiergiorgio Beruto #define NCN26000_IRQ_PLCAST_BIT		BIT(1)
30*b53e7e8dSPiergiorgio Beruto #define NCN26000_IRQ_LJABBER_BIT	BIT(2)
31*b53e7e8dSPiergiorgio Beruto #define NCN26000_IRQ_RJABBER_BIT	BIT(3)
32*b53e7e8dSPiergiorgio Beruto #define NCN26000_IRQ_PLCAREC_BIT	BIT(4)
33*b53e7e8dSPiergiorgio Beruto #define NCN26000_IRQ_PHYSCOL_BIT	BIT(5)
34*b53e7e8dSPiergiorgio Beruto #define NCN26000_IRQ_RESET_BIT		BIT(15)
35*b53e7e8dSPiergiorgio Beruto 
36*b53e7e8dSPiergiorgio Beruto #define TO_TMR_DEFAULT			32
37*b53e7e8dSPiergiorgio Beruto 
ncn26000_config_init(struct phy_device * phydev)38*b53e7e8dSPiergiorgio Beruto static int ncn26000_config_init(struct phy_device *phydev)
39*b53e7e8dSPiergiorgio Beruto {
40*b53e7e8dSPiergiorgio Beruto 	/* HW bug workaround: the default value of the PLCA TO_TIMER should be
41*b53e7e8dSPiergiorgio Beruto 	 * 32, where the current version of NCN26000 reports 24. This will be
42*b53e7e8dSPiergiorgio Beruto 	 * fixed in future PHY versions. For the time being, we force the
43*b53e7e8dSPiergiorgio Beruto 	 * correct default here.
44*b53e7e8dSPiergiorgio Beruto 	 */
45*b53e7e8dSPiergiorgio Beruto 	return phy_write_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR,
46*b53e7e8dSPiergiorgio Beruto 			     TO_TMR_DEFAULT);
47*b53e7e8dSPiergiorgio Beruto }
48*b53e7e8dSPiergiorgio Beruto 
ncn26000_config_aneg(struct phy_device * phydev)49*b53e7e8dSPiergiorgio Beruto static int ncn26000_config_aneg(struct phy_device *phydev)
50*b53e7e8dSPiergiorgio Beruto {
51*b53e7e8dSPiergiorgio Beruto 	/* Note: the NCN26000 supports only P2MP link mode. Therefore, AN is not
52*b53e7e8dSPiergiorgio Beruto 	 * supported. However, this function is invoked by phylib to enable the
53*b53e7e8dSPiergiorgio Beruto 	 * PHY, regardless of the AN support.
54*b53e7e8dSPiergiorgio Beruto 	 */
55*b53e7e8dSPiergiorgio Beruto 	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
56*b53e7e8dSPiergiorgio Beruto 	phydev->mdix = ETH_TP_MDI;
57*b53e7e8dSPiergiorgio Beruto 
58*b53e7e8dSPiergiorgio Beruto 	// bring up the link
59*b53e7e8dSPiergiorgio Beruto 	return phy_write(phydev, MII_BMCR, NCN26000_BCMR_LINK_CTRL_BIT);
60*b53e7e8dSPiergiorgio Beruto }
61*b53e7e8dSPiergiorgio Beruto 
ncn26000_read_status(struct phy_device * phydev)62*b53e7e8dSPiergiorgio Beruto static int ncn26000_read_status(struct phy_device *phydev)
63*b53e7e8dSPiergiorgio Beruto {
64*b53e7e8dSPiergiorgio Beruto 	/* The NCN26000 reports NCN26000_LINK_STATUS_BIT if the link status of
65*b53e7e8dSPiergiorgio Beruto 	 * the PHY is up. It further reports the logical AND of the link status
66*b53e7e8dSPiergiorgio Beruto 	 * and the PLCA status in the BMSR_LSTATUS bit.
67*b53e7e8dSPiergiorgio Beruto 	 */
68*b53e7e8dSPiergiorgio Beruto 	int ret;
69*b53e7e8dSPiergiorgio Beruto 
70*b53e7e8dSPiergiorgio Beruto 	/* The link state is latched low so that momentary link
71*b53e7e8dSPiergiorgio Beruto 	 * drops can be detected. Do not double-read the status
72*b53e7e8dSPiergiorgio Beruto 	 * in polling mode to detect such short link drops except
73*b53e7e8dSPiergiorgio Beruto 	 * the link was already down.
74*b53e7e8dSPiergiorgio Beruto 	 */
75*b53e7e8dSPiergiorgio Beruto 	if (!phy_polling_mode(phydev) || !phydev->link) {
76*b53e7e8dSPiergiorgio Beruto 		ret = phy_read(phydev, MII_BMSR);
77*b53e7e8dSPiergiorgio Beruto 		if (ret < 0)
78*b53e7e8dSPiergiorgio Beruto 			return ret;
79*b53e7e8dSPiergiorgio Beruto 		else if (ret & NCN26000_BMSR_LINK_STATUS_BIT)
80*b53e7e8dSPiergiorgio Beruto 			goto upd_link;
81*b53e7e8dSPiergiorgio Beruto 	}
82*b53e7e8dSPiergiorgio Beruto 
83*b53e7e8dSPiergiorgio Beruto 	ret = phy_read(phydev, MII_BMSR);
84*b53e7e8dSPiergiorgio Beruto 	if (ret < 0)
85*b53e7e8dSPiergiorgio Beruto 		return ret;
86*b53e7e8dSPiergiorgio Beruto 
87*b53e7e8dSPiergiorgio Beruto upd_link:
88*b53e7e8dSPiergiorgio Beruto 	// update link status
89*b53e7e8dSPiergiorgio Beruto 	if (ret & NCN26000_BMSR_LINK_STATUS_BIT) {
90*b53e7e8dSPiergiorgio Beruto 		phydev->link = 1;
91*b53e7e8dSPiergiorgio Beruto 		phydev->pause = 0;
92*b53e7e8dSPiergiorgio Beruto 		phydev->duplex = DUPLEX_HALF;
93*b53e7e8dSPiergiorgio Beruto 		phydev->speed = SPEED_10;
94*b53e7e8dSPiergiorgio Beruto 	} else {
95*b53e7e8dSPiergiorgio Beruto 		phydev->link = 0;
96*b53e7e8dSPiergiorgio Beruto 		phydev->duplex = DUPLEX_UNKNOWN;
97*b53e7e8dSPiergiorgio Beruto 		phydev->speed = SPEED_UNKNOWN;
98*b53e7e8dSPiergiorgio Beruto 	}
99*b53e7e8dSPiergiorgio Beruto 
100*b53e7e8dSPiergiorgio Beruto 	return 0;
101*b53e7e8dSPiergiorgio Beruto }
102*b53e7e8dSPiergiorgio Beruto 
ncn26000_handle_interrupt(struct phy_device * phydev)103*b53e7e8dSPiergiorgio Beruto static irqreturn_t ncn26000_handle_interrupt(struct phy_device *phydev)
104*b53e7e8dSPiergiorgio Beruto {
105*b53e7e8dSPiergiorgio Beruto 	int ret;
106*b53e7e8dSPiergiorgio Beruto 
107*b53e7e8dSPiergiorgio Beruto 	// read and aknowledge the IRQ status register
108*b53e7e8dSPiergiorgio Beruto 	ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
109*b53e7e8dSPiergiorgio Beruto 
110*b53e7e8dSPiergiorgio Beruto 	// check only link status changes
111*b53e7e8dSPiergiorgio Beruto 	if (ret < 0 || (ret & NCN26000_REG_IRQ_STATUS) == 0)
112*b53e7e8dSPiergiorgio Beruto 		return IRQ_NONE;
113*b53e7e8dSPiergiorgio Beruto 
114*b53e7e8dSPiergiorgio Beruto 	phy_trigger_machine(phydev);
115*b53e7e8dSPiergiorgio Beruto 	return IRQ_HANDLED;
116*b53e7e8dSPiergiorgio Beruto }
117*b53e7e8dSPiergiorgio Beruto 
ncn26000_config_intr(struct phy_device * phydev)118*b53e7e8dSPiergiorgio Beruto static int ncn26000_config_intr(struct phy_device *phydev)
119*b53e7e8dSPiergiorgio Beruto {
120*b53e7e8dSPiergiorgio Beruto 	int ret;
121*b53e7e8dSPiergiorgio Beruto 	u16 irqe;
122*b53e7e8dSPiergiorgio Beruto 
123*b53e7e8dSPiergiorgio Beruto 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
124*b53e7e8dSPiergiorgio Beruto 		// acknowledge IRQs
125*b53e7e8dSPiergiorgio Beruto 		ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
126*b53e7e8dSPiergiorgio Beruto 		if (ret < 0)
127*b53e7e8dSPiergiorgio Beruto 			return ret;
128*b53e7e8dSPiergiorgio Beruto 
129*b53e7e8dSPiergiorgio Beruto 		// get link status notifications
130*b53e7e8dSPiergiorgio Beruto 		irqe = NCN26000_IRQ_LINKST_BIT;
131*b53e7e8dSPiergiorgio Beruto 	} else {
132*b53e7e8dSPiergiorgio Beruto 		// disable all IRQs
133*b53e7e8dSPiergiorgio Beruto 		irqe = 0;
134*b53e7e8dSPiergiorgio Beruto 	}
135*b53e7e8dSPiergiorgio Beruto 
136*b53e7e8dSPiergiorgio Beruto 	ret = phy_write(phydev, NCN26000_REG_IRQ_CTL, irqe);
137*b53e7e8dSPiergiorgio Beruto 	if (ret != 0)
138*b53e7e8dSPiergiorgio Beruto 		return ret;
139*b53e7e8dSPiergiorgio Beruto 
140*b53e7e8dSPiergiorgio Beruto 	return 0;
141*b53e7e8dSPiergiorgio Beruto }
142*b53e7e8dSPiergiorgio Beruto 
143*b53e7e8dSPiergiorgio Beruto static struct phy_driver ncn26000_driver[] = {
144*b53e7e8dSPiergiorgio Beruto 	{
145*b53e7e8dSPiergiorgio Beruto 		PHY_ID_MATCH_MODEL(PHY_ID_NCN26000),
146*b53e7e8dSPiergiorgio Beruto 		.name			= "NCN26000",
147*b53e7e8dSPiergiorgio Beruto 		.features		= PHY_BASIC_T1S_P2MP_FEATURES,
148*b53e7e8dSPiergiorgio Beruto 		.config_init            = ncn26000_config_init,
149*b53e7e8dSPiergiorgio Beruto 		.config_intr            = ncn26000_config_intr,
150*b53e7e8dSPiergiorgio Beruto 		.config_aneg		= ncn26000_config_aneg,
151*b53e7e8dSPiergiorgio Beruto 		.read_status		= ncn26000_read_status,
152*b53e7e8dSPiergiorgio Beruto 		.handle_interrupt       = ncn26000_handle_interrupt,
153*b53e7e8dSPiergiorgio Beruto 		.get_plca_cfg		= genphy_c45_plca_get_cfg,
154*b53e7e8dSPiergiorgio Beruto 		.set_plca_cfg		= genphy_c45_plca_set_cfg,
155*b53e7e8dSPiergiorgio Beruto 		.get_plca_status	= genphy_c45_plca_get_status,
156*b53e7e8dSPiergiorgio Beruto 		.soft_reset             = genphy_soft_reset,
157*b53e7e8dSPiergiorgio Beruto 	},
158*b53e7e8dSPiergiorgio Beruto };
159*b53e7e8dSPiergiorgio Beruto 
160*b53e7e8dSPiergiorgio Beruto module_phy_driver(ncn26000_driver);
161*b53e7e8dSPiergiorgio Beruto 
162*b53e7e8dSPiergiorgio Beruto static struct mdio_device_id __maybe_unused ncn26000_tbl[] = {
163*b53e7e8dSPiergiorgio Beruto 	{ PHY_ID_MATCH_MODEL(PHY_ID_NCN26000) },
164*b53e7e8dSPiergiorgio Beruto 	{ }
165*b53e7e8dSPiergiorgio Beruto };
166*b53e7e8dSPiergiorgio Beruto 
167*b53e7e8dSPiergiorgio Beruto MODULE_DEVICE_TABLE(mdio, ncn26000_tbl);
168*b53e7e8dSPiergiorgio Beruto 
169*b53e7e8dSPiergiorgio Beruto MODULE_AUTHOR("Piergiorgio Beruto");
170*b53e7e8dSPiergiorgio Beruto MODULE_DESCRIPTION("onsemi 10BASE-T1S PHY driver");
171*b53e7e8dSPiergiorgio Beruto MODULE_LICENSE("Dual BSD/GPL");
172