aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/phy/nxp-tja11xx.c
blob: a61471f4277739737f0a5a9a8798c7bf1a43febb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// SPDX-License-Identifier: GPL-2.0
/* NXP TJA1100 BroadRReach PHY driver
 *
 * Copyright (C) 2022 Michael Trimarchi <michael@amarulasolutions.com>
 * Copyright (C) 2022 Ariel D'Alessandro <ariel.dalessandro@collabora.com>
 * Copyright (C) 2018 Marek Vasut <marex@denx.de>
 */

#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <phy.h>

#define PHY_ID_MASK			0xfffffff0
#define PHY_ID_TJA1100			0x0180dc40
#define PHY_ID_TJA1101			0x0180dd00

#define MII_ECTRL			17
#define MII_ECTRL_LINK_CONTROL		BIT(15)
#define MII_ECTRL_POWER_MODE_MASK	GENMASK(14, 11)
#define MII_ECTRL_POWER_MODE_NO_CHANGE	(0x0 << 11)
#define MII_ECTRL_POWER_MODE_NORMAL	(0x3 << 11)
#define MII_ECTRL_POWER_MODE_STANDBY	(0xc << 11)
#define MII_ECTRL_CABLE_TEST		BIT(5)
#define MII_ECTRL_CONFIG_EN		BIT(2)
#define MII_ECTRL_WAKE_REQUEST		BIT(0)

#define MII_CFG1			18
#define MII_CFG1_MASTER_SLAVE		BIT(15)
#define MII_CFG1_AUTO_OP		BIT(14)
#define MII_CFG1_SLEEP_CONFIRM		BIT(6)
#define MII_CFG1_LED_MODE_MASK		GENMASK(5, 4)
#define MII_CFG1_LED_MODE_LINKUP	0
#define MII_CFG1_LED_ENABLE		BIT(3)

#define MII_CFG2			19
#define MII_CFG2_SLEEP_REQUEST_TO	GENMASK(1, 0)
#define MII_CFG2_SLEEP_REQUEST_TO_16MS	0x3

#define MII_INTSRC			21
#define MII_INTSRC_LINK_FAIL		BIT(10)
#define MII_INTSRC_LINK_UP		BIT(9)
#define MII_INTSRC_MASK			(MII_INTSRC_LINK_FAIL | \
					 MII_INTSRC_LINK_UP)
#define MII_INTSRC_UV_ERR		BIT(3)
#define MII_INTSRC_TEMP_ERR		BIT(1)

#define MII_INTEN			22
#define MII_INTEN_LINK_FAIL		BIT(10)
#define MII_INTEN_LINK_UP		BIT(9)
#define MII_INTEN_UV_ERR		BIT(3)
#define MII_INTEN_TEMP_ERR		BIT(1)

#define MII_COMMSTAT			23
#define MII_COMMSTAT_LINK_UP		BIT(15)
#define MII_COMMSTAT_SQI_STATE		GENMASK(7, 5)
#define MII_COMMSTAT_SQI_MAX		7

#define MII_GENSTAT			24
#define MII_GENSTAT_PLL_LOCKED		BIT(14)

#define MII_EXTSTAT			25
#define MII_EXTSTAT_SHORT_DETECT	BIT(8)
#define MII_EXTSTAT_OPEN_DETECT		BIT(7)
#define MII_EXTSTAT_POLARITY_DETECT	BIT(6)

#define MII_COMMCFG			27
#define MII_COMMCFG_AUTO_OP		BIT(15)

static inline int tja11xx_set_bits(struct phy_device *phydev, u32 regnum,
				   u16 val)
{
	return phy_set_bits_mmd(phydev, MDIO_DEVAD_NONE, regnum, val);
}

static inline int tja11xx_clear_bits(struct phy_device *phydev, u32 regnum,
				     u16 val)
{
	return phy_clear_bits_mmd(phydev, MDIO_DEVAD_NONE, regnum, val);
}

static inline int tja11xx_read(struct phy_device *phydev, int regnum)
{
	return phy_read(phydev, MDIO_DEVAD_NONE, regnum);
}

static inline int tja11xx_modify(struct phy_device *phydev, int regnum,
				 u16 mask, u16 set)
{
	return phy_modify(phydev, MDIO_DEVAD_NONE, regnum, mask, set);
}

static int tja11xx_check(struct phy_device *phydev, u8 reg, u16 mask, u16 set)
{
	int val;

	return read_poll_timeout(tja11xx_read, val, (val & mask) == set, 150,
				 30000, phydev, reg);
}

static int tja11xx_modify_check(struct phy_device *phydev, u8 reg,
			    u16 mask, u16 set)
{
	int ret;

	ret = tja11xx_modify(phydev, reg, mask, set);
	if (ret)
		return ret;

	return tja11xx_check(phydev, reg, mask, set);
}

static int tja11xx_enable_reg_write(struct phy_device *phydev)
{
	return tja11xx_set_bits(phydev, MII_ECTRL, MII_ECTRL_CONFIG_EN);
}

static int tja11xx_enable_link_control(struct phy_device *phydev)
{
	return tja11xx_set_bits(phydev, MII_ECTRL, MII_ECTRL_LINK_CONTROL);
}

static int tja11xx_wakeup(struct phy_device *phydev)
{
	int ret;

	ret = tja11xx_read(phydev, MII_ECTRL);
	if (ret < 0)
		return ret;

	switch (ret & MII_ECTRL_POWER_MODE_MASK) {
	case MII_ECTRL_POWER_MODE_NO_CHANGE:
		break;
	case MII_ECTRL_POWER_MODE_NORMAL:
		ret = tja11xx_set_bits(phydev, MII_ECTRL,
				       MII_ECTRL_WAKE_REQUEST);
		if (ret)
			return ret;

		ret = tja11xx_clear_bits(phydev, MII_ECTRL,
					 MII_ECTRL_WAKE_REQUEST);
		if (ret)
			return ret;
		break;
	case MII_ECTRL_POWER_MODE_STANDBY:
		ret = tja11xx_modify_check(phydev, MII_ECTRL,
					   MII_ECTRL_POWER_MODE_MASK,
					   MII_ECTRL_POWER_MODE_STANDBY);
		if (ret)
			return ret;

		ret = tja11xx_modify(phydev, MII_ECTRL,
				     MII_ECTRL_POWER_MODE_MASK,
				     MII_ECTRL_POWER_MODE_NORMAL);
		if (ret)
			return ret;

		ret = tja11xx_modify_check(phydev, MII_GENSTAT,
					   MII_GENSTAT_PLL_LOCKED,
					   MII_GENSTAT_PLL_LOCKED);
		if (ret)
			return ret;

		return tja11xx_enable_link_control(phydev);
	default:
		break;
	}

	return 0;
}

static int tja11xx_config_init(struct phy_device *phydev)
{
	int ret;

	ret = tja11xx_enable_reg_write(phydev);
	if (ret)
		return ret;

	phydev->autoneg = AUTONEG_DISABLE;
	phydev->speed = SPEED_100;
	phydev->duplex = DUPLEX_FULL;

	switch (phydev->phy_id & PHY_ID_MASK) {
	case PHY_ID_TJA1100:
		ret = tja11xx_modify(phydev, MII_CFG1,
				     MII_CFG1_AUTO_OP | MII_CFG1_LED_MODE_MASK |
				     MII_CFG1_LED_ENABLE,
				     MII_CFG1_AUTO_OP |
				     MII_CFG1_LED_MODE_LINKUP |
				     MII_CFG1_LED_ENABLE);
		if (ret)
			return ret;
		break;
	case PHY_ID_TJA1101:
		ret = tja11xx_set_bits(phydev, MII_COMMCFG,
				       MII_COMMCFG_AUTO_OP);
		if (ret)
			return ret;
		break;
	default:
		return -EINVAL;
	}

	ret = tja11xx_clear_bits(phydev, MII_CFG1, MII_CFG1_SLEEP_CONFIRM);
	if (ret)
		return ret;

	ret = tja11xx_modify(phydev, MII_CFG2, MII_CFG2_SLEEP_REQUEST_TO,
			     MII_CFG2_SLEEP_REQUEST_TO_16MS);
	if (ret)
		return ret;

	ret = tja11xx_wakeup(phydev);
	if (ret < 0)
		return ret;

	/* ACK interrupts by reading the status register */
	ret = tja11xx_read(phydev, MII_INTSRC);
	if (ret < 0)
		return ret;

	return 0;
}

static int tja11xx_startup(struct phy_device *phydev)
{
	int ret;

	ret = genphy_update_link(phydev);
	if (ret)
		return ret;

	ret = tja11xx_read(phydev, MII_CFG1);
	if (ret < 0)
		return ret;

	if (phydev->link) {
		ret = tja11xx_read(phydev, MII_COMMSTAT);
		if (ret < 0)
			return ret;

		if (!(ret & MII_COMMSTAT_LINK_UP))
			phydev->link = 0;
	}

	return 0;
}

U_BOOT_PHY_DRIVER(tja1100) = {
	.name = "NXP TJA1100",
	.uid = PHY_ID_TJA1100,
	.mask = PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES,
	.config	= &tja11xx_config_init,
	.startup = &tja11xx_startup,
	.shutdown = &genphy_shutdown,
};

U_BOOT_PHY_DRIVER(tja1101) = {
	.name = "NXP TJA1101",
	.uid = PHY_ID_TJA1101,
	.mask = PHY_ID_MASK,
	.features = PHY_BASIC_FEATURES,
	.config	= &tja11xx_config_init,
	.startup = &tja11xx_startup,
	.shutdown = &genphy_shutdown,
};