summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_rgmii.c
blob: 4e422ca50828f4b0f4d7695c4138a90f5fd9956b (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
// SPDX-License-Identifier: GPL-2.0+
/* Microchip lan969x Switch driver
 *
 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
 */

#include "lan969x.h"

/* Tx clock selectors */
#define LAN969X_RGMII_TX_CLK_SEL_125MHZ 1  /* 1000Mbps */
#define LAN969X_RGMII_TX_CLK_SEL_25MHZ  2  /* 100Mbps */
#define LAN969X_RGMII_TX_CLK_SEL_2M5MHZ 3  /* 10Mbps */

/* Port speed selectors */
#define LAN969X_RGMII_SPEED_SEL_10 0   /* Select 10Mbps speed */
#define LAN969X_RGMII_SPEED_SEL_100 1  /* Select 100Mbps speed */
#define LAN969X_RGMII_SPEED_SEL_1000 2 /* Select 1000Mbps speed */

/* Clock delay selectors */
#define LAN969X_RGMII_CLK_DELAY_SEL_1_0_NS 2  /* Phase shift 45deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_1_7_NS 3  /* Phase shift 77deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_2_0_NS 4  /* Phase shift 90deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS 5  /* Phase shift 112deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_3_0_NS 6  /* Phase shift 135deg */
#define LAN969X_RGMII_CLK_DELAY_SEL_3_3_NS 7  /* Phase shift 147deg */

#define LAN969X_RGMII_PORT_START_IDX 28 /* Index of the first RGMII port */
#define LAN969X_RGMII_IFG_TX 4          /* TX Inter Frame Gap value */
#define LAN969X_RGMII_IFG_RX1 5         /* RX1 Inter Frame Gap value */
#define LAN969X_RGMII_IFG_RX2 1         /* RX2 Inter Frame Gap value */

#define RGMII_PORT_IDX(port) ((port)->portno - LAN969X_RGMII_PORT_START_IDX)

/* Get the tx clock selector based on the port speed. */
static int lan969x_rgmii_get_clk_sel(int speed)
{
	return (speed == SPEED_10  ? LAN969X_RGMII_TX_CLK_SEL_2M5MHZ :
		speed == SPEED_100 ? LAN969X_RGMII_TX_CLK_SEL_25MHZ :
				     LAN969X_RGMII_TX_CLK_SEL_125MHZ);
}

/* Get the port speed selector based on the port speed. */
static int lan969x_rgmii_get_speed_sel(int speed)
{
	return (speed == SPEED_10  ? LAN969X_RGMII_SPEED_SEL_10 :
		speed == SPEED_100 ? LAN969X_RGMII_SPEED_SEL_100 :
				     LAN969X_RGMII_SPEED_SEL_1000);
}

/* Get the clock delay selector based on the clock delay in picoseconds. */
static int lan969x_rgmii_get_clk_delay_sel(struct sparx5_port *port,
					   u32 delay_ps, u32 *clk_delay_sel)
{
	switch (delay_ps) {
	case 0:
		/* Hardware default selector. */
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS;
		break;
	case 1000:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_1_0_NS;
		break;
	case 1700:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_1_7_NS;
		break;
	case 2000:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_0_NS;
		break;
	case 2500:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_2_5_NS;
		break;
	case 3000:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_3_0_NS;
		break;
	case 3300:
		*clk_delay_sel = LAN969X_RGMII_CLK_DELAY_SEL_3_3_NS;
		break;
	default:
		dev_err(port->sparx5->dev, "Invalid RGMII delay: %u", delay_ps);
		return -EINVAL;
	}

	return 0;
}

/* Configure the RGMII tx clock frequency. */
static void lan969x_rgmii_tx_clk_config(struct sparx5_port *port,
					struct sparx5_port_config *conf)
{
	u32 clk_sel = lan969x_rgmii_get_clk_sel(conf->speed);
	u32 idx = RGMII_PORT_IDX(port);

	/* Take the RGMII clock domain out of reset and set tx clock
	 * frequency.
	 */
	spx5_rmw(HSIO_WRAP_RGMII_CFG_TX_CLK_CFG_SET(clk_sel) |
		 HSIO_WRAP_RGMII_CFG_RGMII_TX_RST_SET(0) |
		 HSIO_WRAP_RGMII_CFG_RGMII_RX_RST_SET(0),
		 HSIO_WRAP_RGMII_CFG_TX_CLK_CFG |
		 HSIO_WRAP_RGMII_CFG_RGMII_TX_RST |
		 HSIO_WRAP_RGMII_CFG_RGMII_RX_RST,
		 port->sparx5, HSIO_WRAP_RGMII_CFG(idx));
}

/* Configure the RGMII port device. */
static void lan969x_rgmii_port_device_config(struct sparx5_port *port,
					     struct sparx5_port_config *conf)
{
	u32 dtag, dotag, etype, speed_sel, idx = RGMII_PORT_IDX(port);

	speed_sel = lan969x_rgmii_get_speed_sel(conf->speed);

	etype = (port->vlan_type == SPX5_VLAN_PORT_TYPE_S_CUSTOM ?
		 port->custom_etype :
		 port->vlan_type == SPX5_VLAN_PORT_TYPE_C ?
		 ETH_P_8021Q : ETH_P_8021AD);

	dtag = port->max_vlan_tags == SPX5_PORT_MAX_TAGS_TWO;
	dotag = port->max_vlan_tags != SPX5_PORT_MAX_TAGS_NONE;

	/* Enable the MAC. */
	spx5_wr(DEVRGMII_MAC_ENA_CFG_RX_ENA_SET(1) |
		DEVRGMII_MAC_ENA_CFG_TX_ENA_SET(1),
		port->sparx5, DEVRGMII_MAC_ENA_CFG(idx));

	/* Configure the Inter Frame Gap. */
	spx5_wr(DEVRGMII_MAC_IFG_CFG_TX_IFG_SET(LAN969X_RGMII_IFG_TX) |
		DEVRGMII_MAC_IFG_CFG_RX_IFG1_SET(LAN969X_RGMII_IFG_RX1) |
		DEVRGMII_MAC_IFG_CFG_RX_IFG2_SET(LAN969X_RGMII_IFG_RX2),
		port->sparx5, DEVRGMII_MAC_IFG_CFG(idx));

	/* Configure port data rate. */
	spx5_wr(DEVRGMII_DEV_RST_CTRL_SPEED_SEL_SET(speed_sel),
		port->sparx5, DEVRGMII_DEV_RST_CTRL(idx));

	/* Configure VLAN awareness. */
	spx5_wr(DEVRGMII_MAC_TAGS_CFG_TAG_ID_SET(etype) |
		DEVRGMII_MAC_TAGS_CFG_PB_ENA_SET(dtag) |
		DEVRGMII_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(dotag) |
		DEVRGMII_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA_SET(dotag),
		port->sparx5,
		DEVRGMII_MAC_TAGS_CFG(idx));
}

/* Configure the RGMII delay lines in the MAC.
 *
 * We use the rx-internal-delay-ps" and "tx-internal-delay-ps" properties to
 * configure the rx and tx delays for the MAC. If these properties are missing
 * or set to zero, the MAC will not apply any delay.
 *
 * The PHY side delays are determined by the PHY mode
 * (e.g. PHY_INTERFACE_MODE_RGMII_{ID, RXID, TXID}), and ignored by the MAC side
 * entirely.
 */
static int lan969x_rgmii_delay_config(struct sparx5_port *port,
				      struct sparx5_port_config *conf)
{
	u32 tx_clk_sel, rx_clk_sel, tx_delay_ps = 0, rx_delay_ps = 0;
	u32 idx = RGMII_PORT_IDX(port);
	int err;

	of_property_read_u32(port->of_node, "rx-internal-delay-ps",
			     &rx_delay_ps);

	of_property_read_u32(port->of_node, "tx-internal-delay-ps",
			     &tx_delay_ps);

	err = lan969x_rgmii_get_clk_delay_sel(port, rx_delay_ps, &rx_clk_sel);
	if (err)
		return err;

	err = lan969x_rgmii_get_clk_delay_sel(port, tx_delay_ps, &tx_clk_sel);
	if (err)
		return err;

	/* Configure rx delay. */
	spx5_rmw(HSIO_WRAP_DLL_CFG_DLL_RST_SET(0) |
		 HSIO_WRAP_DLL_CFG_DLL_ENA_SET(1) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_SET(!!rx_delay_ps) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_SET(rx_clk_sel),
		 HSIO_WRAP_DLL_CFG_DLL_RST |
		 HSIO_WRAP_DLL_CFG_DLL_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL,
		 port->sparx5, HSIO_WRAP_DLL_CFG(idx, 0));

	/* Configure tx delay. */
	spx5_rmw(HSIO_WRAP_DLL_CFG_DLL_RST_SET(0) |
		 HSIO_WRAP_DLL_CFG_DLL_ENA_SET(1) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA_SET(!!tx_delay_ps) |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL_SET(tx_clk_sel),
		 HSIO_WRAP_DLL_CFG_DLL_RST |
		 HSIO_WRAP_DLL_CFG_DLL_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_ENA |
		 HSIO_WRAP_DLL_CFG_DLL_CLK_SEL,
		 port->sparx5, HSIO_WRAP_DLL_CFG(idx, 1));

	return 0;
}

/* Configure GPIO's to be used as RGMII interface. */
static void lan969x_rgmii_gpio_config(struct sparx5_port *port)
{
	u32 idx = RGMII_PORT_IDX(port);

	/* Enable the RGMII on the GPIOs. */
	spx5_wr(HSIO_WRAP_XMII_CFG_GPIO_XMII_CFG_SET(1), port->sparx5,
		HSIO_WRAP_XMII_CFG(!idx));
}

int lan969x_port_config_rgmii(struct sparx5_port *port,
			      struct sparx5_port_config *conf)
{
	int err;

	err = lan969x_rgmii_delay_config(port, conf);
	if (err)
		return err;

	lan969x_rgmii_tx_clk_config(port, conf);
	lan969x_rgmii_gpio_config(port);
	lan969x_rgmii_port_device_config(port, conf);

	return 0;
}