summaryrefslogtreecommitdiff
path: root/drivers/net/phy/adin.c
blob: a5bfd960d9c96af250df052be48ae8c800c33fe8 (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
269
270
// SPDX-License-Identifier: GPL-2.0+
/**
 *  Driver for Analog Devices Industrial Ethernet PHYs
 *
 * Copyright 2019 Analog Devices Inc.
 * Copyright 2022 Variscite Ltd.
 * Copyright 2022 Josua Mayer <josua@solid-run.com>
 */
#include <common.h>
#include <phy.h>
#include <linux/bitops.h>
#include <linux/bitfield.h>

#define PHY_ID_ADIN1300				0x0283bc30
#define ADIN1300_EXT_REG_PTR			0x10
#define ADIN1300_EXT_REG_DATA			0x11

#define ADIN1300_GE_CLK_CFG_REG			0xff1f
#define   ADIN1300_GE_CLK_CFG_MASK		GENMASK(5, 0)
#define   ADIN1300_GE_CLK_CFG_RCVR_125		BIT(5)
#define   ADIN1300_GE_CLK_CFG_FREE_125		BIT(4)
#define   ADIN1300_GE_CLK_CFG_REF_EN		BIT(3)
#define   ADIN1300_GE_CLK_CFG_HRT_RCVR		BIT(2)
#define   ADIN1300_GE_CLK_CFG_HRT_FREE		BIT(1)
#define   ADIN1300_GE_CLK_CFG_25		BIT(0)

#define ADIN1300_GE_RGMII_CFG			0xff23
#define ADIN1300_GE_RGMII_RX_MSK		GENMASK(8, 6)
#define ADIN1300_GE_RGMII_RX_SEL(x)		\
		FIELD_PREP(ADIN1300_GE_RGMII_RX_MSK, x)
#define ADIN1300_GE_RGMII_GTX_MSK		GENMASK(5, 3)
#define ADIN1300_GE_RGMII_GTX_SEL(x)		\
		FIELD_PREP(ADIN1300_GE_RGMII_GTX_MSK, x)
#define ADIN1300_GE_RGMII_RXID_EN		BIT(2)
#define ADIN1300_GE_RGMII_TXID_EN		BIT(1)
#define ADIN1300_GE_RGMII_EN			BIT(0)

/* RGMII internal delay settings for rx and tx for ADIN1300 */
#define ADIN1300_RGMII_1_60_NS			0x0001
#define ADIN1300_RGMII_1_80_NS			0x0002
#define	ADIN1300_RGMII_2_00_NS			0x0000
#define	ADIN1300_RGMII_2_20_NS			0x0006
#define	ADIN1300_RGMII_2_40_NS			0x0007

/**
 * struct adin_cfg_reg_map - map a config value to aregister value
 * @cfg		value in device configuration
 * @reg		value in the register
 */
struct adin_cfg_reg_map {
	int cfg;
	int reg;
};

static const struct adin_cfg_reg_map adin_rgmii_delays[] = {
	{ 1600, ADIN1300_RGMII_1_60_NS },
	{ 1800, ADIN1300_RGMII_1_80_NS },
	{ 2000, ADIN1300_RGMII_2_00_NS },
	{ 2200, ADIN1300_RGMII_2_20_NS },
	{ 2400, ADIN1300_RGMII_2_40_NS },
	{ },
};

static int adin_lookup_reg_value(const struct adin_cfg_reg_map *tbl, int cfg)
{
	size_t i;

	for (i = 0; tbl[i].cfg; i++) {
		if (tbl[i].cfg == cfg)
			return tbl[i].reg;
	}

	return -EINVAL;
}

static u32 adin_get_reg_value(struct phy_device *phydev,
			      const char *prop_name,
			      const struct adin_cfg_reg_map *tbl,
			      u32 dflt)
{
	u32 val;
	int rc;

	ofnode node = phy_get_ofnode(phydev);
	if (!ofnode_valid(node)) {
		printf("%s: failed to get node\n", __func__);
		return -EINVAL;
	}

	if (ofnode_read_u32(node, prop_name, &val)) {
		printf("%s: failed to find %s, using default %d\n",
		       __func__, prop_name, dflt);
		return dflt;
	}

	debug("%s: %s = '%d'\n", __func__, prop_name, val);

	rc = adin_lookup_reg_value(tbl, val);
	if (rc < 0) {
		printf("%s: Unsupported value %u for %s using default (%u)\n",
		      __func__, val, prop_name, dflt);
		return dflt;
	}

	return rc;
}

/**
 * adin_get_phy_mode_override - Get phy-mode override for adin PHY
 *
 * The function gets phy-mode string from property 'adi,phy-mode-override'
 * and return its index in phy_interface_strings table, or -1 in error case.
 */
phy_interface_t adin_get_phy_mode_override(struct phy_device *phydev)
{
	ofnode node = phy_get_ofnode(phydev);
	const char *phy_mode_override;
	const char *prop_phy_mode_override = "adi,phy-mode-override";
	int i;

	phy_mode_override = ofnode_read_string(node, prop_phy_mode_override);
	if (!phy_mode_override)
		return PHY_INTERFACE_MODE_NA;

	debug("%s: %s = '%s'\n",
	      __func__, prop_phy_mode_override, phy_mode_override);

	for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++)
		if (!strcmp(phy_mode_override, phy_interface_strings[i]))
			return (phy_interface_t) i;

	printf("%s: Invalid PHY interface '%s'\n", __func__, phy_mode_override);

	return PHY_INTERFACE_MODE_NA;
}

static u16 adin_ext_read(struct phy_device *phydev, const u32 regnum)
{
	u16 val;

	phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_PTR, regnum);
	val = phy_read(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_DATA);

	debug("%s: adin@0x%x 0x%x=0x%x\n", __func__, phydev->addr, regnum, val);

	return val;
}

static int adin_ext_write(struct phy_device *phydev, const u32 regnum, const u16 val)
{
	debug("%s: adin@0x%x 0x%x=0x%x\n", __func__, phydev->addr, regnum, val);

	phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_PTR, regnum);

	return phy_write(phydev, MDIO_DEVAD_NONE, ADIN1300_EXT_REG_DATA, val);
}

static int adin_config_clk_out(struct phy_device *phydev)
{
	ofnode node = phy_get_ofnode(phydev);
	const char *val = NULL;
	u8 sel = 0;

	val = ofnode_read_string(node, "adi,phy-output-clock");
	if (!val) {
		/* property not present, do not enable GP_CLK pin */
	} else if (strcmp(val, "25mhz-reference") == 0) {
		sel |= ADIN1300_GE_CLK_CFG_25;
	} else if (strcmp(val, "125mhz-free-running") == 0) {
		sel |= ADIN1300_GE_CLK_CFG_FREE_125;
	} else if (strcmp(val, "adaptive-free-running") == 0) {
		sel |= ADIN1300_GE_CLK_CFG_HRT_FREE;
	} else {
		pr_err("%s: invalid adi,phy-output-clock\n", __func__);
		return -EINVAL;
	}

	if (ofnode_read_bool(node, "adi,phy-output-reference-clock"))
		sel |= ADIN1300_GE_CLK_CFG_REF_EN;

	return adin_ext_write(phydev, ADIN1300_GE_CLK_CFG_REG,
			      ADIN1300_GE_CLK_CFG_MASK & sel);
}

static int adin_config_rgmii_mode(struct phy_device *phydev)
{
	u16 reg_val;
	u32 val;
	phy_interface_t phy_mode_override = adin_get_phy_mode_override(phydev);

	if (phy_mode_override != PHY_INTERFACE_MODE_NA) {
		phydev->interface = phy_mode_override;
	}

	reg_val = adin_ext_read(phydev, ADIN1300_GE_RGMII_CFG);

	if (!phy_interface_is_rgmii(phydev)) {
		/* Disable RGMII */
		reg_val &= ~ADIN1300_GE_RGMII_EN;
		return adin_ext_write(phydev, ADIN1300_GE_RGMII_CFG, reg_val);
	}

	/* Enable RGMII */
	reg_val |= ADIN1300_GE_RGMII_EN;

	/* Enable / Disable RGMII RX Delay */
	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
		reg_val |= ADIN1300_GE_RGMII_RXID_EN;

		val = adin_get_reg_value(phydev, "adi,rx-internal-delay-ps",
					 adin_rgmii_delays,
					 ADIN1300_RGMII_2_00_NS);
		reg_val &= ~ADIN1300_GE_RGMII_RX_MSK;
		reg_val |= ADIN1300_GE_RGMII_RX_SEL(val);
	} else {
		reg_val &= ~ADIN1300_GE_RGMII_RXID_EN;
	}

	/* Enable / Disable RGMII RX Delay */
	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
		reg_val |= ADIN1300_GE_RGMII_TXID_EN;

		val = adin_get_reg_value(phydev, "adi,tx-internal-delay-ps",
					 adin_rgmii_delays,
					 ADIN1300_RGMII_2_00_NS);
		reg_val &= ~ADIN1300_GE_RGMII_GTX_MSK;
		reg_val |= ADIN1300_GE_RGMII_GTX_SEL(val);
	} else {
		reg_val &= ~ADIN1300_GE_RGMII_TXID_EN;
	}

	return adin_ext_write(phydev, ADIN1300_GE_RGMII_CFG, reg_val);
}

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

	printf("ADIN1300 PHY detected at addr %d\n", phydev->addr);

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

	ret = adin_config_rgmii_mode(phydev);

	if (ret < 0)
		return ret;

	return genphy_config(phydev);
}

static struct phy_driver ADIN1300_driver =  {
	.name = "ADIN1300",
	.uid = PHY_ID_ADIN1300,
	.mask = 0xffffffff,
	.features = PHY_GBIT_FEATURES,
	.config = adin1300_config,
	.startup = genphy_startup,
	.shutdown = genphy_shutdown,
};

int phy_adin_init(void)
{
	phy_register(&ADIN1300_driver);

	return 0;
}