1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * This file is based on code from OCTEON SDK by Cavium Networks. |
4 | * |
5 | * Copyright (c) 2003-2007 Cavium Networks |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/netdevice.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/phy.h> |
12 | #include <linux/ratelimit.h> |
13 | #include <net/dst.h> |
14 | |
15 | #include "octeon-ethernet.h" |
16 | #include "ethernet-defines.h" |
17 | #include "ethernet-util.h" |
18 | #include "ethernet-mdio.h" |
19 | |
20 | static DEFINE_SPINLOCK(global_register_lock); |
21 | |
22 | static void cvm_oct_set_hw_preamble(struct octeon_ethernet *priv, bool enable) |
23 | { |
24 | union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl; |
25 | union cvmx_ipd_sub_port_fcs ipd_sub_port_fcs; |
26 | union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg; |
27 | int interface = INTERFACE(ipd_port: priv->port); |
28 | int index = INDEX(ipd_port: priv->port); |
29 | |
30 | /* Set preamble checking. */ |
31 | gmxx_rxx_frm_ctl.u64 = cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, |
32 | interface)); |
33 | gmxx_rxx_frm_ctl.s.pre_chk = enable; |
34 | cvmx_write_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface), |
35 | val: gmxx_rxx_frm_ctl.u64); |
36 | |
37 | /* Set FCS stripping. */ |
38 | ipd_sub_port_fcs.u64 = cvmx_read_csr(CVMX_IPD_SUB_PORT_FCS); |
39 | if (enable) |
40 | ipd_sub_port_fcs.s.port_bit |= 1ull << priv->port; |
41 | else |
42 | ipd_sub_port_fcs.s.port_bit &= |
43 | 0xffffffffull ^ (1ull << priv->port); |
44 | cvmx_write_csr(CVMX_IPD_SUB_PORT_FCS, val: ipd_sub_port_fcs.u64); |
45 | |
46 | /* Clear any error bits. */ |
47 | gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(index, |
48 | interface)); |
49 | cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface), |
50 | val: gmxx_rxx_int_reg.u64); |
51 | } |
52 | |
53 | static void cvm_oct_check_preamble_errors(struct net_device *dev) |
54 | { |
55 | struct octeon_ethernet *priv = netdev_priv(dev); |
56 | union cvmx_helper_link_info link_info; |
57 | unsigned long flags; |
58 | |
59 | link_info.u64 = priv->link_info; |
60 | |
61 | /* |
62 | * Take the global register lock since we are going to |
63 | * touch registers that affect more than one port. |
64 | */ |
65 | spin_lock_irqsave(&global_register_lock, flags); |
66 | |
67 | if (link_info.s.speed == 10 && priv->last_speed == 10) { |
68 | /* |
69 | * Read the GMXX_RXX_INT_REG[PCTERR] bit and see if we are |
70 | * getting preamble errors. |
71 | */ |
72 | int interface = INTERFACE(ipd_port: priv->port); |
73 | int index = INDEX(ipd_port: priv->port); |
74 | union cvmx_gmxx_rxx_int_reg gmxx_rxx_int_reg; |
75 | |
76 | gmxx_rxx_int_reg.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_REG |
77 | (index, interface)); |
78 | if (gmxx_rxx_int_reg.s.pcterr) { |
79 | /* |
80 | * We are getting preamble errors at 10Mbps. Most |
81 | * likely the PHY is giving us packets with misaligned |
82 | * preambles. In order to get these packets we need to |
83 | * disable preamble checking and do it in software. |
84 | */ |
85 | cvm_oct_set_hw_preamble(priv, enable: false); |
86 | printk_ratelimited("%s: Using 10Mbps with software preamble removal\n" , |
87 | dev->name); |
88 | } |
89 | } else { |
90 | /* |
91 | * Since the 10Mbps preamble workaround is allowed we need to |
92 | * enable preamble checking, FCS stripping, and clear error |
93 | * bits on every speed change. If errors occur during 10Mbps |
94 | * operation the above code will change this stuff |
95 | */ |
96 | if (priv->last_speed != link_info.s.speed) |
97 | cvm_oct_set_hw_preamble(priv, enable: true); |
98 | priv->last_speed = link_info.s.speed; |
99 | } |
100 | spin_unlock_irqrestore(lock: &global_register_lock, flags); |
101 | } |
102 | |
103 | static void cvm_oct_rgmii_poll(struct net_device *dev) |
104 | { |
105 | struct octeon_ethernet *priv = netdev_priv(dev); |
106 | union cvmx_helper_link_info link_info; |
107 | bool status_change; |
108 | |
109 | link_info = cvmx_helper_link_get(ipd_port: priv->port); |
110 | if (priv->link_info != link_info.u64 && |
111 | cvmx_helper_link_set(ipd_port: priv->port, link_info)) |
112 | link_info.u64 = priv->link_info; |
113 | status_change = priv->link_info != link_info.u64; |
114 | priv->link_info = link_info.u64; |
115 | |
116 | cvm_oct_check_preamble_errors(dev); |
117 | |
118 | if (likely(!status_change)) |
119 | return; |
120 | |
121 | /* Tell core. */ |
122 | if (link_info.s.link_up) { |
123 | if (!netif_carrier_ok(dev)) |
124 | netif_carrier_on(dev); |
125 | } else if (netif_carrier_ok(dev)) { |
126 | netif_carrier_off(dev); |
127 | } |
128 | cvm_oct_note_carrier(priv, li: link_info); |
129 | } |
130 | |
131 | int cvm_oct_rgmii_open(struct net_device *dev) |
132 | { |
133 | struct octeon_ethernet *priv = netdev_priv(dev); |
134 | int ret; |
135 | |
136 | ret = cvm_oct_common_open(dev, link_poll: cvm_oct_rgmii_poll); |
137 | if (ret) |
138 | return ret; |
139 | |
140 | if (dev->phydev) { |
141 | /* |
142 | * In phydev mode, we need still periodic polling for the |
143 | * preamble error checking, and we also need to call this |
144 | * function on every link state change. |
145 | * |
146 | * Only true RGMII ports need to be polled. In GMII mode, port |
147 | * 0 is really a RGMII port. |
148 | */ |
149 | if ((priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII && |
150 | priv->port == 0) || |
151 | (priv->imode == CVMX_HELPER_INTERFACE_MODE_RGMII)) { |
152 | priv->poll = cvm_oct_check_preamble_errors; |
153 | cvm_oct_check_preamble_errors(dev); |
154 | } |
155 | } |
156 | |
157 | return 0; |
158 | } |
159 | |