]>
Commit | Line | Data |
---|---|---|
9d0dca11 K |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Texas Instruments K3 AM65 Ethernet Switch SubSystem Driver | |
4 | * | |
5 | * Copyright (C) 2019, Texas Instruments, Incorporated | |
6 | * | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
336d4615 | 10 | #include <malloc.h> |
90526e9f | 11 | #include <asm/cache.h> |
9d0dca11 K |
12 | #include <asm/io.h> |
13 | #include <asm/processor.h> | |
14 | #include <clk.h> | |
15 | #include <dm.h> | |
336d4615 | 16 | #include <dm/device_compat.h> |
9d0dca11 K |
17 | #include <dm/lists.h> |
18 | #include <dma-uclass.h> | |
19 | #include <dm/of_access.h> | |
20 | #include <miiphy.h> | |
21 | #include <net.h> | |
22 | #include <phy.h> | |
23 | #include <power-domain.h> | |
24 | #include <linux/soc/ti/ti-udma.h> | |
25 | ||
26 | #include "cpsw_mdio.h" | |
27 | ||
28 | #define AM65_CPSW_CPSWNU_MAX_PORTS 2 | |
29 | ||
30 | #define AM65_CPSW_SS_BASE 0x0 | |
31 | #define AM65_CPSW_SGMII_BASE 0x100 | |
32 | #define AM65_CPSW_MDIO_BASE 0xf00 | |
33 | #define AM65_CPSW_XGMII_BASE 0x2100 | |
34 | #define AM65_CPSW_CPSW_NU_BASE 0x20000 | |
35 | #define AM65_CPSW_CPSW_NU_ALE_BASE 0x1e000 | |
36 | ||
37 | #define AM65_CPSW_CPSW_NU_PORTS_OFFSET 0x1000 | |
38 | #define AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET 0x330 | |
39 | ||
40 | #define AM65_CPSW_MDIO_BUS_FREQ_DEF 1000000 | |
41 | ||
42 | #define AM65_CPSW_CTL_REG 0x4 | |
43 | #define AM65_CPSW_STAT_PORT_EN_REG 0x14 | |
44 | #define AM65_CPSW_PTYPE_REG 0x18 | |
45 | ||
46 | #define AM65_CPSW_CTL_REG_P0_ENABLE BIT(2) | |
47 | #define AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE BIT(13) | |
48 | #define AM65_CPSW_CTL_REG_P0_RX_PAD BIT(14) | |
49 | ||
50 | #define AM65_CPSW_P0_FLOW_ID_REG 0x8 | |
51 | #define AM65_CPSW_PN_RX_MAXLEN_REG 0x24 | |
52 | #define AM65_CPSW_PN_REG_SA_L 0x308 | |
53 | #define AM65_CPSW_PN_REG_SA_H 0x30c | |
54 | ||
55 | #define AM65_CPSW_ALE_CTL_REG 0x8 | |
56 | #define AM65_CPSW_ALE_CTL_REG_ENABLE BIT(31) | |
57 | #define AM65_CPSW_ALE_CTL_REG_RESET_TBL BIT(30) | |
58 | #define AM65_CPSW_ALE_CTL_REG_BYPASS BIT(4) | |
59 | #define AM65_CPSW_ALE_PN_CTL_REG(x) (0x40 + (x) * 4) | |
60 | #define AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD 0x3 | |
61 | #define AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY BIT(11) | |
62 | ||
63 | #define AM65_CPSW_MACSL_CTL_REG 0x0 | |
64 | #define AM65_CPSW_MACSL_CTL_REG_IFCTL_A BIT(15) | |
39821d58 | 65 | #define AM65_CPSW_MACSL_CTL_EXT_EN BIT(18) |
9d0dca11 K |
66 | #define AM65_CPSW_MACSL_CTL_REG_GIG BIT(7) |
67 | #define AM65_CPSW_MACSL_CTL_REG_GMII_EN BIT(5) | |
68 | #define AM65_CPSW_MACSL_CTL_REG_LOOPBACK BIT(1) | |
69 | #define AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX BIT(0) | |
70 | #define AM65_CPSW_MACSL_RESET_REG 0x8 | |
71 | #define AM65_CPSW_MACSL_RESET_REG_RESET BIT(0) | |
72 | #define AM65_CPSW_MACSL_STATUS_REG 0x4 | |
73 | #define AM65_CPSW_MACSL_RESET_REG_PN_IDLE BIT(31) | |
74 | #define AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE BIT(30) | |
75 | #define AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE BIT(29) | |
76 | #define AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE BIT(28) | |
77 | #define AM65_CPSW_MACSL_RESET_REG_IDLE_MASK \ | |
78 | (AM65_CPSW_MACSL_RESET_REG_PN_IDLE | \ | |
79 | AM65_CPSW_MACSL_RESET_REG_PN_E_IDLE | \ | |
80 | AM65_CPSW_MACSL_RESET_REG_PN_P_IDLE | \ | |
81 | AM65_CPSW_MACSL_RESET_REG_PN_TX_IDLE) | |
82 | ||
83 | #define AM65_CPSW_CPPI_PKT_TYPE 0x7 | |
84 | ||
85 | struct am65_cpsw_port { | |
86 | fdt_addr_t port_base; | |
87 | fdt_addr_t macsl_base; | |
88 | bool disabled; | |
89 | u32 mac_control; | |
90 | }; | |
91 | ||
92 | struct am65_cpsw_common { | |
93 | struct udevice *dev; | |
94 | fdt_addr_t ss_base; | |
95 | fdt_addr_t cpsw_base; | |
96 | fdt_addr_t mdio_base; | |
97 | fdt_addr_t ale_base; | |
98 | fdt_addr_t gmii_sel; | |
99 | fdt_addr_t mac_efuse; | |
100 | ||
101 | struct clk fclk; | |
102 | struct power_domain pwrdmn; | |
103 | ||
104 | u32 port_num; | |
105 | struct am65_cpsw_port ports[AM65_CPSW_CPSWNU_MAX_PORTS]; | |
9d0dca11 K |
106 | |
107 | struct mii_dev *bus; | |
108 | u32 bus_freq; | |
109 | ||
110 | struct dma dma_tx; | |
111 | struct dma dma_rx; | |
112 | u32 rx_next; | |
113 | u32 rx_pend; | |
114 | bool started; | |
115 | }; | |
116 | ||
117 | struct am65_cpsw_priv { | |
118 | struct udevice *dev; | |
119 | struct am65_cpsw_common *cpsw_common; | |
120 | u32 port_id; | |
121 | ||
122 | struct phy_device *phydev; | |
123 | bool has_phy; | |
124 | ofnode phy_node; | |
125 | u32 phy_addr; | |
126 | }; | |
127 | ||
128 | #ifdef PKTSIZE_ALIGN | |
129 | #define UDMA_RX_BUF_SIZE PKTSIZE_ALIGN | |
130 | #else | |
131 | #define UDMA_RX_BUF_SIZE ALIGN(1522, ARCH_DMA_MINALIGN) | |
132 | #endif | |
133 | ||
134 | #ifdef PKTBUFSRX | |
135 | #define UDMA_RX_DESC_NUM PKTBUFSRX | |
136 | #else | |
137 | #define UDMA_RX_DESC_NUM 4 | |
138 | #endif | |
139 | ||
140 | #define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ | |
141 | ((mac)[2] << 16) | ((mac)[3] << 24)) | |
142 | #define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) | |
143 | ||
144 | static void am65_cpsw_set_sl_mac(struct am65_cpsw_port *slave, | |
145 | unsigned char *addr) | |
146 | { | |
147 | writel(mac_hi(addr), | |
148 | slave->port_base + AM65_CPSW_PN_REG_SA_H); | |
149 | writel(mac_lo(addr), | |
150 | slave->port_base + AM65_CPSW_PN_REG_SA_L); | |
151 | } | |
152 | ||
153 | int am65_cpsw_macsl_reset(struct am65_cpsw_port *slave) | |
154 | { | |
155 | u32 i = 100; | |
156 | ||
157 | /* Set the soft reset bit */ | |
158 | writel(AM65_CPSW_MACSL_RESET_REG_RESET, | |
159 | slave->macsl_base + AM65_CPSW_MACSL_RESET_REG); | |
160 | ||
161 | while ((readl(slave->macsl_base + AM65_CPSW_MACSL_RESET_REG) & | |
162 | AM65_CPSW_MACSL_RESET_REG_RESET) && i--) | |
163 | cpu_relax(); | |
164 | ||
165 | /* Timeout on the reset */ | |
166 | return i; | |
167 | } | |
168 | ||
169 | static int am65_cpsw_macsl_wait_for_idle(struct am65_cpsw_port *slave) | |
170 | { | |
171 | u32 i = 100; | |
172 | ||
173 | while ((readl(slave->macsl_base + AM65_CPSW_MACSL_STATUS_REG) & | |
174 | AM65_CPSW_MACSL_RESET_REG_IDLE_MASK) && i--) | |
175 | cpu_relax(); | |
176 | ||
177 | return i; | |
178 | } | |
179 | ||
180 | static int am65_cpsw_update_link(struct am65_cpsw_priv *priv) | |
181 | { | |
182 | struct am65_cpsw_common *common = priv->cpsw_common; | |
183 | struct am65_cpsw_port *port = &common->ports[priv->port_id]; | |
184 | struct phy_device *phy = priv->phydev; | |
185 | u32 mac_control = 0; | |
186 | ||
187 | if (phy->link) { /* link up */ | |
188 | mac_control = /*AM65_CPSW_MACSL_CTL_REG_LOOPBACK |*/ | |
189 | AM65_CPSW_MACSL_CTL_REG_GMII_EN; | |
190 | if (phy->speed == 1000) | |
191 | mac_control |= AM65_CPSW_MACSL_CTL_REG_GIG; | |
39821d58 MK |
192 | if (phy->speed == 10 && phy_interface_is_rgmii(phy)) |
193 | /* Can be used with in band mode only */ | |
194 | mac_control |= AM65_CPSW_MACSL_CTL_EXT_EN; | |
9d0dca11 K |
195 | if (phy->duplex == DUPLEX_FULL) |
196 | mac_control |= AM65_CPSW_MACSL_CTL_REG_FULL_DUPLEX; | |
197 | if (phy->speed == 100) | |
198 | mac_control |= AM65_CPSW_MACSL_CTL_REG_IFCTL_A; | |
199 | } | |
200 | ||
201 | if (mac_control == port->mac_control) | |
202 | goto out; | |
203 | ||
204 | if (mac_control) { | |
205 | printf("link up on port %d, speed %d, %s duplex\n", | |
206 | priv->port_id, phy->speed, | |
207 | (phy->duplex == DUPLEX_FULL) ? "full" : "half"); | |
208 | } else { | |
209 | printf("link down on port %d\n", priv->port_id); | |
210 | } | |
211 | ||
212 | writel(mac_control, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); | |
213 | port->mac_control = mac_control; | |
214 | ||
215 | out: | |
216 | return phy->link; | |
217 | } | |
218 | ||
219 | #define AM65_GMII_SEL_MODE_MII 0 | |
220 | #define AM65_GMII_SEL_MODE_RMII 1 | |
221 | #define AM65_GMII_SEL_MODE_RGMII 2 | |
222 | ||
223 | #define AM65_GMII_SEL_RGMII_IDMODE BIT(4) | |
224 | ||
225 | static void am65_cpsw_gmii_sel_k3(struct am65_cpsw_priv *priv, | |
226 | phy_interface_t phy_mode, int slave) | |
227 | { | |
228 | struct am65_cpsw_common *common = priv->cpsw_common; | |
229 | u32 reg; | |
230 | u32 mode = 0; | |
231 | bool rgmii_id = false; | |
232 | ||
233 | reg = readl(common->gmii_sel); | |
234 | ||
235 | dev_dbg(common->dev, "old gmii_sel: %08x\n", reg); | |
236 | ||
237 | switch (phy_mode) { | |
238 | case PHY_INTERFACE_MODE_RMII: | |
239 | mode = AM65_GMII_SEL_MODE_RMII; | |
240 | break; | |
241 | ||
242 | case PHY_INTERFACE_MODE_RGMII: | |
da6a728e | 243 | case PHY_INTERFACE_MODE_RGMII_RXID: |
9d0dca11 K |
244 | mode = AM65_GMII_SEL_MODE_RGMII; |
245 | break; | |
246 | ||
247 | case PHY_INTERFACE_MODE_RGMII_ID: | |
9d0dca11 K |
248 | case PHY_INTERFACE_MODE_RGMII_TXID: |
249 | mode = AM65_GMII_SEL_MODE_RGMII; | |
250 | rgmii_id = true; | |
251 | break; | |
252 | ||
253 | default: | |
254 | dev_warn(common->dev, | |
255 | "Unsupported PHY mode: %u. Defaulting to MII.\n", | |
256 | phy_mode); | |
257 | /* fallthrough */ | |
258 | case PHY_INTERFACE_MODE_MII: | |
259 | mode = AM65_GMII_SEL_MODE_MII; | |
260 | break; | |
261 | }; | |
262 | ||
263 | if (rgmii_id) | |
264 | mode |= AM65_GMII_SEL_RGMII_IDMODE; | |
265 | ||
266 | reg = mode; | |
267 | dev_dbg(common->dev, "gmii_sel PHY mode: %u, new gmii_sel: %08x\n", | |
268 | phy_mode, reg); | |
269 | writel(reg, common->gmii_sel); | |
270 | ||
271 | reg = readl(common->gmii_sel); | |
272 | if (reg != mode) | |
273 | dev_err(common->dev, | |
274 | "gmii_sel PHY mode NOT SET!: requested: %08x, gmii_sel: %08x\n", | |
275 | mode, reg); | |
276 | } | |
277 | ||
278 | static int am65_cpsw_start(struct udevice *dev) | |
279 | { | |
280 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
281 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
282 | struct am65_cpsw_common *common = priv->cpsw_common; | |
283 | struct am65_cpsw_port *port = &common->ports[priv->port_id]; | |
284 | struct am65_cpsw_port *port0 = &common->ports[0]; | |
461a290c | 285 | struct ti_udma_drv_chan_cfg_data *dma_rx_cfg_data; |
9d0dca11 K |
286 | int ret, i; |
287 | ||
288 | ret = power_domain_on(&common->pwrdmn); | |
289 | if (ret) { | |
290 | dev_err(dev, "power_domain_on() failed %d\n", ret); | |
291 | goto out; | |
292 | } | |
293 | ||
294 | ret = clk_enable(&common->fclk); | |
295 | if (ret) { | |
296 | dev_err(dev, "clk enabled failed %d\n", ret); | |
297 | goto err_off_pwrdm; | |
298 | } | |
299 | ||
300 | common->rx_next = 0; | |
301 | common->rx_pend = 0; | |
302 | ret = dma_get_by_name(common->dev, "tx0", &common->dma_tx); | |
303 | if (ret) { | |
304 | dev_err(dev, "TX dma get failed %d\n", ret); | |
305 | goto err_off_clk; | |
306 | } | |
307 | ret = dma_get_by_name(common->dev, "rx", &common->dma_rx); | |
308 | if (ret) { | |
309 | dev_err(dev, "RX dma get failed %d\n", ret); | |
310 | goto err_free_tx; | |
311 | } | |
312 | ||
313 | for (i = 0; i < UDMA_RX_DESC_NUM; i++) { | |
314 | ret = dma_prepare_rcv_buf(&common->dma_rx, | |
315 | net_rx_packets[i], | |
316 | UDMA_RX_BUF_SIZE); | |
317 | if (ret) { | |
318 | dev_err(dev, "RX dma add buf failed %d\n", ret); | |
319 | goto err_free_tx; | |
320 | } | |
321 | } | |
322 | ||
323 | ret = dma_enable(&common->dma_tx); | |
324 | if (ret) { | |
325 | dev_err(dev, "TX dma_enable failed %d\n", ret); | |
326 | goto err_free_rx; | |
327 | } | |
328 | ret = dma_enable(&common->dma_rx); | |
329 | if (ret) { | |
330 | dev_err(dev, "RX dma_enable failed %d\n", ret); | |
331 | goto err_dis_tx; | |
332 | } | |
333 | ||
334 | /* Control register */ | |
335 | writel(AM65_CPSW_CTL_REG_P0_ENABLE | | |
336 | AM65_CPSW_CTL_REG_P0_TX_CRC_REMOVE | | |
337 | AM65_CPSW_CTL_REG_P0_RX_PAD, | |
338 | common->cpsw_base + AM65_CPSW_CTL_REG); | |
339 | ||
340 | /* disable priority elevation */ | |
341 | writel(0, common->cpsw_base + AM65_CPSW_PTYPE_REG); | |
342 | ||
343 | /* enable statistics */ | |
344 | writel(BIT(0) | BIT(priv->port_id), | |
345 | common->cpsw_base + AM65_CPSW_STAT_PORT_EN_REG); | |
346 | ||
347 | /* Port 0 length register */ | |
348 | writel(PKTSIZE_ALIGN, port0->port_base + AM65_CPSW_PN_RX_MAXLEN_REG); | |
349 | ||
350 | /* set base flow_id */ | |
461a290c VR |
351 | dma_get_cfg(&common->dma_rx, 0, (void **)&dma_rx_cfg_data); |
352 | writel(dma_rx_cfg_data->flow_id_base, | |
9d0dca11 | 353 | port0->port_base + AM65_CPSW_P0_FLOW_ID_REG); |
461a290c VR |
354 | dev_info(dev, "K3 CPSW: rflow_id_base: %u\n", |
355 | dma_rx_cfg_data->flow_id_base); | |
9d0dca11 K |
356 | |
357 | /* Reset and enable the ALE */ | |
358 | writel(AM65_CPSW_ALE_CTL_REG_ENABLE | AM65_CPSW_ALE_CTL_REG_RESET_TBL | | |
359 | AM65_CPSW_ALE_CTL_REG_BYPASS, | |
360 | common->ale_base + AM65_CPSW_ALE_CTL_REG); | |
361 | ||
362 | /* port 0 put into forward mode */ | |
363 | writel(AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD, | |
364 | common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); | |
365 | ||
366 | /* PORT x configuration */ | |
367 | ||
368 | /* Port x Max length register */ | |
369 | writel(PKTSIZE_ALIGN, port->port_base + AM65_CPSW_PN_RX_MAXLEN_REG); | |
370 | ||
371 | /* Port x set mac */ | |
372 | am65_cpsw_set_sl_mac(port, pdata->enetaddr); | |
373 | ||
374 | /* Port x ALE: mac_only, Forwarding */ | |
375 | writel(AM65_CPSW_ALE_PN_CTL_REG_MAC_ONLY | | |
376 | AM65_CPSW_ALE_PN_CTL_REG_MODE_FORWARD, | |
377 | common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); | |
378 | ||
379 | port->mac_control = 0; | |
380 | if (!am65_cpsw_macsl_reset(port)) { | |
381 | dev_err(dev, "mac_sl reset failed\n"); | |
382 | ret = -EFAULT; | |
383 | goto err_dis_rx; | |
384 | } | |
385 | ||
386 | ret = phy_startup(priv->phydev); | |
387 | if (ret) { | |
388 | dev_err(dev, "phy_startup failed\n"); | |
389 | goto err_dis_rx; | |
390 | } | |
391 | ||
392 | ret = am65_cpsw_update_link(priv); | |
393 | if (!ret) { | |
394 | ret = -ENODEV; | |
395 | goto err_phy_shutdown; | |
396 | } | |
397 | ||
398 | common->started = true; | |
399 | ||
400 | return 0; | |
401 | ||
402 | err_phy_shutdown: | |
403 | phy_shutdown(priv->phydev); | |
404 | err_dis_rx: | |
405 | /* disable ports */ | |
406 | writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); | |
407 | writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); | |
408 | if (!am65_cpsw_macsl_wait_for_idle(port)) | |
409 | dev_err(dev, "mac_sl idle timeout\n"); | |
410 | writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); | |
411 | writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG); | |
412 | writel(0, common->cpsw_base + AM65_CPSW_CTL_REG); | |
413 | ||
414 | dma_disable(&common->dma_rx); | |
415 | err_dis_tx: | |
416 | dma_disable(&common->dma_tx); | |
417 | err_free_rx: | |
418 | dma_free(&common->dma_rx); | |
419 | err_free_tx: | |
420 | dma_free(&common->dma_tx); | |
421 | err_off_clk: | |
422 | clk_disable(&common->fclk); | |
423 | err_off_pwrdm: | |
424 | power_domain_off(&common->pwrdmn); | |
425 | out: | |
426 | dev_err(dev, "%s end error\n", __func__); | |
427 | ||
428 | return ret; | |
429 | } | |
430 | ||
431 | static int am65_cpsw_send(struct udevice *dev, void *packet, int length) | |
432 | { | |
433 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
434 | struct am65_cpsw_common *common = priv->cpsw_common; | |
435 | struct ti_udma_drv_packet_data packet_data; | |
436 | int ret; | |
437 | ||
438 | packet_data.pkt_type = AM65_CPSW_CPPI_PKT_TYPE; | |
439 | packet_data.dest_tag = priv->port_id; | |
440 | ret = dma_send(&common->dma_tx, packet, length, &packet_data); | |
441 | if (ret) { | |
442 | dev_err(dev, "TX dma_send failed %d\n", ret); | |
443 | return ret; | |
444 | } | |
445 | ||
446 | return 0; | |
447 | } | |
448 | ||
449 | static int am65_cpsw_recv(struct udevice *dev, int flags, uchar **packetp) | |
450 | { | |
451 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
452 | struct am65_cpsw_common *common = priv->cpsw_common; | |
453 | ||
454 | /* try to receive a new packet */ | |
455 | return dma_receive(&common->dma_rx, (void **)packetp, NULL); | |
456 | } | |
457 | ||
458 | static int am65_cpsw_free_pkt(struct udevice *dev, uchar *packet, int length) | |
459 | { | |
460 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
461 | struct am65_cpsw_common *common = priv->cpsw_common; | |
462 | int ret; | |
463 | ||
464 | if (length > 0) { | |
465 | u32 pkt = common->rx_next % UDMA_RX_DESC_NUM; | |
466 | ||
467 | ret = dma_prepare_rcv_buf(&common->dma_rx, | |
468 | net_rx_packets[pkt], | |
469 | UDMA_RX_BUF_SIZE); | |
470 | if (ret) | |
471 | dev_err(dev, "RX dma free_pkt failed %d\n", ret); | |
472 | common->rx_next++; | |
473 | } | |
474 | ||
475 | return 0; | |
476 | } | |
477 | ||
478 | static void am65_cpsw_stop(struct udevice *dev) | |
479 | { | |
480 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
481 | struct am65_cpsw_common *common = priv->cpsw_common; | |
482 | struct am65_cpsw_port *port = &common->ports[priv->port_id]; | |
483 | ||
484 | if (!common->started) | |
485 | return; | |
486 | ||
487 | phy_shutdown(priv->phydev); | |
488 | ||
489 | writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(priv->port_id)); | |
490 | writel(0, common->ale_base + AM65_CPSW_ALE_PN_CTL_REG(0)); | |
491 | if (!am65_cpsw_macsl_wait_for_idle(port)) | |
492 | dev_err(dev, "mac_sl idle timeout\n"); | |
493 | writel(0, port->macsl_base + AM65_CPSW_MACSL_CTL_REG); | |
494 | writel(0, common->ale_base + AM65_CPSW_ALE_CTL_REG); | |
495 | writel(0, common->cpsw_base + AM65_CPSW_CTL_REG); | |
496 | ||
497 | dma_disable(&common->dma_tx); | |
498 | dma_free(&common->dma_tx); | |
499 | ||
500 | dma_disable(&common->dma_rx); | |
501 | dma_free(&common->dma_rx); | |
502 | ||
503 | common->started = false; | |
504 | } | |
505 | ||
506 | static int am65_cpsw_read_rom_hwaddr(struct udevice *dev) | |
507 | { | |
508 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
509 | struct am65_cpsw_common *common = priv->cpsw_common; | |
510 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
511 | u32 mac_hi, mac_lo; | |
512 | ||
513 | if (common->mac_efuse == FDT_ADDR_T_NONE) | |
514 | return -1; | |
515 | ||
516 | mac_lo = readl(common->mac_efuse); | |
517 | mac_hi = readl(common->mac_efuse + 4); | |
518 | pdata->enetaddr[0] = (mac_hi >> 8) & 0xff; | |
519 | pdata->enetaddr[1] = mac_hi & 0xff; | |
520 | pdata->enetaddr[2] = (mac_lo >> 24) & 0xff; | |
521 | pdata->enetaddr[3] = (mac_lo >> 16) & 0xff; | |
522 | pdata->enetaddr[4] = (mac_lo >> 8) & 0xff; | |
523 | pdata->enetaddr[5] = mac_lo & 0xff; | |
524 | ||
525 | return 0; | |
526 | } | |
527 | ||
528 | static const struct eth_ops am65_cpsw_ops = { | |
529 | .start = am65_cpsw_start, | |
530 | .send = am65_cpsw_send, | |
531 | .recv = am65_cpsw_recv, | |
532 | .free_pkt = am65_cpsw_free_pkt, | |
533 | .stop = am65_cpsw_stop, | |
534 | .read_rom_hwaddr = am65_cpsw_read_rom_hwaddr, | |
535 | }; | |
536 | ||
537 | static int am65_cpsw_mdio_init(struct udevice *dev) | |
538 | { | |
539 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
540 | struct am65_cpsw_common *cpsw_common = priv->cpsw_common; | |
541 | ||
542 | if (!priv->has_phy || cpsw_common->bus) | |
543 | return 0; | |
544 | ||
545 | cpsw_common->bus = cpsw_mdio_init(dev->name, | |
546 | cpsw_common->mdio_base, | |
547 | cpsw_common->bus_freq, | |
548 | clk_get_rate(&cpsw_common->fclk)); | |
549 | if (!cpsw_common->bus) | |
550 | return -EFAULT; | |
551 | ||
552 | return 0; | |
553 | } | |
554 | ||
555 | static int am65_cpsw_phy_init(struct udevice *dev) | |
556 | { | |
557 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
558 | struct am65_cpsw_common *cpsw_common = priv->cpsw_common; | |
559 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
560 | struct phy_device *phydev; | |
561 | u32 supported = PHY_GBIT_FEATURES; | |
562 | int ret; | |
563 | ||
564 | phydev = phy_connect(cpsw_common->bus, | |
565 | priv->phy_addr, | |
566 | priv->dev, | |
567 | pdata->phy_interface); | |
568 | ||
569 | if (!phydev) { | |
570 | dev_err(dev, "phy_connect() failed\n"); | |
571 | return -ENODEV; | |
572 | } | |
573 | ||
574 | phydev->supported &= supported; | |
575 | if (pdata->max_speed) { | |
576 | ret = phy_set_supported(phydev, pdata->max_speed); | |
577 | if (ret) | |
578 | return ret; | |
579 | } | |
580 | phydev->advertising = phydev->supported; | |
581 | ||
582 | if (ofnode_valid(priv->phy_node)) | |
583 | phydev->node = priv->phy_node; | |
584 | ||
585 | priv->phydev = phydev; | |
586 | ret = phy_config(phydev); | |
587 | if (ret < 0) | |
588 | pr_err("phy_config() failed: %d", ret); | |
589 | ||
590 | return ret; | |
591 | } | |
592 | ||
593 | static int am65_cpsw_ofdata_parse_phy(struct udevice *dev, ofnode port_np) | |
594 | { | |
595 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
596 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
597 | struct ofnode_phandle_args out_args; | |
598 | const char *phy_mode; | |
599 | int ret = 0; | |
600 | ||
601 | phy_mode = ofnode_read_string(port_np, "phy-mode"); | |
602 | if (phy_mode) { | |
603 | pdata->phy_interface = | |
604 | phy_get_interface_by_name(phy_mode); | |
605 | if (pdata->phy_interface == -1) { | |
606 | dev_err(dev, "Invalid PHY mode '%s', port %u\n", | |
607 | phy_mode, priv->port_id); | |
608 | ret = -EINVAL; | |
609 | goto out; | |
610 | } | |
611 | } | |
612 | ||
613 | ofnode_read_u32(port_np, "max-speed", (u32 *)&pdata->max_speed); | |
614 | if (pdata->max_speed) | |
615 | dev_err(dev, "Port %u speed froced to %uMbit\n", | |
616 | priv->port_id, pdata->max_speed); | |
617 | ||
618 | priv->has_phy = true; | |
619 | ret = ofnode_parse_phandle_with_args(port_np, "phy-handle", | |
620 | NULL, 0, 0, &out_args); | |
621 | if (ret) { | |
622 | dev_err(dev, "can't parse phy-handle port %u (%d)\n", | |
623 | priv->port_id, ret); | |
624 | priv->has_phy = false; | |
625 | ret = 0; | |
626 | } | |
627 | ||
628 | priv->phy_node = out_args.node; | |
629 | if (priv->has_phy) { | |
630 | ret = ofnode_read_u32(priv->phy_node, "reg", &priv->phy_addr); | |
631 | if (ret) { | |
632 | dev_err(dev, "failed to get phy_addr port %u (%d)\n", | |
633 | priv->port_id, ret); | |
634 | goto out; | |
635 | } | |
636 | } | |
637 | ||
638 | out: | |
639 | return ret; | |
640 | } | |
641 | ||
642 | static int am65_cpsw_probe_cpsw(struct udevice *dev) | |
643 | { | |
644 | struct am65_cpsw_priv *priv = dev_get_priv(dev); | |
645 | struct eth_pdata *pdata = dev_get_platdata(dev); | |
646 | struct am65_cpsw_common *cpsw_common; | |
647 | ofnode ports_np, node; | |
648 | int ret, i; | |
649 | ||
650 | priv->dev = dev; | |
651 | ||
652 | cpsw_common = calloc(1, sizeof(*priv->cpsw_common)); | |
653 | if (!cpsw_common) | |
654 | return -ENOMEM; | |
655 | priv->cpsw_common = cpsw_common; | |
656 | ||
657 | cpsw_common->dev = dev; | |
658 | cpsw_common->ss_base = dev_read_addr(dev); | |
659 | if (cpsw_common->ss_base == FDT_ADDR_T_NONE) | |
660 | return -EINVAL; | |
661 | cpsw_common->mac_efuse = devfdt_get_addr_name(dev, "mac_efuse"); | |
662 | /* no err check - optional */ | |
663 | ||
664 | ret = power_domain_get_by_index(dev, &cpsw_common->pwrdmn, 0); | |
665 | if (ret) { | |
666 | dev_err(dev, "failed to get pwrdmn: %d\n", ret); | |
667 | return ret; | |
668 | } | |
669 | ||
670 | ret = clk_get_by_name(dev, "fck", &cpsw_common->fclk); | |
671 | if (ret) { | |
672 | power_domain_free(&cpsw_common->pwrdmn); | |
673 | dev_err(dev, "failed to get clock %d\n", ret); | |
674 | return ret; | |
675 | } | |
676 | ||
677 | cpsw_common->cpsw_base = cpsw_common->ss_base + AM65_CPSW_CPSW_NU_BASE; | |
678 | cpsw_common->ale_base = cpsw_common->cpsw_base + | |
679 | AM65_CPSW_CPSW_NU_ALE_BASE; | |
680 | cpsw_common->mdio_base = cpsw_common->ss_base + AM65_CPSW_MDIO_BASE; | |
681 | ||
9d0dca11 K |
682 | ports_np = dev_read_subnode(dev, "ports"); |
683 | if (!ofnode_valid(ports_np)) { | |
684 | ret = -ENOENT; | |
685 | goto out; | |
686 | } | |
687 | ||
688 | ofnode_for_each_subnode(node, ports_np) { | |
689 | const char *node_name; | |
690 | u32 port_id; | |
691 | bool disabled; | |
692 | ||
693 | node_name = ofnode_get_name(node); | |
694 | ||
695 | disabled = !ofnode_is_available(node); | |
696 | ||
697 | ret = ofnode_read_u32(node, "reg", &port_id); | |
698 | if (ret) { | |
699 | dev_err(dev, "%s: failed to get port_id (%d)\n", | |
700 | node_name, ret); | |
701 | goto out; | |
702 | } | |
703 | ||
704 | if (port_id >= AM65_CPSW_CPSWNU_MAX_PORTS) { | |
705 | dev_err(dev, "%s: invalid port_id (%d)\n", | |
706 | node_name, port_id); | |
707 | ret = -EINVAL; | |
708 | goto out; | |
709 | } | |
710 | cpsw_common->port_num++; | |
711 | ||
712 | if (!port_id) | |
713 | continue; | |
714 | ||
715 | priv->port_id = port_id; | |
716 | cpsw_common->ports[port_id].disabled = disabled; | |
717 | if (disabled) | |
718 | continue; | |
719 | ||
720 | ret = am65_cpsw_ofdata_parse_phy(dev, node); | |
721 | if (ret) | |
722 | goto out; | |
723 | } | |
724 | ||
725 | for (i = 0; i < AM65_CPSW_CPSWNU_MAX_PORTS; i++) { | |
726 | struct am65_cpsw_port *port = &cpsw_common->ports[i]; | |
727 | ||
728 | port->port_base = cpsw_common->cpsw_base + | |
729 | AM65_CPSW_CPSW_NU_PORTS_OFFSET + | |
730 | (i * AM65_CPSW_CPSW_NU_PORTS_OFFSET); | |
731 | port->macsl_base = port->port_base + | |
732 | AM65_CPSW_CPSW_NU_PORT_MACSL_OFFSET; | |
733 | } | |
734 | ||
735 | node = dev_read_subnode(dev, "cpsw-phy-sel"); | |
736 | if (!ofnode_valid(node)) { | |
737 | dev_err(dev, "can't find cpsw-phy-sel\n"); | |
738 | ret = -ENOENT; | |
739 | goto out; | |
740 | } | |
741 | ||
742 | cpsw_common->gmii_sel = ofnode_get_addr(node); | |
743 | if (cpsw_common->gmii_sel == FDT_ADDR_T_NONE) { | |
744 | dev_err(dev, "failed to get gmii_sel base\n"); | |
745 | goto out; | |
746 | } | |
747 | ||
748 | node = dev_read_subnode(dev, "mdio"); | |
749 | if (!ofnode_valid(node)) { | |
750 | dev_err(dev, "can't find mdio\n"); | |
751 | ret = -ENOENT; | |
752 | goto out; | |
753 | } | |
754 | ||
755 | cpsw_common->bus_freq = | |
756 | dev_read_u32_default(dev, "bus_freq", | |
757 | AM65_CPSW_MDIO_BUS_FREQ_DEF); | |
758 | ||
759 | am65_cpsw_gmii_sel_k3(priv, pdata->phy_interface, priv->port_id); | |
760 | ||
761 | ret = am65_cpsw_mdio_init(dev); | |
762 | if (ret) | |
763 | goto out; | |
764 | ||
765 | ret = am65_cpsw_phy_init(dev); | |
766 | if (ret) | |
767 | goto out; | |
768 | ||
461a290c | 769 | dev_info(dev, "K3 CPSW: nuss_ver: 0x%08X cpsw_ver: 0x%08X ale_ver: 0x%08X Ports:%u mdio_freq:%u\n", |
9d0dca11 K |
770 | readl(cpsw_common->ss_base), |
771 | readl(cpsw_common->cpsw_base), | |
772 | readl(cpsw_common->ale_base), | |
773 | cpsw_common->port_num, | |
9d0dca11 K |
774 | cpsw_common->bus_freq); |
775 | ||
776 | out: | |
777 | clk_free(&cpsw_common->fclk); | |
778 | power_domain_free(&cpsw_common->pwrdmn); | |
779 | return ret; | |
780 | } | |
781 | ||
782 | static const struct udevice_id am65_cpsw_nuss_ids[] = { | |
783 | { .compatible = "ti,am654-cpsw-nuss" }, | |
382c0c62 | 784 | { .compatible = "ti,j721e-cpsw-nuss" }, |
9d0dca11 K |
785 | { } |
786 | }; | |
787 | ||
788 | U_BOOT_DRIVER(am65_cpsw_nuss_slave) = { | |
789 | .name = "am65_cpsw_nuss_slave", | |
790 | .id = UCLASS_ETH, | |
791 | .of_match = am65_cpsw_nuss_ids, | |
792 | .probe = am65_cpsw_probe_cpsw, | |
793 | .ops = &am65_cpsw_ops, | |
794 | .priv_auto_alloc_size = sizeof(struct am65_cpsw_priv), | |
795 | .platdata_auto_alloc_size = sizeof(struct eth_pdata), | |
796 | .flags = DM_FLAG_ALLOC_PRIV_DMA, | |
797 | }; |