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
20static DEFINE_SPINLOCK(global_register_lock);
21
22static 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
53static 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
103static 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
131int 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

source code of linux/drivers/staging/octeon/ethernet-rgmii.c