1 // SPDX-License-Identifier: GPL-2.0
2 /* Driver for 100BASE-TX PHY embedded into NXP SJA1110 switch
4 * Copyright 2022-2023 NXP
7 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/phy.h>
12 #define PHY_ID_CBTX_SJA1110 0x001bb020
15 #define CBTX_MODE_CTRL_STAT 0x11
16 #define CBTX_PDOWN_CTRL 0x18
17 #define CBTX_RX_ERR_COUNTER 0x1a
18 #define CBTX_IRQ_STAT 0x1d
19 #define CBTX_IRQ_ENABLE 0x1e
22 #define CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN BIT(7)
23 #define CBTX_MODE_CTRL_STAT_MDIX_MODE BIT(6)
25 #define CBTX_PDOWN_CTL_TRUE_PDOWN BIT(0)
27 #define CBTX_IRQ_ENERGYON BIT(7)
28 #define CBTX_IRQ_AN_COMPLETE BIT(6)
29 #define CBTX_IRQ_REM_FAULT BIT(5)
30 #define CBTX_IRQ_LINK_DOWN BIT(4)
31 #define CBTX_IRQ_AN_LP_ACK BIT(3)
32 #define CBTX_IRQ_PARALLEL_DETECT_FAULT BIT(2)
33 #define CBTX_IRQ_AN_PAGE_RECV BIT(1)
35 static int cbtx_soft_reset(struct phy_device
*phydev
)
39 /* Can't soft reset unless we remove PHY from true power down mode */
40 ret
= phy_clear_bits(phydev
, CBTX_PDOWN_CTRL
,
41 CBTX_PDOWN_CTL_TRUE_PDOWN
);
45 return genphy_soft_reset(phydev
);
48 static int cbtx_config_init(struct phy_device
*phydev
)
50 /* Wait for cbtx_config_aneg() to kick in and apply this */
51 phydev
->mdix_ctrl
= ETH_TP_MDI_AUTO
;
56 static int cbtx_mdix_status(struct phy_device
*phydev
)
60 ret
= phy_read(phydev
, CBTX_MODE_CTRL_STAT
);
64 if (ret
& CBTX_MODE_CTRL_STAT_MDIX_MODE
)
65 phydev
->mdix
= ETH_TP_MDI_X
;
67 phydev
->mdix
= ETH_TP_MDI
;
72 static int cbtx_read_status(struct phy_device
*phydev
)
76 ret
= cbtx_mdix_status(phydev
);
80 return genphy_read_status(phydev
);
83 static int cbtx_mdix_config(struct phy_device
*phydev
)
87 switch (phydev
->mdix_ctrl
) {
89 return phy_set_bits(phydev
, CBTX_MODE_CTRL_STAT
,
90 CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN
);
92 ret
= phy_clear_bits(phydev
, CBTX_MODE_CTRL_STAT
,
93 CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN
);
97 return phy_clear_bits(phydev
, CBTX_MODE_CTRL_STAT
,
98 CBTX_MODE_CTRL_STAT_MDIX_MODE
);
100 ret
= phy_clear_bits(phydev
, CBTX_MODE_CTRL_STAT
,
101 CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN
);
105 return phy_set_bits(phydev
, CBTX_MODE_CTRL_STAT
,
106 CBTX_MODE_CTRL_STAT_MDIX_MODE
);
112 static int cbtx_config_aneg(struct phy_device
*phydev
)
116 ret
= cbtx_mdix_config(phydev
);
120 return genphy_config_aneg(phydev
);
123 static int cbtx_ack_interrupts(struct phy_device
*phydev
)
125 return phy_read(phydev
, CBTX_IRQ_STAT
);
128 static int cbtx_config_intr(struct phy_device
*phydev
)
132 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
) {
133 ret
= cbtx_ack_interrupts(phydev
);
137 ret
= phy_write(phydev
, CBTX_IRQ_ENABLE
, CBTX_IRQ_LINK_DOWN
|
138 CBTX_IRQ_AN_COMPLETE
| CBTX_IRQ_ENERGYON
);
142 ret
= phy_write(phydev
, CBTX_IRQ_ENABLE
, 0);
146 ret
= cbtx_ack_interrupts(phydev
);
154 static irqreturn_t
cbtx_handle_interrupt(struct phy_device
*phydev
)
156 int irq_stat
, irq_enabled
;
158 irq_stat
= cbtx_ack_interrupts(phydev
);
164 irq_enabled
= phy_read(phydev
, CBTX_IRQ_ENABLE
);
165 if (irq_enabled
< 0) {
170 if (!(irq_enabled
& irq_stat
))
173 phy_trigger_machine(phydev
);
178 static int cbtx_get_sset_count(struct phy_device
*phydev
)
183 static void cbtx_get_strings(struct phy_device
*phydev
, u8
*data
)
185 ethtool_puts(&data
, "100btx_rx_err");
188 static void cbtx_get_stats(struct phy_device
*phydev
,
189 struct ethtool_stats
*stats
, u64
*data
)
193 ret
= phy_read(phydev
, CBTX_RX_ERR_COUNTER
);
194 data
[0] = (ret
< 0) ? U64_MAX
: ret
;
197 static struct phy_driver cbtx_driver
[] = {
199 PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110
),
200 .name
= "NXP CBTX (SJA1110)",
201 /* PHY_BASIC_FEATURES */
202 .soft_reset
= cbtx_soft_reset
,
203 .config_init
= cbtx_config_init
,
204 .suspend
= genphy_suspend
,
205 .resume
= genphy_resume
,
206 .config_intr
= cbtx_config_intr
,
207 .handle_interrupt
= cbtx_handle_interrupt
,
208 .read_status
= cbtx_read_status
,
209 .config_aneg
= cbtx_config_aneg
,
210 .get_sset_count
= cbtx_get_sset_count
,
211 .get_strings
= cbtx_get_strings
,
212 .get_stats
= cbtx_get_stats
,
216 module_phy_driver(cbtx_driver
);
218 static struct mdio_device_id __maybe_unused cbtx_tbl
[] = {
219 { PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110
) },
223 MODULE_DEVICE_TABLE(mdio
, cbtx_tbl
);
225 MODULE_AUTHOR("Vladimir Oltean <vladimir.oltean@nxp.com>");
226 MODULE_DESCRIPTION("NXP CBTX PHY driver");
227 MODULE_LICENSE("GPL");