Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/* Driver for 100BASE-TX PHY embedded into NXP SJA1110 switch
  3 *
  4 * Copyright 2022-2023 NXP
  5 */
  6
  7#include <linux/kernel.h>
  8#include <linux/mii.h>
  9#include <linux/module.h>
 10#include <linux/phy.h>
 11
 12#define PHY_ID_CBTX_SJA1110			0x001bb020
 13
 14/* Registers */
 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
 20
 21/* Fields */
 22#define CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN	BIT(7)
 23#define CBTX_MODE_CTRL_STAT_MDIX_MODE		BIT(6)
 24
 25#define CBTX_PDOWN_CTL_TRUE_PDOWN		BIT(0)
 26
 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)
 34
 35static int cbtx_soft_reset(struct phy_device *phydev)
 36{
 37	int ret;
 38
 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);
 42	if (ret)
 43		return ret;
 44
 45	return genphy_soft_reset(phydev);
 46}
 47
 48static int cbtx_config_init(struct phy_device *phydev)
 49{
 50	/* Wait for cbtx_config_aneg() to kick in and apply this */
 51	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
 52
 53	return 0;
 54}
 55
 56static int cbtx_mdix_status(struct phy_device *phydev)
 57{
 58	int ret;
 59
 60	ret = phy_read(phydev, CBTX_MODE_CTRL_STAT);
 61	if (ret < 0)
 62		return ret;
 63
 64	if (ret & CBTX_MODE_CTRL_STAT_MDIX_MODE)
 65		phydev->mdix = ETH_TP_MDI_X;
 66	else
 67		phydev->mdix = ETH_TP_MDI;
 68
 69	return 0;
 70}
 71
 72static int cbtx_read_status(struct phy_device *phydev)
 73{
 74	int ret;
 75
 76	ret = cbtx_mdix_status(phydev);
 77	if (ret)
 78		return ret;
 79
 80	return genphy_read_status(phydev);
 81}
 82
 83static int cbtx_mdix_config(struct phy_device *phydev)
 84{
 85	int ret;
 86
 87	switch (phydev->mdix_ctrl) {
 88	case ETH_TP_MDI_AUTO:
 89		return phy_set_bits(phydev, CBTX_MODE_CTRL_STAT,
 90				    CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);
 91	case ETH_TP_MDI:
 92		ret = phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,
 93				     CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);
 94		if (ret)
 95			return ret;
 96
 97		return phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,
 98				      CBTX_MODE_CTRL_STAT_MDIX_MODE);
 99	case ETH_TP_MDI_X:
100		ret = phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,
101				     CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);
102		if (ret)
103			return ret;
104
105		return phy_set_bits(phydev, CBTX_MODE_CTRL_STAT,
106				    CBTX_MODE_CTRL_STAT_MDIX_MODE);
107	}
108
109	return 0;
110}
111
112static int cbtx_config_aneg(struct phy_device *phydev)
113{
114	int ret;
115
116	ret = cbtx_mdix_config(phydev);
117	if (ret)
118		return ret;
119
120	return genphy_config_aneg(phydev);
121}
122
123static int cbtx_ack_interrupts(struct phy_device *phydev)
124{
125	return phy_read(phydev, CBTX_IRQ_STAT);
126}
127
128static int cbtx_config_intr(struct phy_device *phydev)
129{
130	int ret;
131
132	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
133		ret = cbtx_ack_interrupts(phydev);
134		if (ret < 0)
135			return ret;
136
137		ret = phy_write(phydev, CBTX_IRQ_ENABLE, CBTX_IRQ_LINK_DOWN |
138				CBTX_IRQ_AN_COMPLETE | CBTX_IRQ_ENERGYON);
139		if (ret)
140			return ret;
141	} else {
142		ret = phy_write(phydev, CBTX_IRQ_ENABLE, 0);
143		if (ret)
144			return ret;
145
146		ret = cbtx_ack_interrupts(phydev);
147		if (ret < 0)
148			return ret;
149	}
150
151	return 0;
152}
153
154static irqreturn_t cbtx_handle_interrupt(struct phy_device *phydev)
155{
156	int irq_stat, irq_enabled;
157
158	irq_stat = cbtx_ack_interrupts(phydev);
159	if (irq_stat < 0) {
160		phy_error(phydev);
161		return IRQ_NONE;
162	}
163
164	irq_enabled = phy_read(phydev, CBTX_IRQ_ENABLE);
165	if (irq_enabled < 0) {
166		phy_error(phydev);
167		return IRQ_NONE;
168	}
169
170	if (!(irq_enabled & irq_stat))
171		return IRQ_NONE;
172
173	phy_trigger_machine(phydev);
174
175	return IRQ_HANDLED;
176}
177
178static int cbtx_get_sset_count(struct phy_device *phydev)
179{
180	return 1;
181}
182
183static void cbtx_get_strings(struct phy_device *phydev, u8 *data)
184{
185	strncpy(data, "100btx_rx_err", ETH_GSTRING_LEN);
186}
187
188static void cbtx_get_stats(struct phy_device *phydev,
189			   struct ethtool_stats *stats, u64 *data)
190{
191	int ret;
192
193	ret = phy_read(phydev, CBTX_RX_ERR_COUNTER);
194	data[0] = (ret < 0) ? U64_MAX : ret;
195}
196
197static struct phy_driver cbtx_driver[] = {
198	{
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,
213	},
214};
215
216module_phy_driver(cbtx_driver);
217
218static struct mdio_device_id __maybe_unused cbtx_tbl[] = {
219	{ PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110) },
220	{ },
221};
222
223MODULE_DEVICE_TABLE(mdio, cbtx_tbl);
224
225MODULE_AUTHOR("Vladimir Oltean <vladimir.oltean@nxp.com>");
226MODULE_DESCRIPTION("NXP CBTX PHY driver");
227MODULE_LICENSE("GPL");