1422d97b8SChris Packham // SPDX-License-Identifier: GPL-2.0
2422d97b8SChris Packham /*
3422d97b8SChris Packham  * This file is based on code from OCTEON SDK by Cavium Networks.
4422d97b8SChris Packham  *
5422d97b8SChris Packham  * Copyright (c) 2003-2007 Cavium Networks
6422d97b8SChris Packham  */
7422d97b8SChris Packham 
8422d97b8SChris Packham #include <linux/platform_device.h>
9422d97b8SChris Packham #include <linux/kernel.h>
10422d97b8SChris Packham #include <linux/module.h>
11422d97b8SChris Packham #include <linux/netdevice.h>
12422d97b8SChris Packham #include <linux/etherdevice.h>
13422d97b8SChris Packham #include <linux/phy.h>
14422d97b8SChris Packham #include <linux/slab.h>
15422d97b8SChris Packham #include <linux/interrupt.h>
16179f5dc3SAlexander Sverdlin #include <linux/of_mdio.h>
17422d97b8SChris Packham #include <linux/of_net.h>
18422d97b8SChris Packham #include <linux/if_ether.h>
19422d97b8SChris Packham #include <linux/if_vlan.h>
20422d97b8SChris Packham 
21422d97b8SChris Packham #include <net/dst.h>
22422d97b8SChris Packham 
23422d97b8SChris Packham #include "octeon-ethernet.h"
24422d97b8SChris Packham #include "ethernet-defines.h"
25422d97b8SChris Packham #include "ethernet-mem.h"
26422d97b8SChris Packham #include "ethernet-rx.h"
27422d97b8SChris Packham #include "ethernet-tx.h"
28422d97b8SChris Packham #include "ethernet-mdio.h"
29422d97b8SChris Packham #include "ethernet-util.h"
30422d97b8SChris Packham 
31422d97b8SChris Packham #define OCTEON_MAX_MTU 65392
32422d97b8SChris Packham 
33422d97b8SChris Packham static int num_packet_buffers = 1024;
34422d97b8SChris Packham module_param(num_packet_buffers, int, 0444);
35422d97b8SChris Packham MODULE_PARM_DESC(num_packet_buffers, "\n"
36422d97b8SChris Packham 	"\tNumber of packet buffers to allocate and store in the\n"
37422d97b8SChris Packham 	"\tFPA. By default, 1024 packet buffers are used.\n");
38422d97b8SChris Packham 
39422d97b8SChris Packham static int pow_receive_group = 15;
40422d97b8SChris Packham module_param(pow_receive_group, int, 0444);
41422d97b8SChris Packham MODULE_PARM_DESC(pow_receive_group, "\n"
42422d97b8SChris Packham 	"\tPOW group to receive packets from. All ethernet hardware\n"
43422d97b8SChris Packham 	"\twill be configured to send incoming packets to this POW\n"
44422d97b8SChris Packham 	"\tgroup. Also any other software can submit packets to this\n"
45422d97b8SChris Packham 	"\tgroup for the kernel to process.");
46422d97b8SChris Packham 
47422d97b8SChris Packham static int receive_group_order;
48422d97b8SChris Packham module_param(receive_group_order, int, 0444);
49422d97b8SChris Packham MODULE_PARM_DESC(receive_group_order, "\n"
50422d97b8SChris Packham 	"\tOrder (0..4) of receive groups to take into use. Ethernet hardware\n"
51422d97b8SChris Packham 	"\twill be configured to send incoming packets to multiple POW\n"
52422d97b8SChris Packham 	"\tgroups. pow_receive_group parameter is ignored when multiple\n"
53422d97b8SChris Packham 	"\tgroups are taken into use and groups are allocated starting\n"
54422d97b8SChris Packham 	"\tfrom 0. By default, a single group is used.\n");
55422d97b8SChris Packham 
56422d97b8SChris Packham int pow_send_group = -1;
57422d97b8SChris Packham module_param(pow_send_group, int, 0644);
58422d97b8SChris Packham MODULE_PARM_DESC(pow_send_group, "\n"
59422d97b8SChris Packham 	"\tPOW group to send packets to other software on. This\n"
60422d97b8SChris Packham 	"\tcontrols the creation of the virtual device pow0.\n"
61422d97b8SChris Packham 	"\talways_use_pow also depends on this value.");
62422d97b8SChris Packham 
63422d97b8SChris Packham int always_use_pow;
64422d97b8SChris Packham module_param(always_use_pow, int, 0444);
65422d97b8SChris Packham MODULE_PARM_DESC(always_use_pow, "\n"
66422d97b8SChris Packham 	"\tWhen set, always send to the pow group. This will cause\n"
67422d97b8SChris Packham 	"\tpackets sent to real ethernet devices to be sent to the\n"
68422d97b8SChris Packham 	"\tPOW group instead of the hardware. Unless some other\n"
69422d97b8SChris Packham 	"\tapplication changes the config, packets will still be\n"
70422d97b8SChris Packham 	"\treceived from the low level hardware. Use this option\n"
71422d97b8SChris Packham 	"\tto allow a CVMX app to intercept all packets from the\n"
72422d97b8SChris Packham 	"\tlinux kernel. You must specify pow_send_group along with\n"
73422d97b8SChris Packham 	"\tthis option.");
74422d97b8SChris Packham 
75422d97b8SChris Packham char pow_send_list[128] = "";
76422d97b8SChris Packham module_param_string(pow_send_list, pow_send_list, sizeof(pow_send_list), 0444);
77422d97b8SChris Packham MODULE_PARM_DESC(pow_send_list, "\n"
78422d97b8SChris Packham 	"\tComma separated list of ethernet devices that should use the\n"
79422d97b8SChris Packham 	"\tPOW for transmit instead of the actual ethernet hardware. This\n"
80422d97b8SChris Packham 	"\tis a per port version of always_use_pow. always_use_pow takes\n"
81422d97b8SChris Packham 	"\tprecedence over this list. For example, setting this to\n"
82422d97b8SChris Packham 	"\t\"eth2,spi3,spi7\" would cause these three devices to transmit\n"
83422d97b8SChris Packham 	"\tusing the pow_send_group.");
84422d97b8SChris Packham 
85422d97b8SChris Packham int rx_napi_weight = 32;
86422d97b8SChris Packham module_param(rx_napi_weight, int, 0444);
87422d97b8SChris Packham MODULE_PARM_DESC(rx_napi_weight, "The NAPI WEIGHT parameter.");
88422d97b8SChris Packham 
89422d97b8SChris Packham /* Mask indicating which receive groups are in use. */
90422d97b8SChris Packham int pow_receive_groups;
91422d97b8SChris Packham 
92422d97b8SChris Packham /*
93422d97b8SChris Packham  * cvm_oct_poll_queue_stopping - flag to indicate polling should stop.
94422d97b8SChris Packham  *
95422d97b8SChris Packham  * Set to one right before cvm_oct_poll_queue is destroyed.
96422d97b8SChris Packham  */
97422d97b8SChris Packham atomic_t cvm_oct_poll_queue_stopping = ATOMIC_INIT(0);
98422d97b8SChris Packham 
99422d97b8SChris Packham /*
100422d97b8SChris Packham  * Array of every ethernet device owned by this driver indexed by
101422d97b8SChris Packham  * the ipd input port number.
102422d97b8SChris Packham  */
103422d97b8SChris Packham struct net_device *cvm_oct_device[TOTAL_NUMBER_OF_PORTS];
104422d97b8SChris Packham 
105422d97b8SChris Packham u64 cvm_oct_tx_poll_interval;
106422d97b8SChris Packham 
107422d97b8SChris Packham static void cvm_oct_rx_refill_worker(struct work_struct *work);
108422d97b8SChris Packham static DECLARE_DELAYED_WORK(cvm_oct_rx_refill_work, cvm_oct_rx_refill_worker);
109422d97b8SChris Packham 
cvm_oct_rx_refill_worker(struct work_struct * work)110422d97b8SChris Packham static void cvm_oct_rx_refill_worker(struct work_struct *work)
111422d97b8SChris Packham {
112422d97b8SChris Packham 	/*
113422d97b8SChris Packham 	 * FPA 0 may have been drained, try to refill it if we need
114422d97b8SChris Packham 	 * more than num_packet_buffers / 2, otherwise normal receive
115422d97b8SChris Packham 	 * processing will refill it.  If it were drained, no packets
116422d97b8SChris Packham 	 * could be received so cvm_oct_napi_poll would never be
117422d97b8SChris Packham 	 * invoked to do the refill.
118422d97b8SChris Packham 	 */
119422d97b8SChris Packham 	cvm_oct_rx_refill_pool(num_packet_buffers / 2);
120422d97b8SChris Packham 
121422d97b8SChris Packham 	if (!atomic_read(&cvm_oct_poll_queue_stopping))
122422d97b8SChris Packham 		schedule_delayed_work(&cvm_oct_rx_refill_work, HZ);
123422d97b8SChris Packham }
124422d97b8SChris Packham 
cvm_oct_periodic_worker(struct work_struct * work)125422d97b8SChris Packham static void cvm_oct_periodic_worker(struct work_struct *work)
126422d97b8SChris Packham {
127422d97b8SChris Packham 	struct octeon_ethernet *priv = container_of(work,
128422d97b8SChris Packham 						    struct octeon_ethernet,
129422d97b8SChris Packham 						    port_periodic_work.work);
130422d97b8SChris Packham 
131422d97b8SChris Packham 	if (priv->poll)
132422d97b8SChris Packham 		priv->poll(cvm_oct_device[priv->port]);
133422d97b8SChris Packham 
134422d97b8SChris Packham 	cvm_oct_device[priv->port]->netdev_ops->ndo_get_stats
135422d97b8SChris Packham 						(cvm_oct_device[priv->port]);
136422d97b8SChris Packham 
137422d97b8SChris Packham 	if (!atomic_read(&cvm_oct_poll_queue_stopping))
138422d97b8SChris Packham 		schedule_delayed_work(&priv->port_periodic_work, HZ);
139422d97b8SChris Packham }
140422d97b8SChris Packham 
cvm_oct_configure_common_hw(void)141422d97b8SChris Packham static void cvm_oct_configure_common_hw(void)
142422d97b8SChris Packham {
143422d97b8SChris Packham 	/* Setup the FPA */
144422d97b8SChris Packham 	cvmx_fpa_enable();
145422d97b8SChris Packham 	cvm_oct_mem_fill_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
146422d97b8SChris Packham 			     num_packet_buffers);
147422d97b8SChris Packham 	cvm_oct_mem_fill_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
148422d97b8SChris Packham 			     num_packet_buffers);
149422d97b8SChris Packham 	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
150422d97b8SChris Packham 		cvm_oct_mem_fill_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
151422d97b8SChris Packham 				     CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 1024);
152422d97b8SChris Packham 
153422d97b8SChris Packham #ifdef __LITTLE_ENDIAN
154422d97b8SChris Packham 	{
155422d97b8SChris Packham 		union cvmx_ipd_ctl_status ipd_ctl_status;
156422d97b8SChris Packham 
157422d97b8SChris Packham 		ipd_ctl_status.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS);
158422d97b8SChris Packham 		ipd_ctl_status.s.pkt_lend = 1;
159422d97b8SChris Packham 		ipd_ctl_status.s.wqe_lend = 1;
160422d97b8SChris Packham 		cvmx_write_csr(CVMX_IPD_CTL_STATUS, ipd_ctl_status.u64);
161422d97b8SChris Packham 	}
162422d97b8SChris Packham #endif
163422d97b8SChris Packham 
164422d97b8SChris Packham 	cvmx_helper_setup_red(num_packet_buffers / 4, num_packet_buffers / 8);
165422d97b8SChris Packham }
166422d97b8SChris Packham 
167422d97b8SChris Packham /**
168422d97b8SChris Packham  * cvm_oct_free_work- Free a work queue entry
169422d97b8SChris Packham  *
170422d97b8SChris Packham  * @work_queue_entry: Work queue entry to free
171422d97b8SChris Packham  *
172422d97b8SChris Packham  * Returns Zero on success, Negative on failure.
173422d97b8SChris Packham  */
cvm_oct_free_work(void * work_queue_entry)174422d97b8SChris Packham int cvm_oct_free_work(void *work_queue_entry)
175422d97b8SChris Packham {
176422d97b8SChris Packham 	struct cvmx_wqe *work = work_queue_entry;
177422d97b8SChris Packham 
178422d97b8SChris Packham 	int segments = work->word2.s.bufs;
179422d97b8SChris Packham 	union cvmx_buf_ptr segment_ptr = work->packet_ptr;
180422d97b8SChris Packham 
181422d97b8SChris Packham 	while (segments--) {
182422d97b8SChris Packham 		union cvmx_buf_ptr next_ptr = *(union cvmx_buf_ptr *)
183422d97b8SChris Packham 			cvmx_phys_to_ptr(segment_ptr.s.addr - 8);
184422d97b8SChris Packham 		if (unlikely(!segment_ptr.s.i))
185422d97b8SChris Packham 			cvmx_fpa_free(cvm_oct_get_buffer_ptr(segment_ptr),
186422d97b8SChris Packham 				      segment_ptr.s.pool,
187422d97b8SChris Packham 				      CVMX_FPA_PACKET_POOL_SIZE / 128);
188422d97b8SChris Packham 		segment_ptr = next_ptr;
189422d97b8SChris Packham 	}
190422d97b8SChris Packham 	cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, 1);
191422d97b8SChris Packham 
192422d97b8SChris Packham 	return 0;
193422d97b8SChris Packham }
194422d97b8SChris Packham EXPORT_SYMBOL(cvm_oct_free_work);
195422d97b8SChris Packham 
196422d97b8SChris Packham /**
197422d97b8SChris Packham  * cvm_oct_common_get_stats - get the low level ethernet statistics
198422d97b8SChris Packham  * @dev:    Device to get the statistics from
199422d97b8SChris Packham  *
200422d97b8SChris Packham  * Returns Pointer to the statistics
201422d97b8SChris Packham  */
cvm_oct_common_get_stats(struct net_device * dev)202422d97b8SChris Packham static struct net_device_stats *cvm_oct_common_get_stats(struct net_device *dev)
203422d97b8SChris Packham {
204422d97b8SChris Packham 	cvmx_pip_port_status_t rx_status;
205422d97b8SChris Packham 	cvmx_pko_port_status_t tx_status;
206422d97b8SChris Packham 	struct octeon_ethernet *priv = netdev_priv(dev);
207422d97b8SChris Packham 
208422d97b8SChris Packham 	if (priv->port < CVMX_PIP_NUM_INPUT_PORTS) {
209422d97b8SChris Packham 		if (octeon_is_simulation()) {
210422d97b8SChris Packham 			/* The simulator doesn't support statistics */
211422d97b8SChris Packham 			memset(&rx_status, 0, sizeof(rx_status));
212422d97b8SChris Packham 			memset(&tx_status, 0, sizeof(tx_status));
213422d97b8SChris Packham 		} else {
214422d97b8SChris Packham 			cvmx_pip_get_port_status(priv->port, 1, &rx_status);
215422d97b8SChris Packham 			cvmx_pko_get_port_status(priv->port, 1, &tx_status);
216422d97b8SChris Packham 		}
217422d97b8SChris Packham 
218422d97b8SChris Packham 		dev->stats.rx_packets += rx_status.inb_packets;
219422d97b8SChris Packham 		dev->stats.tx_packets += tx_status.packets;
220422d97b8SChris Packham 		dev->stats.rx_bytes += rx_status.inb_octets;
221422d97b8SChris Packham 		dev->stats.tx_bytes += tx_status.octets;
222422d97b8SChris Packham 		dev->stats.multicast += rx_status.multicast_packets;
223422d97b8SChris Packham 		dev->stats.rx_crc_errors += rx_status.inb_errors;
224422d97b8SChris Packham 		dev->stats.rx_frame_errors += rx_status.fcs_align_err_packets;
225422d97b8SChris Packham 		dev->stats.rx_dropped += rx_status.dropped_packets;
226422d97b8SChris Packham 	}
227422d97b8SChris Packham 
228422d97b8SChris Packham 	return &dev->stats;
229422d97b8SChris Packham }
230422d97b8SChris Packham 
231422d97b8SChris Packham /**
232422d97b8SChris Packham  * cvm_oct_common_change_mtu - change the link MTU
233422d97b8SChris Packham  * @dev:     Device to change
234422d97b8SChris Packham  * @new_mtu: The new MTU
235422d97b8SChris Packham  *
236422d97b8SChris Packham  * Returns Zero on success
237422d97b8SChris Packham  */
cvm_oct_common_change_mtu(struct net_device * dev,int new_mtu)238422d97b8SChris Packham static int cvm_oct_common_change_mtu(struct net_device *dev, int new_mtu)
239422d97b8SChris Packham {
240422d97b8SChris Packham 	struct octeon_ethernet *priv = netdev_priv(dev);
241422d97b8SChris Packham 	int interface = INTERFACE(priv->port);
242422d97b8SChris Packham #if IS_ENABLED(CONFIG_VLAN_8021Q)
243422d97b8SChris Packham 	int vlan_bytes = VLAN_HLEN;
244422d97b8SChris Packham #else
245422d97b8SChris Packham 	int vlan_bytes = 0;
246422d97b8SChris Packham #endif
247422d97b8SChris Packham 	int mtu_overhead = ETH_HLEN + ETH_FCS_LEN + vlan_bytes;
248422d97b8SChris Packham 
249422d97b8SChris Packham 	dev->mtu = new_mtu;
250422d97b8SChris Packham 
251422d97b8SChris Packham 	if ((interface < 2) &&
252422d97b8SChris Packham 	    (cvmx_helper_interface_get_mode(interface) !=
253422d97b8SChris Packham 		CVMX_HELPER_INTERFACE_MODE_SPI)) {
254422d97b8SChris Packham 		int index = INDEX(priv->port);
255422d97b8SChris Packham 		/* Add ethernet header and FCS, and VLAN if configured. */
256422d97b8SChris Packham 		int max_packet = new_mtu + mtu_overhead;
257422d97b8SChris Packham 
258422d97b8SChris Packham 		if (OCTEON_IS_MODEL(OCTEON_CN3XXX) ||
259422d97b8SChris Packham 		    OCTEON_IS_MODEL(OCTEON_CN58XX)) {
260422d97b8SChris Packham 			/* Signal errors on packets larger than the MTU */
261422d97b8SChris Packham 			cvmx_write_csr(CVMX_GMXX_RXX_FRM_MAX(index, interface),
262422d97b8SChris Packham 				       max_packet);
263422d97b8SChris Packham 		} else {
264422d97b8SChris Packham 			/*
265422d97b8SChris Packham 			 * Set the hardware to truncate packets larger
266422d97b8SChris Packham 			 * than the MTU and smaller the 64 bytes.
267422d97b8SChris Packham 			 */
268422d97b8SChris Packham 			union cvmx_pip_frm_len_chkx frm_len_chk;
269422d97b8SChris Packham 
270422d97b8SChris Packham 			frm_len_chk.u64 = 0;
271422d97b8SChris Packham 			frm_len_chk.s.minlen = VLAN_ETH_ZLEN;
272422d97b8SChris Packham 			frm_len_chk.s.maxlen = max_packet;
273422d97b8SChris Packham 			cvmx_write_csr(CVMX_PIP_FRM_LEN_CHKX(interface),
274422d97b8SChris Packham 				       frm_len_chk.u64);
275422d97b8SChris Packham 		}
276422d97b8SChris Packham 		/*
277422d97b8SChris Packham 		 * Set the hardware to truncate packets larger than
278422d97b8SChris Packham 		 * the MTU. The jabber register must be set to a
279422d97b8SChris Packham 		 * multiple of 8 bytes, so round up.
280422d97b8SChris Packham 		 */
281422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_JABBER(index, interface),
282422d97b8SChris Packham 			       (max_packet + 7) & ~7u);
283422d97b8SChris Packham 	}
284422d97b8SChris Packham 	return 0;
285422d97b8SChris Packham }
286422d97b8SChris Packham 
287422d97b8SChris Packham /**
288422d97b8SChris Packham  * cvm_oct_common_set_multicast_list - set the multicast list
289422d97b8SChris Packham  * @dev:    Device to work on
290422d97b8SChris Packham  */
cvm_oct_common_set_multicast_list(struct net_device * dev)291422d97b8SChris Packham static void cvm_oct_common_set_multicast_list(struct net_device *dev)
292422d97b8SChris Packham {
293422d97b8SChris Packham 	union cvmx_gmxx_prtx_cfg gmx_cfg;
294422d97b8SChris Packham 	struct octeon_ethernet *priv = netdev_priv(dev);
295422d97b8SChris Packham 	int interface = INTERFACE(priv->port);
296422d97b8SChris Packham 
297422d97b8SChris Packham 	if ((interface < 2) &&
298422d97b8SChris Packham 	    (cvmx_helper_interface_get_mode(interface) !=
299422d97b8SChris Packham 		CVMX_HELPER_INTERFACE_MODE_SPI)) {
300422d97b8SChris Packham 		union cvmx_gmxx_rxx_adr_ctl control;
301422d97b8SChris Packham 		int index = INDEX(priv->port);
302422d97b8SChris Packham 
303422d97b8SChris Packham 		control.u64 = 0;
304422d97b8SChris Packham 		control.s.bcst = 1;	/* Allow broadcast MAC addresses */
305422d97b8SChris Packham 
306422d97b8SChris Packham 		if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI) ||
307422d97b8SChris Packham 		    (dev->flags & IFF_PROMISC))
308422d97b8SChris Packham 			/* Force accept multicast packets */
309422d97b8SChris Packham 			control.s.mcst = 2;
310422d97b8SChris Packham 		else
311422d97b8SChris Packham 			/* Force reject multicast packets */
312422d97b8SChris Packham 			control.s.mcst = 1;
313422d97b8SChris Packham 
314422d97b8SChris Packham 		if (dev->flags & IFF_PROMISC)
315422d97b8SChris Packham 			/*
316422d97b8SChris Packham 			 * Reject matches if promisc. Since CAM is
317422d97b8SChris Packham 			 * shut off, should accept everything.
318422d97b8SChris Packham 			 */
319422d97b8SChris Packham 			control.s.cam_mode = 0;
320422d97b8SChris Packham 		else
321422d97b8SChris Packham 			/* Filter packets based on the CAM */
322422d97b8SChris Packham 			control.s.cam_mode = 1;
323422d97b8SChris Packham 
324422d97b8SChris Packham 		gmx_cfg.u64 =
325422d97b8SChris Packham 		    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
326422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
327422d97b8SChris Packham 			       gmx_cfg.u64 & ~1ull);
328422d97b8SChris Packham 
329422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_ADR_CTL(index, interface),
330422d97b8SChris Packham 			       control.u64);
331422d97b8SChris Packham 		if (dev->flags & IFF_PROMISC)
332422d97b8SChris Packham 			cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
333422d97b8SChris Packham 				       (index, interface), 0);
334422d97b8SChris Packham 		else
335422d97b8SChris Packham 			cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM_EN
336422d97b8SChris Packham 				       (index, interface), 1);
337422d97b8SChris Packham 
338422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
339422d97b8SChris Packham 			       gmx_cfg.u64);
340422d97b8SChris Packham 	}
341422d97b8SChris Packham }
342422d97b8SChris Packham 
cvm_oct_set_mac_filter(struct net_device * dev)343422d97b8SChris Packham static int cvm_oct_set_mac_filter(struct net_device *dev)
344422d97b8SChris Packham {
345422d97b8SChris Packham 	struct octeon_ethernet *priv = netdev_priv(dev);
346422d97b8SChris Packham 	union cvmx_gmxx_prtx_cfg gmx_cfg;
347422d97b8SChris Packham 	int interface = INTERFACE(priv->port);
348422d97b8SChris Packham 
349422d97b8SChris Packham 	if ((interface < 2) &&
350422d97b8SChris Packham 	    (cvmx_helper_interface_get_mode(interface) !=
351422d97b8SChris Packham 		CVMX_HELPER_INTERFACE_MODE_SPI)) {
352422d97b8SChris Packham 		int i;
353524b09eaSJakub Kicinski 		const u8 *ptr = dev->dev_addr;
354422d97b8SChris Packham 		u64 mac = 0;
355422d97b8SChris Packham 		int index = INDEX(priv->port);
356422d97b8SChris Packham 
357422d97b8SChris Packham 		for (i = 0; i < 6; i++)
358422d97b8SChris Packham 			mac = (mac << 8) | (u64)ptr[i];
359422d97b8SChris Packham 
360422d97b8SChris Packham 		gmx_cfg.u64 =
361422d97b8SChris Packham 		    cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
362422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
363422d97b8SChris Packham 			       gmx_cfg.u64 & ~1ull);
364422d97b8SChris Packham 
365422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_SMACX(index, interface), mac);
366422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM0(index, interface),
367422d97b8SChris Packham 			       ptr[0]);
368422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM1(index, interface),
369422d97b8SChris Packham 			       ptr[1]);
370422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM2(index, interface),
371422d97b8SChris Packham 			       ptr[2]);
372422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM3(index, interface),
373422d97b8SChris Packham 			       ptr[3]);
374422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM4(index, interface),
375422d97b8SChris Packham 			       ptr[4]);
376422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_RXX_ADR_CAM5(index, interface),
377422d97b8SChris Packham 			       ptr[5]);
378422d97b8SChris Packham 		cvm_oct_common_set_multicast_list(dev);
379422d97b8SChris Packham 		cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface),
380422d97b8SChris Packham 			       gmx_cfg.u64);
381422d97b8SChris Packham 	}
382422d97b8SChris Packham 	return 0;
383422d97b8SChris Packham }
384422d97b8SChris Packham 
385422d97b8SChris Packham /**
386422d97b8SChris Packham  * cvm_oct_common_set_mac_address - set the hardware MAC address for a device
387422d97b8SChris Packham  * @dev:    The device in question.
388422d97b8SChris Packham  * @addr:   Socket address.
389422d97b8SChris Packham  *
390422d97b8SChris Packham  * Returns Zero on success
391422d97b8SChris Packham  */
cvm_oct_common_set_mac_address(struct net_device * dev,void * addr)392422d97b8SChris Packham static int cvm_oct_common_set_mac_address(struct net_device *dev, void *addr)
393422d97b8SChris Packham {
394422d97b8SChris Packham 	int r = eth_mac_addr(dev, addr);
395422d97b8SChris Packham 
396422d97b8SChris Packham 	if (r)
397422d97b8SChris Packham 		return r;
398422d97b8SChris Packham 	return cvm_oct_set_mac_filter(dev);
399422d97b8SChris Packham }
400422d97b8SChris Packham 
401422d97b8SChris Packham /**
402422d97b8SChris Packham  * cvm_oct_common_init - per network device initialization
403422d97b8SChris Packham  * @dev:    Device to initialize
404422d97b8SChris Packham  *
405422d97b8SChris Packham  * Returns Zero on success
406422d97b8SChris Packham  */
cvm_oct_common_init(struct net_device * dev)407422d97b8SChris Packham int cvm_oct_common_init(struct net_device *dev)
408422d97b8SChris Packham {
409422d97b8SChris Packham 	struct octeon_ethernet *priv = netdev_priv(dev);
41083216e39SMichael Walle 	int ret;
411422d97b8SChris Packham 
4128b6ce9b0SJakub Kicinski 	ret = of_get_ethdev_address(priv->of_node, dev);
41383216e39SMichael Walle 	if (ret)
414422d97b8SChris Packham 		eth_hw_addr_random(dev);
415422d97b8SChris Packham 
416422d97b8SChris Packham 	/*
417422d97b8SChris Packham 	 * Force the interface to use the POW send if always_use_pow
418422d97b8SChris Packham 	 * was specified or it is in the pow send list.
419422d97b8SChris Packham 	 */
420422d97b8SChris Packham 	if ((pow_send_group != -1) &&
421422d97b8SChris Packham 	    (always_use_pow || strstr(pow_send_list, dev->name)))
422422d97b8SChris Packham 		priv->queue = -1;
423422d97b8SChris Packham 
424422d97b8SChris Packham 	if (priv->queue != -1)
425422d97b8SChris Packham 		dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
426422d97b8SChris Packham 
427422d97b8SChris Packham 	/* We do our own locking, Linux doesn't need to */
428422d97b8SChris Packham 	dev->features |= NETIF_F_LLTX;
429422d97b8SChris Packham 	dev->ethtool_ops = &cvm_oct_ethtool_ops;
430422d97b8SChris Packham 
431422d97b8SChris Packham 	cvm_oct_set_mac_filter(dev);
432422d97b8SChris Packham 	dev_set_mtu(dev, dev->mtu);
433422d97b8SChris Packham 
434422d97b8SChris Packham 	/*
435422d97b8SChris Packham 	 * Zero out stats for port so we won't mistakenly show
436422d97b8SChris Packham 	 * counters from the bootloader.
437422d97b8SChris Packham 	 */
438422d97b8SChris Packham 	memset(dev->netdev_ops->ndo_get_stats(dev), 0,
439422d97b8SChris Packham 	       sizeof(struct net_device_stats));
440422d97b8SChris Packham 
441422d97b8SChris Packham 	if (dev->netdev_ops->ndo_stop)
442422d97b8SChris Packham 		dev->netdev_ops->ndo_stop(dev);
443422d97b8SChris Packham 
444422d97b8SChris Packham 	return 0;
445422d97b8SChris Packham }
446422d97b8SChris Packham 
cvm_oct_common_uninit(struct net_device * dev)447422d97b8SChris Packham void cvm_oct_common_uninit(struct net_device *dev)
448422d97b8SChris Packham {
449422d97b8SChris Packham 	if (dev->phydev)
450422d97b8SChris Packham 		phy_disconnect(dev->phydev);
451422d97b8SChris Packham }
452422d97b8SChris Packham 
cvm_oct_common_open(struct net_device * dev,void (* link_poll)(struct net_device *))453422d97b8SChris Packham int cvm_oct_common_open(struct net_device *dev,
454422d97b8SChris Packham 			void (*link_poll)(struct net_device *))
455422d97b8SChris Packham {
456422d97b8SChris Packham 	union cvmx_gmxx_prtx_cfg gmx_cfg;
457422d97b8SChris Packham 	struct octeon_ethernet *priv = netdev_priv(dev);
458422d97b8SChris Packham 	int interface = INTERFACE(priv->port);
459422d97b8SChris Packham 	int index = INDEX(priv->port);
460422d97b8SChris Packham 	union cvmx_helper_link_info link_info;
461422d97b8SChris Packham 	int rv;
462422d97b8SChris Packham 
463422d97b8SChris Packham 	rv = cvm_oct_phy_setup_device(dev);
464422d97b8SChris Packham 	if (rv)
465422d97b8SChris Packham 		return rv;
466422d97b8SChris Packham 
467422d97b8SChris Packham 	gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface));
468422d97b8SChris Packham 	gmx_cfg.s.en = 1;
469422d97b8SChris Packham 	if (octeon_has_feature(OCTEON_FEATURE_PKND))
470422d97b8SChris Packham 		gmx_cfg.s.pknd = priv->port;
471422d97b8SChris Packham 	cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64);
472422d97b8SChris Packham 
473422d97b8SChris Packham 	if (octeon_is_simulation())
474422d97b8SChris Packham 		return 0;
475422d97b8SChris Packham 
476422d97b8SChris Packham 	if (dev->phydev) {
477422d97b8SChris Packham 		int r = phy_read_status(dev->phydev);
478422d97b8SChris Packham 
479422d97b8SChris Packham 		if (r == 0 && dev->phydev->link == 0)
480422d97b8SChris Packham 			netif_carrier_off(dev);
481422d97b8SChris Packham 		cvm_oct_adjust_link(dev);
482422d97b8SChris Packham 	} else {
483422d97b8SChris Packham 		link_info = cvmx_helper_link_get(priv->port);
484422d97b8SChris Packham 		if (!link_info.s.link_up)
485422d97b8SChris Packham 			netif_carrier_off(dev);
486422d97b8SChris Packham 		priv->poll = link_poll;
487422d97b8SChris Packham 		link_poll(dev);
488422d97b8SChris Packham 	}
489422d97b8SChris Packham 
490422d97b8SChris Packham 	return 0;
491422d97b8SChris Packham }
492422d97b8SChris Packham 
cvm_oct_link_poll(struct net_device * dev)493422d97b8SChris Packham void cvm_oct_link_poll(struct net_device *dev)
494422d97b8SChris Packham {
495422d97b8SChris Packham 	struct octeon_ethernet *priv = netdev_priv(dev);
496422d97b8SChris Packham 	union cvmx_helper_link_info link_info;
497422d97b8SChris Packham 
498422d97b8SChris Packham 	link_info = cvmx_helper_link_get(priv->port);
499422d97b8SChris Packham 	if (link_info.u64 == priv->link_info)
500422d97b8SChris Packham 		return;
501422d97b8SChris Packham 
502422d97b8SChris Packham 	if (cvmx_helper_link_set(priv->port, link_info))
503422d97b8SChris Packham 		link_info.u64 = priv->link_info;
504422d97b8SChris Packham 	else
505422d97b8SChris Packham 		priv->link_info = link_info.u64;
506422d97b8SChris Packham 
507422d97b8SChris Packham 	if (link_info.s.link_up) {
508422d97b8SChris Packham 		if (!netif_carrier_ok(dev))
509422d97b8SChris Packham 			netif_carrier_on(dev);
510422d97b8SChris Packham 	} else if (netif_carrier_ok(dev)) {
511422d97b8SChris Packham 		netif_carrier_off(dev);
512422d97b8SChris Packham 	}
513422d97b8SChris Packham 	cvm_oct_note_carrier(priv, link_info);
514422d97b8SChris Packham }
515422d97b8SChris Packham 
cvm_oct_xaui_open(struct net_device * dev)516422d97b8SChris Packham static int cvm_oct_xaui_open(struct net_device *dev)
517422d97b8SChris Packham {
518422d97b8SChris Packham 	return cvm_oct_common_open(dev, cvm_oct_link_poll);
519422d97b8SChris Packham }
520422d97b8SChris Packham 
521422d97b8SChris Packham static const struct net_device_ops cvm_oct_npi_netdev_ops = {
522422d97b8SChris Packham 	.ndo_init		= cvm_oct_common_init,
523422d97b8SChris Packham 	.ndo_uninit		= cvm_oct_common_uninit,
524422d97b8SChris Packham 	.ndo_start_xmit		= cvm_oct_xmit,
525422d97b8SChris Packham 	.ndo_set_rx_mode	= cvm_oct_common_set_multicast_list,
526422d97b8SChris Packham 	.ndo_set_mac_address	= cvm_oct_common_set_mac_address,
527a7605370SArnd Bergmann 	.ndo_eth_ioctl		= cvm_oct_ioctl,
528422d97b8SChris Packham 	.ndo_change_mtu		= cvm_oct_common_change_mtu,
529422d97b8SChris Packham 	.ndo_get_stats		= cvm_oct_common_get_stats,
530422d97b8SChris Packham #ifdef CONFIG_NET_POLL_CONTROLLER
531422d97b8SChris Packham 	.ndo_poll_controller	= cvm_oct_poll_controller,
532422d97b8SChris Packham #endif
533422d97b8SChris Packham };
534422d97b8SChris Packham 
535422d97b8SChris Packham static const struct net_device_ops cvm_oct_xaui_netdev_ops = {
536422d97b8SChris Packham 	.ndo_init		= cvm_oct_common_init,
537422d97b8SChris Packham 	.ndo_uninit		= cvm_oct_common_uninit,
538422d97b8SChris Packham 	.ndo_open		= cvm_oct_xaui_open,
539422d97b8SChris Packham 	.ndo_stop		= cvm_oct_common_stop,
540422d97b8SChris Packham 	.ndo_start_xmit		= cvm_oct_xmit,
541422d97b8SChris Packham 	.ndo_set_rx_mode	= cvm_oct_common_set_multicast_list,
542422d97b8SChris Packham 	.ndo_set_mac_address	= cvm_oct_common_set_mac_address,
543a7605370SArnd Bergmann 	.ndo_eth_ioctl		= cvm_oct_ioctl,
544422d97b8SChris Packham 	.ndo_change_mtu		= cvm_oct_common_change_mtu,
545422d97b8SChris Packham 	.ndo_get_stats		= cvm_oct_common_get_stats,
546422d97b8SChris Packham #ifdef CONFIG_NET_POLL_CONTROLLER
547422d97b8SChris Packham 	.ndo_poll_controller	= cvm_oct_poll_controller,
548422d97b8SChris Packham #endif
549422d97b8SChris Packham };
550422d97b8SChris Packham 
551422d97b8SChris Packham static const struct net_device_ops cvm_oct_sgmii_netdev_ops = {
552422d97b8SChris Packham 	.ndo_init		= cvm_oct_sgmii_init,
553422d97b8SChris Packham 	.ndo_uninit		= cvm_oct_common_uninit,
554422d97b8SChris Packham 	.ndo_open		= cvm_oct_sgmii_open,
555422d97b8SChris Packham 	.ndo_stop		= cvm_oct_common_stop,
556422d97b8SChris Packham 	.ndo_start_xmit		= cvm_oct_xmit,
557422d97b8SChris Packham 	.ndo_set_rx_mode	= cvm_oct_common_set_multicast_list,
558422d97b8SChris Packham 	.ndo_set_mac_address	= cvm_oct_common_set_mac_address,
559a7605370SArnd Bergmann 	.ndo_eth_ioctl		= cvm_oct_ioctl,
560422d97b8SChris Packham 	.ndo_change_mtu		= cvm_oct_common_change_mtu,
561422d97b8SChris Packham 	.ndo_get_stats		= cvm_oct_common_get_stats,
562422d97b8SChris Packham #ifdef CONFIG_NET_POLL_CONTROLLER
563422d97b8SChris Packham 	.ndo_poll_controller	= cvm_oct_poll_controller,
564422d97b8SChris Packham #endif
565422d97b8SChris Packham };
566422d97b8SChris Packham 
567422d97b8SChris Packham static const struct net_device_ops cvm_oct_spi_netdev_ops = {
568422d97b8SChris Packham 	.ndo_init		= cvm_oct_spi_init,
569422d97b8SChris Packham 	.ndo_uninit		= cvm_oct_spi_uninit,
570422d97b8SChris Packham 	.ndo_start_xmit		= cvm_oct_xmit,
571422d97b8SChris Packham 	.ndo_set_rx_mode	= cvm_oct_common_set_multicast_list,
572422d97b8SChris Packham 	.ndo_set_mac_address	= cvm_oct_common_set_mac_address,
573a7605370SArnd Bergmann 	.ndo_eth_ioctl		= cvm_oct_ioctl,
574422d97b8SChris Packham 	.ndo_change_mtu		= cvm_oct_common_change_mtu,
575422d97b8SChris Packham 	.ndo_get_stats		= cvm_oct_common_get_stats,
576422d97b8SChris Packham #ifdef CONFIG_NET_POLL_CONTROLLER
577422d97b8SChris Packham 	.ndo_poll_controller	= cvm_oct_poll_controller,
578422d97b8SChris Packham #endif
579422d97b8SChris Packham };
580422d97b8SChris Packham 
581422d97b8SChris Packham static const struct net_device_ops cvm_oct_rgmii_netdev_ops = {
582422d97b8SChris Packham 	.ndo_init		= cvm_oct_common_init,
583422d97b8SChris Packham 	.ndo_uninit		= cvm_oct_common_uninit,
584422d97b8SChris Packham 	.ndo_open		= cvm_oct_rgmii_open,
585422d97b8SChris Packham 	.ndo_stop		= cvm_oct_common_stop,
586422d97b8SChris Packham 	.ndo_start_xmit		= cvm_oct_xmit,
587422d97b8SChris Packham 	.ndo_set_rx_mode	= cvm_oct_common_set_multicast_list,
588422d97b8SChris Packham 	.ndo_set_mac_address	= cvm_oct_common_set_mac_address,
589a7605370SArnd Bergmann 	.ndo_eth_ioctl		= cvm_oct_ioctl,
590422d97b8SChris Packham 	.ndo_change_mtu		= cvm_oct_common_change_mtu,
591422d97b8SChris Packham 	.ndo_get_stats		= cvm_oct_common_get_stats,
592422d97b8SChris Packham #ifdef CONFIG_NET_POLL_CONTROLLER
593422d97b8SChris Packham 	.ndo_poll_controller	= cvm_oct_poll_controller,
594422d97b8SChris Packham #endif
595422d97b8SChris Packham };
596422d97b8SChris Packham 
597422d97b8SChris Packham static const struct net_device_ops cvm_oct_pow_netdev_ops = {
598422d97b8SChris Packham 	.ndo_init		= cvm_oct_common_init,
599422d97b8SChris Packham 	.ndo_start_xmit		= cvm_oct_xmit_pow,
600422d97b8SChris Packham 	.ndo_set_rx_mode	= cvm_oct_common_set_multicast_list,
601422d97b8SChris Packham 	.ndo_set_mac_address	= cvm_oct_common_set_mac_address,
602a7605370SArnd Bergmann 	.ndo_eth_ioctl		= cvm_oct_ioctl,
603422d97b8SChris Packham 	.ndo_change_mtu		= cvm_oct_common_change_mtu,
604422d97b8SChris Packham 	.ndo_get_stats		= cvm_oct_common_get_stats,
605422d97b8SChris Packham #ifdef CONFIG_NET_POLL_CONTROLLER
606422d97b8SChris Packham 	.ndo_poll_controller	= cvm_oct_poll_controller,
607422d97b8SChris Packham #endif
608422d97b8SChris Packham };
609422d97b8SChris Packham 
cvm_oct_of_get_child(const struct device_node * parent,int reg_val)610422d97b8SChris Packham static struct device_node *cvm_oct_of_get_child
611422d97b8SChris Packham 				(const struct device_node *parent, int reg_val)
612422d97b8SChris Packham {
613c295d300SChristophe JAILLET 	struct device_node *node;
614422d97b8SChris Packham 	const __be32 *addr;
615c295d300SChristophe JAILLET 	int size;
616422d97b8SChris Packham 
617c295d300SChristophe JAILLET 	for_each_child_of_node(parent, node) {
618422d97b8SChris Packham 		addr = of_get_property(node, "reg", &size);
619422d97b8SChris Packham 		if (addr && (be32_to_cpu(*addr) == reg_val))
620422d97b8SChris Packham 			break;
621422d97b8SChris Packham 	}
622422d97b8SChris Packham 	return node;
623422d97b8SChris Packham }
624422d97b8SChris Packham 
cvm_oct_node_for_port(struct device_node * pip,int interface,int port)625422d97b8SChris Packham static struct device_node *cvm_oct_node_for_port(struct device_node *pip,
626422d97b8SChris Packham 						 int interface, int port)
627422d97b8SChris Packham {
628422d97b8SChris Packham 	struct device_node *ni, *np;
629422d97b8SChris Packham 
630422d97b8SChris Packham 	ni = cvm_oct_of_get_child(pip, interface);
631422d97b8SChris Packham 	if (!ni)
632422d97b8SChris Packham 		return NULL;
633422d97b8SChris Packham 
634422d97b8SChris Packham 	np = cvm_oct_of_get_child(ni, port);
635422d97b8SChris Packham 	of_node_put(ni);
636422d97b8SChris Packham 
637422d97b8SChris Packham 	return np;
638422d97b8SChris Packham }
639422d97b8SChris Packham 
cvm_set_rgmii_delay(struct octeon_ethernet * priv,int iface,int port)640422d97b8SChris Packham static void cvm_set_rgmii_delay(struct octeon_ethernet *priv, int iface,
641422d97b8SChris Packham 				int port)
642422d97b8SChris Packham {
643422d97b8SChris Packham 	struct device_node *np = priv->of_node;
644422d97b8SChris Packham 	u32 delay_value;
645422d97b8SChris Packham 	bool rx_delay;
646422d97b8SChris Packham 	bool tx_delay;
647422d97b8SChris Packham 
648422d97b8SChris Packham 	/* By default, both RX/TX delay is enabled in
649422d97b8SChris Packham 	 * __cvmx_helper_rgmii_enable().
650422d97b8SChris Packham 	 */
651422d97b8SChris Packham 	rx_delay = true;
652422d97b8SChris Packham 	tx_delay = true;
653422d97b8SChris Packham 
654422d97b8SChris Packham 	if (!of_property_read_u32(np, "rx-delay", &delay_value)) {
655422d97b8SChris Packham 		cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, iface), delay_value);
656422d97b8SChris Packham 		rx_delay = delay_value > 0;
657422d97b8SChris Packham 	}
658422d97b8SChris Packham 	if (!of_property_read_u32(np, "tx-delay", &delay_value)) {
659422d97b8SChris Packham 		cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, iface), delay_value);
660422d97b8SChris Packham 		tx_delay = delay_value > 0;
661422d97b8SChris Packham 	}
662422d97b8SChris Packham 
663422d97b8SChris Packham 	if (!rx_delay && !tx_delay)
664422d97b8SChris Packham 		priv->phy_mode = PHY_INTERFACE_MODE_RGMII_ID;
665422d97b8SChris Packham 	else if (!rx_delay)
666422d97b8SChris Packham 		priv->phy_mode = PHY_INTERFACE_MODE_RGMII_RXID;
667422d97b8SChris Packham 	else if (!tx_delay)
668422d97b8SChris Packham 		priv->phy_mode = PHY_INTERFACE_MODE_RGMII_TXID;
669422d97b8SChris Packham 	else
670422d97b8SChris Packham 		priv->phy_mode = PHY_INTERFACE_MODE_RGMII;
671422d97b8SChris Packham }
672422d97b8SChris Packham 
cvm_oct_probe(struct platform_device * pdev)673422d97b8SChris Packham static int cvm_oct_probe(struct platform_device *pdev)
674422d97b8SChris Packham {
675422d97b8SChris Packham 	int num_interfaces;
676422d97b8SChris Packham 	int interface;
677422d97b8SChris Packham 	int fau = FAU_NUM_PACKET_BUFFERS_TO_FREE;
678422d97b8SChris Packham 	int qos;
679422d97b8SChris Packham 	struct device_node *pip;
680422d97b8SChris Packham 	int mtu_overhead = ETH_HLEN + ETH_FCS_LEN;
681422d97b8SChris Packham 
682422d97b8SChris Packham #if IS_ENABLED(CONFIG_VLAN_8021Q)
683422d97b8SChris Packham 	mtu_overhead += VLAN_HLEN;
684422d97b8SChris Packham #endif
685422d97b8SChris Packham 
686422d97b8SChris Packham 	pip = pdev->dev.of_node;
687422d97b8SChris Packham 	if (!pip) {
688422d97b8SChris Packham 		pr_err("Error: No 'pip' in /aliases\n");
689422d97b8SChris Packham 		return -EINVAL;
690422d97b8SChris Packham 	}
691422d97b8SChris Packham 
692422d97b8SChris Packham 	cvm_oct_configure_common_hw();
693422d97b8SChris Packham 
694422d97b8SChris Packham 	cvmx_helper_initialize_packet_io_global();
695422d97b8SChris Packham 
696422d97b8SChris Packham 	if (receive_group_order) {
697422d97b8SChris Packham 		if (receive_group_order > 4)
698422d97b8SChris Packham 			receive_group_order = 4;
699422d97b8SChris Packham 		pow_receive_groups = (1 << (1 << receive_group_order)) - 1;
700422d97b8SChris Packham 	} else {
701422d97b8SChris Packham 		pow_receive_groups = BIT(pow_receive_group);
702422d97b8SChris Packham 	}
703422d97b8SChris Packham 
704422d97b8SChris Packham 	/* Change the input group for all ports before input is enabled */
705422d97b8SChris Packham 	num_interfaces = cvmx_helper_get_number_of_interfaces();
706422d97b8SChris Packham 	for (interface = 0; interface < num_interfaces; interface++) {
707422d97b8SChris Packham 		int num_ports = cvmx_helper_ports_on_interface(interface);
708422d97b8SChris Packham 		int port;
709422d97b8SChris Packham 
710422d97b8SChris Packham 		for (port = cvmx_helper_get_ipd_port(interface, 0);
711422d97b8SChris Packham 		     port < cvmx_helper_get_ipd_port(interface, num_ports);
712422d97b8SChris Packham 		     port++) {
713422d97b8SChris Packham 			union cvmx_pip_prt_tagx pip_prt_tagx;
714422d97b8SChris Packham 
715422d97b8SChris Packham 			pip_prt_tagx.u64 =
716422d97b8SChris Packham 			    cvmx_read_csr(CVMX_PIP_PRT_TAGX(port));
717422d97b8SChris Packham 
718422d97b8SChris Packham 			if (receive_group_order) {
719422d97b8SChris Packham 				int tag_mask;
720422d97b8SChris Packham 
721422d97b8SChris Packham 				/* We support only 16 groups at the moment, so
722422d97b8SChris Packham 				 * always disable the two additional "hidden"
723422d97b8SChris Packham 				 * tag_mask bits on CN68XX.
724422d97b8SChris Packham 				 */
725422d97b8SChris Packham 				if (OCTEON_IS_MODEL(OCTEON_CN68XX))
726422d97b8SChris Packham 					pip_prt_tagx.u64 |= 0x3ull << 44;
727422d97b8SChris Packham 
728422d97b8SChris Packham 				tag_mask = ~((1 << receive_group_order) - 1);
729422d97b8SChris Packham 				pip_prt_tagx.s.grptagbase	= 0;
730422d97b8SChris Packham 				pip_prt_tagx.s.grptagmask	= tag_mask;
731422d97b8SChris Packham 				pip_prt_tagx.s.grptag		= 1;
732422d97b8SChris Packham 				pip_prt_tagx.s.tag_mode		= 0;
733422d97b8SChris Packham 				pip_prt_tagx.s.inc_prt_flag	= 1;
734422d97b8SChris Packham 				pip_prt_tagx.s.ip6_dprt_flag	= 1;
735422d97b8SChris Packham 				pip_prt_tagx.s.ip4_dprt_flag	= 1;
736422d97b8SChris Packham 				pip_prt_tagx.s.ip6_sprt_flag	= 1;
737422d97b8SChris Packham 				pip_prt_tagx.s.ip4_sprt_flag	= 1;
738422d97b8SChris Packham 				pip_prt_tagx.s.ip6_dst_flag	= 1;
739422d97b8SChris Packham 				pip_prt_tagx.s.ip4_dst_flag	= 1;
740422d97b8SChris Packham 				pip_prt_tagx.s.ip6_src_flag	= 1;
741422d97b8SChris Packham 				pip_prt_tagx.s.ip4_src_flag	= 1;
742422d97b8SChris Packham 				pip_prt_tagx.s.grp		= 0;
743422d97b8SChris Packham 			} else {
744422d97b8SChris Packham 				pip_prt_tagx.s.grptag	= 0;
745422d97b8SChris Packham 				pip_prt_tagx.s.grp	= pow_receive_group;
746422d97b8SChris Packham 			}
747422d97b8SChris Packham 
748422d97b8SChris Packham 			cvmx_write_csr(CVMX_PIP_PRT_TAGX(port),
749422d97b8SChris Packham 				       pip_prt_tagx.u64);
750422d97b8SChris Packham 		}
751422d97b8SChris Packham 	}
752422d97b8SChris Packham 
753422d97b8SChris Packham 	cvmx_helper_ipd_and_packet_input_enable();
754422d97b8SChris Packham 
755422d97b8SChris Packham 	memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
756422d97b8SChris Packham 
757422d97b8SChris Packham 	/*
758422d97b8SChris Packham 	 * Initialize the FAU used for counting packet buffers that
759422d97b8SChris Packham 	 * need to be freed.
760422d97b8SChris Packham 	 */
761422d97b8SChris Packham 	cvmx_fau_atomic_write32(FAU_NUM_PACKET_BUFFERS_TO_FREE, 0);
762422d97b8SChris Packham 
763422d97b8SChris Packham 	/* Initialize the FAU used for counting tx SKBs that need to be freed */
764422d97b8SChris Packham 	cvmx_fau_atomic_write32(FAU_TOTAL_TX_TO_CLEAN, 0);
765422d97b8SChris Packham 
766422d97b8SChris Packham 	if ((pow_send_group != -1)) {
767422d97b8SChris Packham 		struct net_device *dev;
768422d97b8SChris Packham 
769422d97b8SChris Packham 		dev = alloc_etherdev(sizeof(struct octeon_ethernet));
770422d97b8SChris Packham 		if (dev) {
771422d97b8SChris Packham 			/* Initialize the device private structure. */
772422d97b8SChris Packham 			struct octeon_ethernet *priv = netdev_priv(dev);
773422d97b8SChris Packham 
774422d97b8SChris Packham 			SET_NETDEV_DEV(dev, &pdev->dev);
775422d97b8SChris Packham 			dev->netdev_ops = &cvm_oct_pow_netdev_ops;
776422d97b8SChris Packham 			priv->imode = CVMX_HELPER_INTERFACE_MODE_DISABLED;
777422d97b8SChris Packham 			priv->port = CVMX_PIP_NUM_INPUT_PORTS;
778422d97b8SChris Packham 			priv->queue = -1;
779422d97b8SChris Packham 			strscpy(dev->name, "pow%d", sizeof(dev->name));
780422d97b8SChris Packham 			for (qos = 0; qos < 16; qos++)
781422d97b8SChris Packham 				skb_queue_head_init(&priv->tx_free_list[qos]);
782422d97b8SChris Packham 			dev->min_mtu = VLAN_ETH_ZLEN - mtu_overhead;
783422d97b8SChris Packham 			dev->max_mtu = OCTEON_MAX_MTU - mtu_overhead;
784422d97b8SChris Packham 
785422d97b8SChris Packham 			if (register_netdev(dev) < 0) {
786422d97b8SChris Packham 				pr_err("Failed to register ethernet device for POW\n");
787422d97b8SChris Packham 				free_netdev(dev);
788422d97b8SChris Packham 			} else {
789422d97b8SChris Packham 				cvm_oct_device[CVMX_PIP_NUM_INPUT_PORTS] = dev;
790422d97b8SChris Packham 				pr_info("%s: POW send group %d, receive group %d\n",
791422d97b8SChris Packham 					dev->name, pow_send_group,
792422d97b8SChris Packham 					pow_receive_group);
793422d97b8SChris Packham 			}
794422d97b8SChris Packham 		} else {
795422d97b8SChris Packham 			pr_err("Failed to allocate ethernet device for POW\n");
796422d97b8SChris Packham 		}
797422d97b8SChris Packham 	}
798422d97b8SChris Packham 
799422d97b8SChris Packham 	num_interfaces = cvmx_helper_get_number_of_interfaces();
800422d97b8SChris Packham 	for (interface = 0; interface < num_interfaces; interface++) {
801422d97b8SChris Packham 		cvmx_helper_interface_mode_t imode =
802422d97b8SChris Packham 		    cvmx_helper_interface_get_mode(interface);
803422d97b8SChris Packham 		int num_ports = cvmx_helper_ports_on_interface(interface);
804422d97b8SChris Packham 		int port;
805422d97b8SChris Packham 		int port_index;
806422d97b8SChris Packham 
807422d97b8SChris Packham 		for (port_index = 0,
808422d97b8SChris Packham 		     port = cvmx_helper_get_ipd_port(interface, 0);
809422d97b8SChris Packham 		     port < cvmx_helper_get_ipd_port(interface, num_ports);
810422d97b8SChris Packham 		     port_index++, port++) {
811422d97b8SChris Packham 			struct octeon_ethernet *priv;
812422d97b8SChris Packham 			struct net_device *dev =
813422d97b8SChris Packham 			    alloc_etherdev(sizeof(struct octeon_ethernet));
814422d97b8SChris Packham 			if (!dev) {
815422d97b8SChris Packham 				pr_err("Failed to allocate ethernet device for port %d\n",
816422d97b8SChris Packham 				       port);
817422d97b8SChris Packham 				continue;
818422d97b8SChris Packham 			}
819422d97b8SChris Packham 
820422d97b8SChris Packham 			/* Initialize the device private structure. */
821422d97b8SChris Packham 			SET_NETDEV_DEV(dev, &pdev->dev);
822422d97b8SChris Packham 			priv = netdev_priv(dev);
823422d97b8SChris Packham 			priv->netdev = dev;
824422d97b8SChris Packham 			priv->of_node = cvm_oct_node_for_port(pip, interface,
825422d97b8SChris Packham 							      port_index);
826422d97b8SChris Packham 
827422d97b8SChris Packham 			INIT_DELAYED_WORK(&priv->port_periodic_work,
828422d97b8SChris Packham 					  cvm_oct_periodic_worker);
829422d97b8SChris Packham 			priv->imode = imode;
830422d97b8SChris Packham 			priv->port = port;
831422d97b8SChris Packham 			priv->queue = cvmx_pko_get_base_queue(priv->port);
832422d97b8SChris Packham 			priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
833422d97b8SChris Packham 			priv->phy_mode = PHY_INTERFACE_MODE_NA;
834422d97b8SChris Packham 			for (qos = 0; qos < 16; qos++)
835422d97b8SChris Packham 				skb_queue_head_init(&priv->tx_free_list[qos]);
836422d97b8SChris Packham 			for (qos = 0; qos < cvmx_pko_get_num_queues(port);
837422d97b8SChris Packham 			     qos++)
838422d97b8SChris Packham 				cvmx_fau_atomic_write32(priv->fau + qos * 4, 0);
839422d97b8SChris Packham 			dev->min_mtu = VLAN_ETH_ZLEN - mtu_overhead;
840422d97b8SChris Packham 			dev->max_mtu = OCTEON_MAX_MTU - mtu_overhead;
841422d97b8SChris Packham 
842422d97b8SChris Packham 			switch (priv->imode) {
843422d97b8SChris Packham 			/* These types don't support ports to IPD/PKO */
844422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_DISABLED:
845422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_PCIE:
846422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_PICMG:
847422d97b8SChris Packham 				break;
848422d97b8SChris Packham 
849422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_NPI:
850422d97b8SChris Packham 				dev->netdev_ops = &cvm_oct_npi_netdev_ops;
851422d97b8SChris Packham 				strscpy(dev->name, "npi%d", sizeof(dev->name));
852422d97b8SChris Packham 				break;
853422d97b8SChris Packham 
854422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_XAUI:
855422d97b8SChris Packham 				dev->netdev_ops = &cvm_oct_xaui_netdev_ops;
856422d97b8SChris Packham 				strscpy(dev->name, "xaui%d", sizeof(dev->name));
857422d97b8SChris Packham 				break;
858422d97b8SChris Packham 
859422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_LOOP:
860422d97b8SChris Packham 				dev->netdev_ops = &cvm_oct_npi_netdev_ops;
861422d97b8SChris Packham 				strscpy(dev->name, "loop%d", sizeof(dev->name));
862422d97b8SChris Packham 				break;
863422d97b8SChris Packham 
864422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_SGMII:
865422d97b8SChris Packham 				priv->phy_mode = PHY_INTERFACE_MODE_SGMII;
866422d97b8SChris Packham 				dev->netdev_ops = &cvm_oct_sgmii_netdev_ops;
867422d97b8SChris Packham 				strscpy(dev->name, "eth%d", sizeof(dev->name));
868422d97b8SChris Packham 				break;
869422d97b8SChris Packham 
870422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_SPI:
871422d97b8SChris Packham 				dev->netdev_ops = &cvm_oct_spi_netdev_ops;
872422d97b8SChris Packham 				strscpy(dev->name, "spi%d", sizeof(dev->name));
873422d97b8SChris Packham 				break;
874422d97b8SChris Packham 
875422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_GMII:
876422d97b8SChris Packham 				priv->phy_mode = PHY_INTERFACE_MODE_GMII;
877422d97b8SChris Packham 				dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
878422d97b8SChris Packham 				strscpy(dev->name, "eth%d", sizeof(dev->name));
879422d97b8SChris Packham 				break;
880422d97b8SChris Packham 
881422d97b8SChris Packham 			case CVMX_HELPER_INTERFACE_MODE_RGMII:
882422d97b8SChris Packham 				dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
883422d97b8SChris Packham 				strscpy(dev->name, "eth%d", sizeof(dev->name));
884422d97b8SChris Packham 				cvm_set_rgmii_delay(priv, interface,
885422d97b8SChris Packham 						    port_index);
886422d97b8SChris Packham 				break;
887422d97b8SChris Packham 			}
888422d97b8SChris Packham 
889179f5dc3SAlexander Sverdlin 			if (priv->of_node && of_phy_is_fixed_link(priv->of_node)) {
890179f5dc3SAlexander Sverdlin 				if (of_phy_register_fixed_link(priv->of_node)) {
891179f5dc3SAlexander Sverdlin 					netdev_err(dev, "Failed to register fixed link for interface %d, port %d\n",
892179f5dc3SAlexander Sverdlin 						   interface, priv->port);
893179f5dc3SAlexander Sverdlin 					dev->netdev_ops = NULL;
894179f5dc3SAlexander Sverdlin 				}
895179f5dc3SAlexander Sverdlin 			}
896179f5dc3SAlexander Sverdlin 
897422d97b8SChris Packham 			if (!dev->netdev_ops) {
898422d97b8SChris Packham 				free_netdev(dev);
899422d97b8SChris Packham 			} else if (register_netdev(dev) < 0) {
900422d97b8SChris Packham 				pr_err("Failed to register ethernet device for interface %d, port %d\n",
901422d97b8SChris Packham 				       interface, priv->port);
902422d97b8SChris Packham 				free_netdev(dev);
903422d97b8SChris Packham 			} else {
904422d97b8SChris Packham 				cvm_oct_device[priv->port] = dev;
905422d97b8SChris Packham 				fau -=
906422d97b8SChris Packham 				    cvmx_pko_get_num_queues(priv->port) *
907422d97b8SChris Packham 				    sizeof(u32);
908422d97b8SChris Packham 				schedule_delayed_work(&priv->port_periodic_work,
909422d97b8SChris Packham 						      HZ);
910422d97b8SChris Packham 			}
911422d97b8SChris Packham 		}
912422d97b8SChris Packham 	}
913422d97b8SChris Packham 
914422d97b8SChris Packham 	cvm_oct_tx_initialize();
915422d97b8SChris Packham 	cvm_oct_rx_initialize();
916422d97b8SChris Packham 
917422d97b8SChris Packham 	/*
918422d97b8SChris Packham 	 * 150 uS: about 10 1500-byte packets at 1GE.
919422d97b8SChris Packham 	 */
920422d97b8SChris Packham 	cvm_oct_tx_poll_interval = 150 * (octeon_get_clock_rate() / 1000000);
921422d97b8SChris Packham 
922422d97b8SChris Packham 	schedule_delayed_work(&cvm_oct_rx_refill_work, HZ);
923422d97b8SChris Packham 
924422d97b8SChris Packham 	return 0;
925422d97b8SChris Packham }
926422d97b8SChris Packham 
cvm_oct_remove(struct platform_device * pdev)927*c46d4073SUwe Kleine-König static void cvm_oct_remove(struct platform_device *pdev)
928422d97b8SChris Packham {
929422d97b8SChris Packham 	int port;
930422d97b8SChris Packham 
931422d97b8SChris Packham 	cvmx_ipd_disable();
932422d97b8SChris Packham 
933422d97b8SChris Packham 	atomic_inc_return(&cvm_oct_poll_queue_stopping);
934422d97b8SChris Packham 	cancel_delayed_work_sync(&cvm_oct_rx_refill_work);
935422d97b8SChris Packham 
936422d97b8SChris Packham 	cvm_oct_rx_shutdown();
937422d97b8SChris Packham 	cvm_oct_tx_shutdown();
938422d97b8SChris Packham 
939422d97b8SChris Packham 	cvmx_pko_disable();
940422d97b8SChris Packham 
941422d97b8SChris Packham 	/* Free the ethernet devices */
942422d97b8SChris Packham 	for (port = 0; port < TOTAL_NUMBER_OF_PORTS; port++) {
943422d97b8SChris Packham 		if (cvm_oct_device[port]) {
944422d97b8SChris Packham 			struct net_device *dev = cvm_oct_device[port];
945422d97b8SChris Packham 			struct octeon_ethernet *priv = netdev_priv(dev);
946422d97b8SChris Packham 
947422d97b8SChris Packham 			cancel_delayed_work_sync(&priv->port_periodic_work);
948422d97b8SChris Packham 
949422d97b8SChris Packham 			cvm_oct_tx_shutdown_dev(dev);
950422d97b8SChris Packham 			unregister_netdev(dev);
951422d97b8SChris Packham 			free_netdev(dev);
952422d97b8SChris Packham 			cvm_oct_device[port] = NULL;
953422d97b8SChris Packham 		}
954422d97b8SChris Packham 	}
955422d97b8SChris Packham 
956422d97b8SChris Packham 	cvmx_pko_shutdown();
957422d97b8SChris Packham 
958422d97b8SChris Packham 	cvmx_ipd_free_ptr();
959422d97b8SChris Packham 
960422d97b8SChris Packham 	/* Free the HW pools */
961422d97b8SChris Packham 	cvm_oct_mem_empty_fpa(CVMX_FPA_PACKET_POOL, CVMX_FPA_PACKET_POOL_SIZE,
962422d97b8SChris Packham 			      num_packet_buffers);
963422d97b8SChris Packham 	cvm_oct_mem_empty_fpa(CVMX_FPA_WQE_POOL, CVMX_FPA_WQE_POOL_SIZE,
964422d97b8SChris Packham 			      num_packet_buffers);
965422d97b8SChris Packham 	if (CVMX_FPA_OUTPUT_BUFFER_POOL != CVMX_FPA_PACKET_POOL)
966422d97b8SChris Packham 		cvm_oct_mem_empty_fpa(CVMX_FPA_OUTPUT_BUFFER_POOL,
967422d97b8SChris Packham 				      CVMX_FPA_OUTPUT_BUFFER_POOL_SIZE, 128);
968422d97b8SChris Packham }
969422d97b8SChris Packham 
970422d97b8SChris Packham static const struct of_device_id cvm_oct_match[] = {
971422d97b8SChris Packham 	{
972422d97b8SChris Packham 		.compatible = "cavium,octeon-3860-pip",
973422d97b8SChris Packham 	},
974422d97b8SChris Packham 	{},
975422d97b8SChris Packham };
976422d97b8SChris Packham MODULE_DEVICE_TABLE(of, cvm_oct_match);
977422d97b8SChris Packham 
978422d97b8SChris Packham static struct platform_driver cvm_oct_driver = {
979422d97b8SChris Packham 	.probe		= cvm_oct_probe,
980*c46d4073SUwe Kleine-König 	.remove_new	= cvm_oct_remove,
981422d97b8SChris Packham 	.driver		= {
982422d97b8SChris Packham 		.name	= KBUILD_MODNAME,
983422d97b8SChris Packham 		.of_match_table = cvm_oct_match,
984422d97b8SChris Packham 	},
985422d97b8SChris Packham };
986422d97b8SChris Packham 
987422d97b8SChris Packham module_platform_driver(cvm_oct_driver);
988422d97b8SChris Packham 
989791e5f61SAndrew Lunn MODULE_SOFTDEP("pre: mdio-cavium");
990422d97b8SChris Packham MODULE_LICENSE("GPL");
991422d97b8SChris Packham MODULE_AUTHOR("Cavium Networks <support@caviumnetworks.com>");
992422d97b8SChris Packham MODULE_DESCRIPTION("Cavium Networks Octeon ethernet driver.");
993