18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Marvell Discovery (MV643XX) and Marvell Orion ethernet ports 48c2ecf20Sopenharmony_ci * Copyright (C) 2002 Matthew Dharm <mdharm@momenco.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based on the 64360 driver from: 78c2ecf20Sopenharmony_ci * Copyright (C) 2002 Rabeeh Khoury <rabeeh@galileo.co.il> 88c2ecf20Sopenharmony_ci * Rabeeh Khoury <rabeeh@marvell.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2003 PMC-Sierra, Inc., 118c2ecf20Sopenharmony_ci * written by Manish Lachwani 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright (C) 2003 Ralf Baechle <ralf@linux-mips.org> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright (C) 2004-2006 MontaVista Software, Inc. 168c2ecf20Sopenharmony_ci * Dale Farnsworth <dale@farnsworth.org> 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Copyright (C) 2004 Steven J. Hill <sjhill1@rockwellcollins.com> 198c2ecf20Sopenharmony_ci * <sjhill@realitydiluted.com> 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Marvell Semiconductor 228c2ecf20Sopenharmony_ci * Lennert Buytenhek <buytenh@marvell.com> 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Copyright (C) 2013 Michael Stapelberg <michael@stapelberg.de> 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/init.h> 308c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 318c2ecf20Sopenharmony_ci#include <linux/in.h> 328c2ecf20Sopenharmony_ci#include <linux/ip.h> 338c2ecf20Sopenharmony_ci#include <net/tso.h> 348c2ecf20Sopenharmony_ci#include <linux/tcp.h> 358c2ecf20Sopenharmony_ci#include <linux/udp.h> 368c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 378c2ecf20Sopenharmony_ci#include <linux/delay.h> 388c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 398c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 408c2ecf20Sopenharmony_ci#include <linux/module.h> 418c2ecf20Sopenharmony_ci#include <linux/kernel.h> 428c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 438c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 448c2ecf20Sopenharmony_ci#include <linux/phy.h> 458c2ecf20Sopenharmony_ci#include <linux/mv643xx_eth.h> 468c2ecf20Sopenharmony_ci#include <linux/io.h> 478c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 488c2ecf20Sopenharmony_ci#include <linux/types.h> 498c2ecf20Sopenharmony_ci#include <linux/slab.h> 508c2ecf20Sopenharmony_ci#include <linux/clk.h> 518c2ecf20Sopenharmony_ci#include <linux/of.h> 528c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 538c2ecf20Sopenharmony_ci#include <linux/of_net.h> 548c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic char mv643xx_eth_driver_name[] = "mv643xx_eth"; 578c2ecf20Sopenharmony_cistatic char mv643xx_eth_driver_version[] = "1.4"; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Registers shared between all ports. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci#define PHY_ADDR 0x0000 648c2ecf20Sopenharmony_ci#define WINDOW_BASE(w) (0x0200 + ((w) << 3)) 658c2ecf20Sopenharmony_ci#define WINDOW_SIZE(w) (0x0204 + ((w) << 3)) 668c2ecf20Sopenharmony_ci#define WINDOW_REMAP_HIGH(w) (0x0280 + ((w) << 2)) 678c2ecf20Sopenharmony_ci#define WINDOW_BAR_ENABLE 0x0290 688c2ecf20Sopenharmony_ci#define WINDOW_PROTECT(w) (0x0294 + ((w) << 4)) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * Main per-port registers. These live at offset 0x0400 for 728c2ecf20Sopenharmony_ci * port #0, 0x0800 for port #1, and 0x0c00 for port #2. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci#define PORT_CONFIG 0x0000 758c2ecf20Sopenharmony_ci#define UNICAST_PROMISCUOUS_MODE 0x00000001 768c2ecf20Sopenharmony_ci#define PORT_CONFIG_EXT 0x0004 778c2ecf20Sopenharmony_ci#define MAC_ADDR_LOW 0x0014 788c2ecf20Sopenharmony_ci#define MAC_ADDR_HIGH 0x0018 798c2ecf20Sopenharmony_ci#define SDMA_CONFIG 0x001c 808c2ecf20Sopenharmony_ci#define TX_BURST_SIZE_16_64BIT 0x01000000 818c2ecf20Sopenharmony_ci#define TX_BURST_SIZE_4_64BIT 0x00800000 828c2ecf20Sopenharmony_ci#define BLM_TX_NO_SWAP 0x00000020 838c2ecf20Sopenharmony_ci#define BLM_RX_NO_SWAP 0x00000010 848c2ecf20Sopenharmony_ci#define RX_BURST_SIZE_16_64BIT 0x00000008 858c2ecf20Sopenharmony_ci#define RX_BURST_SIZE_4_64BIT 0x00000004 868c2ecf20Sopenharmony_ci#define PORT_SERIAL_CONTROL 0x003c 878c2ecf20Sopenharmony_ci#define SET_MII_SPEED_TO_100 0x01000000 888c2ecf20Sopenharmony_ci#define SET_GMII_SPEED_TO_1000 0x00800000 898c2ecf20Sopenharmony_ci#define SET_FULL_DUPLEX_MODE 0x00200000 908c2ecf20Sopenharmony_ci#define MAX_RX_PACKET_9700BYTE 0x000a0000 918c2ecf20Sopenharmony_ci#define DISABLE_AUTO_NEG_SPEED_GMII 0x00002000 928c2ecf20Sopenharmony_ci#define DO_NOT_FORCE_LINK_FAIL 0x00000400 938c2ecf20Sopenharmony_ci#define SERIAL_PORT_CONTROL_RESERVED 0x00000200 948c2ecf20Sopenharmony_ci#define DISABLE_AUTO_NEG_FOR_FLOW_CTRL 0x00000008 958c2ecf20Sopenharmony_ci#define DISABLE_AUTO_NEG_FOR_DUPLEX 0x00000004 968c2ecf20Sopenharmony_ci#define FORCE_LINK_PASS 0x00000002 978c2ecf20Sopenharmony_ci#define SERIAL_PORT_ENABLE 0x00000001 988c2ecf20Sopenharmony_ci#define PORT_STATUS 0x0044 998c2ecf20Sopenharmony_ci#define TX_FIFO_EMPTY 0x00000400 1008c2ecf20Sopenharmony_ci#define TX_IN_PROGRESS 0x00000080 1018c2ecf20Sopenharmony_ci#define PORT_SPEED_MASK 0x00000030 1028c2ecf20Sopenharmony_ci#define PORT_SPEED_1000 0x00000010 1038c2ecf20Sopenharmony_ci#define PORT_SPEED_100 0x00000020 1048c2ecf20Sopenharmony_ci#define PORT_SPEED_10 0x00000000 1058c2ecf20Sopenharmony_ci#define FLOW_CONTROL_ENABLED 0x00000008 1068c2ecf20Sopenharmony_ci#define FULL_DUPLEX 0x00000004 1078c2ecf20Sopenharmony_ci#define LINK_UP 0x00000002 1088c2ecf20Sopenharmony_ci#define TXQ_COMMAND 0x0048 1098c2ecf20Sopenharmony_ci#define TXQ_FIX_PRIO_CONF 0x004c 1108c2ecf20Sopenharmony_ci#define PORT_SERIAL_CONTROL1 0x004c 1118c2ecf20Sopenharmony_ci#define CLK125_BYPASS_EN 0x00000010 1128c2ecf20Sopenharmony_ci#define TX_BW_RATE 0x0050 1138c2ecf20Sopenharmony_ci#define TX_BW_MTU 0x0058 1148c2ecf20Sopenharmony_ci#define TX_BW_BURST 0x005c 1158c2ecf20Sopenharmony_ci#define INT_CAUSE 0x0060 1168c2ecf20Sopenharmony_ci#define INT_TX_END 0x07f80000 1178c2ecf20Sopenharmony_ci#define INT_TX_END_0 0x00080000 1188c2ecf20Sopenharmony_ci#define INT_RX 0x000003fc 1198c2ecf20Sopenharmony_ci#define INT_RX_0 0x00000004 1208c2ecf20Sopenharmony_ci#define INT_EXT 0x00000002 1218c2ecf20Sopenharmony_ci#define INT_CAUSE_EXT 0x0064 1228c2ecf20Sopenharmony_ci#define INT_EXT_LINK_PHY 0x00110000 1238c2ecf20Sopenharmony_ci#define INT_EXT_TX 0x000000ff 1248c2ecf20Sopenharmony_ci#define INT_MASK 0x0068 1258c2ecf20Sopenharmony_ci#define INT_MASK_EXT 0x006c 1268c2ecf20Sopenharmony_ci#define TX_FIFO_URGENT_THRESHOLD 0x0074 1278c2ecf20Sopenharmony_ci#define RX_DISCARD_FRAME_CNT 0x0084 1288c2ecf20Sopenharmony_ci#define RX_OVERRUN_FRAME_CNT 0x0088 1298c2ecf20Sopenharmony_ci#define TXQ_FIX_PRIO_CONF_MOVED 0x00dc 1308c2ecf20Sopenharmony_ci#define TX_BW_RATE_MOVED 0x00e0 1318c2ecf20Sopenharmony_ci#define TX_BW_MTU_MOVED 0x00e8 1328c2ecf20Sopenharmony_ci#define TX_BW_BURST_MOVED 0x00ec 1338c2ecf20Sopenharmony_ci#define RXQ_CURRENT_DESC_PTR(q) (0x020c + ((q) << 4)) 1348c2ecf20Sopenharmony_ci#define RXQ_COMMAND 0x0280 1358c2ecf20Sopenharmony_ci#define TXQ_CURRENT_DESC_PTR(q) (0x02c0 + ((q) << 2)) 1368c2ecf20Sopenharmony_ci#define TXQ_BW_TOKENS(q) (0x0300 + ((q) << 4)) 1378c2ecf20Sopenharmony_ci#define TXQ_BW_CONF(q) (0x0304 + ((q) << 4)) 1388c2ecf20Sopenharmony_ci#define TXQ_BW_WRR_CONF(q) (0x0308 + ((q) << 4)) 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * Misc per-port registers. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci#define MIB_COUNTERS(p) (0x1000 + ((p) << 7)) 1448c2ecf20Sopenharmony_ci#define SPECIAL_MCAST_TABLE(p) (0x1400 + ((p) << 10)) 1458c2ecf20Sopenharmony_ci#define OTHER_MCAST_TABLE(p) (0x1500 + ((p) << 10)) 1468c2ecf20Sopenharmony_ci#define UNICAST_TABLE(p) (0x1600 + ((p) << 10)) 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * SDMA configuration register default value. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN) 1538c2ecf20Sopenharmony_ci#define PORT_SDMA_CONFIG_DEFAULT_VALUE \ 1548c2ecf20Sopenharmony_ci (RX_BURST_SIZE_4_64BIT | \ 1558c2ecf20Sopenharmony_ci TX_BURST_SIZE_4_64BIT) 1568c2ecf20Sopenharmony_ci#elif defined(__LITTLE_ENDIAN) 1578c2ecf20Sopenharmony_ci#define PORT_SDMA_CONFIG_DEFAULT_VALUE \ 1588c2ecf20Sopenharmony_ci (RX_BURST_SIZE_4_64BIT | \ 1598c2ecf20Sopenharmony_ci BLM_RX_NO_SWAP | \ 1608c2ecf20Sopenharmony_ci BLM_TX_NO_SWAP | \ 1618c2ecf20Sopenharmony_ci TX_BURST_SIZE_4_64BIT) 1628c2ecf20Sopenharmony_ci#else 1638c2ecf20Sopenharmony_ci#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * Misc definitions. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci#define DEFAULT_RX_QUEUE_SIZE 128 1718c2ecf20Sopenharmony_ci#define DEFAULT_TX_QUEUE_SIZE 512 1728c2ecf20Sopenharmony_ci#define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES) 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* Max number of allowed TCP segments for software TSO */ 1758c2ecf20Sopenharmony_ci#define MV643XX_MAX_TSO_SEGS 100 1768c2ecf20Sopenharmony_ci#define MV643XX_MAX_SKB_DESCS (MV643XX_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci#define IS_TSO_HEADER(txq, addr) \ 1798c2ecf20Sopenharmony_ci ((addr >= txq->tso_hdrs_dma) && \ 1808c2ecf20Sopenharmony_ci (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE)) 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci#define DESC_DMA_MAP_SINGLE 0 1838c2ecf20Sopenharmony_ci#define DESC_DMA_MAP_PAGE 1 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * RX/TX descriptors. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN) 1898c2ecf20Sopenharmony_cistruct rx_desc { 1908c2ecf20Sopenharmony_ci u16 byte_cnt; /* Descriptor buffer byte count */ 1918c2ecf20Sopenharmony_ci u16 buf_size; /* Buffer size */ 1928c2ecf20Sopenharmony_ci u32 cmd_sts; /* Descriptor command status */ 1938c2ecf20Sopenharmony_ci u32 next_desc_ptr; /* Next descriptor pointer */ 1948c2ecf20Sopenharmony_ci u32 buf_ptr; /* Descriptor buffer pointer */ 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistruct tx_desc { 1988c2ecf20Sopenharmony_ci u16 byte_cnt; /* buffer byte count */ 1998c2ecf20Sopenharmony_ci u16 l4i_chk; /* CPU provided TCP checksum */ 2008c2ecf20Sopenharmony_ci u32 cmd_sts; /* Command/status field */ 2018c2ecf20Sopenharmony_ci u32 next_desc_ptr; /* Pointer to next descriptor */ 2028c2ecf20Sopenharmony_ci u32 buf_ptr; /* pointer to buffer for this descriptor*/ 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci#elif defined(__LITTLE_ENDIAN) 2058c2ecf20Sopenharmony_cistruct rx_desc { 2068c2ecf20Sopenharmony_ci u32 cmd_sts; /* Descriptor command status */ 2078c2ecf20Sopenharmony_ci u16 buf_size; /* Buffer size */ 2088c2ecf20Sopenharmony_ci u16 byte_cnt; /* Descriptor buffer byte count */ 2098c2ecf20Sopenharmony_ci u32 buf_ptr; /* Descriptor buffer pointer */ 2108c2ecf20Sopenharmony_ci u32 next_desc_ptr; /* Next descriptor pointer */ 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistruct tx_desc { 2148c2ecf20Sopenharmony_ci u32 cmd_sts; /* Command/status field */ 2158c2ecf20Sopenharmony_ci u16 l4i_chk; /* CPU provided TCP checksum */ 2168c2ecf20Sopenharmony_ci u16 byte_cnt; /* buffer byte count */ 2178c2ecf20Sopenharmony_ci u32 buf_ptr; /* pointer to buffer for this descriptor*/ 2188c2ecf20Sopenharmony_ci u32 next_desc_ptr; /* Pointer to next descriptor */ 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci#else 2218c2ecf20Sopenharmony_ci#error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined 2228c2ecf20Sopenharmony_ci#endif 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* RX & TX descriptor command */ 2258c2ecf20Sopenharmony_ci#define BUFFER_OWNED_BY_DMA 0x80000000 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* RX & TX descriptor status */ 2288c2ecf20Sopenharmony_ci#define ERROR_SUMMARY 0x00000001 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* RX descriptor status */ 2318c2ecf20Sopenharmony_ci#define LAYER_4_CHECKSUM_OK 0x40000000 2328c2ecf20Sopenharmony_ci#define RX_ENABLE_INTERRUPT 0x20000000 2338c2ecf20Sopenharmony_ci#define RX_FIRST_DESC 0x08000000 2348c2ecf20Sopenharmony_ci#define RX_LAST_DESC 0x04000000 2358c2ecf20Sopenharmony_ci#define RX_IP_HDR_OK 0x02000000 2368c2ecf20Sopenharmony_ci#define RX_PKT_IS_IPV4 0x01000000 2378c2ecf20Sopenharmony_ci#define RX_PKT_IS_ETHERNETV2 0x00800000 2388c2ecf20Sopenharmony_ci#define RX_PKT_LAYER4_TYPE_MASK 0x00600000 2398c2ecf20Sopenharmony_ci#define RX_PKT_LAYER4_TYPE_TCP_IPV4 0x00000000 2408c2ecf20Sopenharmony_ci#define RX_PKT_IS_VLAN_TAGGED 0x00080000 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* TX descriptor command */ 2438c2ecf20Sopenharmony_ci#define TX_ENABLE_INTERRUPT 0x00800000 2448c2ecf20Sopenharmony_ci#define GEN_CRC 0x00400000 2458c2ecf20Sopenharmony_ci#define TX_FIRST_DESC 0x00200000 2468c2ecf20Sopenharmony_ci#define TX_LAST_DESC 0x00100000 2478c2ecf20Sopenharmony_ci#define ZERO_PADDING 0x00080000 2488c2ecf20Sopenharmony_ci#define GEN_IP_V4_CHECKSUM 0x00040000 2498c2ecf20Sopenharmony_ci#define GEN_TCP_UDP_CHECKSUM 0x00020000 2508c2ecf20Sopenharmony_ci#define UDP_FRAME 0x00010000 2518c2ecf20Sopenharmony_ci#define MAC_HDR_EXTRA_4_BYTES 0x00008000 2528c2ecf20Sopenharmony_ci#define GEN_TCP_UDP_CHK_FULL 0x00000400 2538c2ecf20Sopenharmony_ci#define MAC_HDR_EXTRA_8_BYTES 0x00000200 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci#define TX_IHL_SHIFT 11 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* global *******************************************************************/ 2598c2ecf20Sopenharmony_cistruct mv643xx_eth_shared_private { 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * Ethernet controller base address. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci void __iomem *base; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * Per-port MBUS window access register value. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci u32 win_protect; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* 2718c2ecf20Sopenharmony_ci * Hardware-specific parameters. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci int extended_rx_coal_limit; 2748c2ecf20Sopenharmony_ci int tx_bw_control; 2758c2ecf20Sopenharmony_ci int tx_csum_limit; 2768c2ecf20Sopenharmony_ci struct clk *clk; 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci#define TX_BW_CONTROL_ABSENT 0 2808c2ecf20Sopenharmony_ci#define TX_BW_CONTROL_OLD_LAYOUT 1 2818c2ecf20Sopenharmony_ci#define TX_BW_CONTROL_NEW_LAYOUT 2 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic int mv643xx_eth_open(struct net_device *dev); 2848c2ecf20Sopenharmony_cistatic int mv643xx_eth_stop(struct net_device *dev); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* per-port *****************************************************************/ 2888c2ecf20Sopenharmony_cistruct mib_counters { 2898c2ecf20Sopenharmony_ci u64 good_octets_received; 2908c2ecf20Sopenharmony_ci u32 bad_octets_received; 2918c2ecf20Sopenharmony_ci u32 internal_mac_transmit_err; 2928c2ecf20Sopenharmony_ci u32 good_frames_received; 2938c2ecf20Sopenharmony_ci u32 bad_frames_received; 2948c2ecf20Sopenharmony_ci u32 broadcast_frames_received; 2958c2ecf20Sopenharmony_ci u32 multicast_frames_received; 2968c2ecf20Sopenharmony_ci u32 frames_64_octets; 2978c2ecf20Sopenharmony_ci u32 frames_65_to_127_octets; 2988c2ecf20Sopenharmony_ci u32 frames_128_to_255_octets; 2998c2ecf20Sopenharmony_ci u32 frames_256_to_511_octets; 3008c2ecf20Sopenharmony_ci u32 frames_512_to_1023_octets; 3018c2ecf20Sopenharmony_ci u32 frames_1024_to_max_octets; 3028c2ecf20Sopenharmony_ci u64 good_octets_sent; 3038c2ecf20Sopenharmony_ci u32 good_frames_sent; 3048c2ecf20Sopenharmony_ci u32 excessive_collision; 3058c2ecf20Sopenharmony_ci u32 multicast_frames_sent; 3068c2ecf20Sopenharmony_ci u32 broadcast_frames_sent; 3078c2ecf20Sopenharmony_ci u32 unrec_mac_control_received; 3088c2ecf20Sopenharmony_ci u32 fc_sent; 3098c2ecf20Sopenharmony_ci u32 good_fc_received; 3108c2ecf20Sopenharmony_ci u32 bad_fc_received; 3118c2ecf20Sopenharmony_ci u32 undersize_received; 3128c2ecf20Sopenharmony_ci u32 fragments_received; 3138c2ecf20Sopenharmony_ci u32 oversize_received; 3148c2ecf20Sopenharmony_ci u32 jabber_received; 3158c2ecf20Sopenharmony_ci u32 mac_receive_error; 3168c2ecf20Sopenharmony_ci u32 bad_crc_event; 3178c2ecf20Sopenharmony_ci u32 collision; 3188c2ecf20Sopenharmony_ci u32 late_collision; 3198c2ecf20Sopenharmony_ci /* Non MIB hardware counters */ 3208c2ecf20Sopenharmony_ci u32 rx_discard; 3218c2ecf20Sopenharmony_ci u32 rx_overrun; 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistruct rx_queue { 3258c2ecf20Sopenharmony_ci int index; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci int rx_ring_size; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci int rx_desc_count; 3308c2ecf20Sopenharmony_ci int rx_curr_desc; 3318c2ecf20Sopenharmony_ci int rx_used_desc; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci struct rx_desc *rx_desc_area; 3348c2ecf20Sopenharmony_ci dma_addr_t rx_desc_dma; 3358c2ecf20Sopenharmony_ci int rx_desc_area_size; 3368c2ecf20Sopenharmony_ci struct sk_buff **rx_skb; 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistruct tx_queue { 3408c2ecf20Sopenharmony_ci int index; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci int tx_ring_size; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci int tx_desc_count; 3458c2ecf20Sopenharmony_ci int tx_curr_desc; 3468c2ecf20Sopenharmony_ci int tx_used_desc; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci int tx_stop_threshold; 3498c2ecf20Sopenharmony_ci int tx_wake_threshold; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci char *tso_hdrs; 3528c2ecf20Sopenharmony_ci dma_addr_t tso_hdrs_dma; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci struct tx_desc *tx_desc_area; 3558c2ecf20Sopenharmony_ci char *tx_desc_mapping; /* array to track the type of the dma mapping */ 3568c2ecf20Sopenharmony_ci dma_addr_t tx_desc_dma; 3578c2ecf20Sopenharmony_ci int tx_desc_area_size; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci struct sk_buff_head tx_skb; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci unsigned long tx_packets; 3628c2ecf20Sopenharmony_ci unsigned long tx_bytes; 3638c2ecf20Sopenharmony_ci unsigned long tx_dropped; 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistruct mv643xx_eth_private { 3678c2ecf20Sopenharmony_ci struct mv643xx_eth_shared_private *shared; 3688c2ecf20Sopenharmony_ci void __iomem *base; 3698c2ecf20Sopenharmony_ci int port_num; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci struct net_device *dev; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci struct timer_list mib_counters_timer; 3748c2ecf20Sopenharmony_ci spinlock_t mib_counters_lock; 3758c2ecf20Sopenharmony_ci struct mib_counters mib_counters; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci struct work_struct tx_timeout_task; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci struct napi_struct napi; 3808c2ecf20Sopenharmony_ci u32 int_mask; 3818c2ecf20Sopenharmony_ci u8 oom; 3828c2ecf20Sopenharmony_ci u8 work_link; 3838c2ecf20Sopenharmony_ci u8 work_tx; 3848c2ecf20Sopenharmony_ci u8 work_tx_end; 3858c2ecf20Sopenharmony_ci u8 work_rx; 3868c2ecf20Sopenharmony_ci u8 work_rx_refill; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci int skb_size; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci /* 3918c2ecf20Sopenharmony_ci * RX state. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci int rx_ring_size; 3948c2ecf20Sopenharmony_ci unsigned long rx_desc_sram_addr; 3958c2ecf20Sopenharmony_ci int rx_desc_sram_size; 3968c2ecf20Sopenharmony_ci int rxq_count; 3978c2ecf20Sopenharmony_ci struct timer_list rx_oom; 3988c2ecf20Sopenharmony_ci struct rx_queue rxq[8]; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* 4018c2ecf20Sopenharmony_ci * TX state. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci int tx_ring_size; 4048c2ecf20Sopenharmony_ci unsigned long tx_desc_sram_addr; 4058c2ecf20Sopenharmony_ci int tx_desc_sram_size; 4068c2ecf20Sopenharmony_ci int txq_count; 4078c2ecf20Sopenharmony_ci struct tx_queue txq[8]; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * Hardware-specific parameters. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci struct clk *clk; 4138c2ecf20Sopenharmony_ci unsigned int t_clk; 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* port register accessors **************************************************/ 4188c2ecf20Sopenharmony_cistatic inline u32 rdl(struct mv643xx_eth_private *mp, int offset) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci return readl(mp->shared->base + offset); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic inline u32 rdlp(struct mv643xx_eth_private *mp, int offset) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci return readl(mp->base + offset); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic inline void wrl(struct mv643xx_eth_private *mp, int offset, u32 data) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci writel(data, mp->shared->base + offset); 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic inline void wrlp(struct mv643xx_eth_private *mp, int offset, u32 data) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci writel(data, mp->base + offset); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/* rxq/txq helper functions *************************************************/ 4408c2ecf20Sopenharmony_cistatic struct mv643xx_eth_private *rxq_to_mp(struct rx_queue *rxq) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci return container_of(rxq, struct mv643xx_eth_private, rxq[rxq->index]); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci return container_of(txq, struct mv643xx_eth_private, txq[txq->index]); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void rxq_enable(struct rx_queue *rxq) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = rxq_to_mp(rxq); 4538c2ecf20Sopenharmony_ci wrlp(mp, RXQ_COMMAND, 1 << rxq->index); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic void rxq_disable(struct rx_queue *rxq) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = rxq_to_mp(rxq); 4598c2ecf20Sopenharmony_ci u8 mask = 1 << rxq->index; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci wrlp(mp, RXQ_COMMAND, mask << 8); 4628c2ecf20Sopenharmony_ci while (rdlp(mp, RXQ_COMMAND) & mask) 4638c2ecf20Sopenharmony_ci udelay(10); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic void txq_reset_hw_ptr(struct tx_queue *txq) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 4698c2ecf20Sopenharmony_ci u32 addr; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci addr = (u32)txq->tx_desc_dma; 4728c2ecf20Sopenharmony_ci addr += txq->tx_curr_desc * sizeof(struct tx_desc); 4738c2ecf20Sopenharmony_ci wrlp(mp, TXQ_CURRENT_DESC_PTR(txq->index), addr); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void txq_enable(struct tx_queue *txq) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 4798c2ecf20Sopenharmony_ci wrlp(mp, TXQ_COMMAND, 1 << txq->index); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void txq_disable(struct tx_queue *txq) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 4858c2ecf20Sopenharmony_ci u8 mask = 1 << txq->index; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci wrlp(mp, TXQ_COMMAND, mask << 8); 4888c2ecf20Sopenharmony_ci while (rdlp(mp, TXQ_COMMAND) & mask) 4898c2ecf20Sopenharmony_ci udelay(10); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic void txq_maybe_wake(struct tx_queue *txq) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 4958c2ecf20Sopenharmony_ci struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (netif_tx_queue_stopped(nq)) { 4988c2ecf20Sopenharmony_ci __netif_tx_lock(nq, smp_processor_id()); 4998c2ecf20Sopenharmony_ci if (txq->tx_desc_count <= txq->tx_wake_threshold) 5008c2ecf20Sopenharmony_ci netif_tx_wake_queue(nq); 5018c2ecf20Sopenharmony_ci __netif_tx_unlock(nq); 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int rxq_process(struct rx_queue *rxq, int budget) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = rxq_to_mp(rxq); 5088c2ecf20Sopenharmony_ci struct net_device_stats *stats = &mp->dev->stats; 5098c2ecf20Sopenharmony_ci int rx; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci rx = 0; 5128c2ecf20Sopenharmony_ci while (rx < budget && rxq->rx_desc_count) { 5138c2ecf20Sopenharmony_ci struct rx_desc *rx_desc; 5148c2ecf20Sopenharmony_ci unsigned int cmd_sts; 5158c2ecf20Sopenharmony_ci struct sk_buff *skb; 5168c2ecf20Sopenharmony_ci u16 byte_cnt; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci rx_desc = &rxq->rx_desc_area[rxq->rx_curr_desc]; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci cmd_sts = rx_desc->cmd_sts; 5218c2ecf20Sopenharmony_ci if (cmd_sts & BUFFER_OWNED_BY_DMA) 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci rmb(); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci skb = rxq->rx_skb[rxq->rx_curr_desc]; 5268c2ecf20Sopenharmony_ci rxq->rx_skb[rxq->rx_curr_desc] = NULL; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci rxq->rx_curr_desc++; 5298c2ecf20Sopenharmony_ci if (rxq->rx_curr_desc == rxq->rx_ring_size) 5308c2ecf20Sopenharmony_ci rxq->rx_curr_desc = 0; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci dma_unmap_single(mp->dev->dev.parent, rx_desc->buf_ptr, 5338c2ecf20Sopenharmony_ci rx_desc->buf_size, DMA_FROM_DEVICE); 5348c2ecf20Sopenharmony_ci rxq->rx_desc_count--; 5358c2ecf20Sopenharmony_ci rx++; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci mp->work_rx_refill |= 1 << rxq->index; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci byte_cnt = rx_desc->byte_cnt; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * Update statistics. 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * Note that the descriptor byte count includes 2 dummy 5458c2ecf20Sopenharmony_ci * bytes automatically inserted by the hardware at the 5468c2ecf20Sopenharmony_ci * start of the packet (which we don't count), and a 4 5478c2ecf20Sopenharmony_ci * byte CRC at the end of the packet (which we do count). 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci stats->rx_packets++; 5508c2ecf20Sopenharmony_ci stats->rx_bytes += byte_cnt - 2; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* 5538c2ecf20Sopenharmony_ci * In case we received a packet without first / last bits 5548c2ecf20Sopenharmony_ci * on, or the error summary bit is set, the packet needs 5558c2ecf20Sopenharmony_ci * to be dropped. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC | ERROR_SUMMARY)) 5588c2ecf20Sopenharmony_ci != (RX_FIRST_DESC | RX_LAST_DESC)) 5598c2ecf20Sopenharmony_ci goto err; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * The -4 is for the CRC in the trailer of the 5638c2ecf20Sopenharmony_ci * received packet 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci skb_put(skb, byte_cnt - 2 - 4); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (cmd_sts & LAYER_4_CHECKSUM_OK) 5688c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 5698c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, mp->dev); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci napi_gro_receive(&mp->napi, skb); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci continue; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cierr: 5768c2ecf20Sopenharmony_ci stats->rx_dropped++; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != 5798c2ecf20Sopenharmony_ci (RX_FIRST_DESC | RX_LAST_DESC)) { 5808c2ecf20Sopenharmony_ci if (net_ratelimit()) 5818c2ecf20Sopenharmony_ci netdev_err(mp->dev, 5828c2ecf20Sopenharmony_ci "received packet spanning multiple descriptors\n"); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (cmd_sts & ERROR_SUMMARY) 5868c2ecf20Sopenharmony_ci stats->rx_errors++; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (rx < budget) 5928c2ecf20Sopenharmony_ci mp->work_rx &= ~(1 << rxq->index); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return rx; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic int rxq_refill(struct rx_queue *rxq, int budget) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = rxq_to_mp(rxq); 6008c2ecf20Sopenharmony_ci int refilled; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci refilled = 0; 6038c2ecf20Sopenharmony_ci while (refilled < budget && rxq->rx_desc_count < rxq->rx_ring_size) { 6048c2ecf20Sopenharmony_ci struct sk_buff *skb; 6058c2ecf20Sopenharmony_ci int rx; 6068c2ecf20Sopenharmony_ci struct rx_desc *rx_desc; 6078c2ecf20Sopenharmony_ci int size; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(mp->dev, mp->skb_size); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (skb == NULL) { 6128c2ecf20Sopenharmony_ci mp->oom = 1; 6138c2ecf20Sopenharmony_ci goto oom; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (SKB_DMA_REALIGN) 6178c2ecf20Sopenharmony_ci skb_reserve(skb, SKB_DMA_REALIGN); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci refilled++; 6208c2ecf20Sopenharmony_ci rxq->rx_desc_count++; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci rx = rxq->rx_used_desc++; 6238c2ecf20Sopenharmony_ci if (rxq->rx_used_desc == rxq->rx_ring_size) 6248c2ecf20Sopenharmony_ci rxq->rx_used_desc = 0; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci rx_desc = rxq->rx_desc_area + rx; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci size = skb_end_pointer(skb) - skb->data; 6298c2ecf20Sopenharmony_ci rx_desc->buf_ptr = dma_map_single(mp->dev->dev.parent, 6308c2ecf20Sopenharmony_ci skb->data, size, 6318c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6328c2ecf20Sopenharmony_ci rx_desc->buf_size = size; 6338c2ecf20Sopenharmony_ci rxq->rx_skb[rx] = skb; 6348c2ecf20Sopenharmony_ci wmb(); 6358c2ecf20Sopenharmony_ci rx_desc->cmd_sts = BUFFER_OWNED_BY_DMA | RX_ENABLE_INTERRUPT; 6368c2ecf20Sopenharmony_ci wmb(); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* 6398c2ecf20Sopenharmony_ci * The hardware automatically prepends 2 bytes of 6408c2ecf20Sopenharmony_ci * dummy data to each received packet, so that the 6418c2ecf20Sopenharmony_ci * IP header ends up 16-byte aligned. 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (refilled < budget) 6478c2ecf20Sopenharmony_ci mp->work_rx_refill &= ~(1 << rxq->index); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cioom: 6508c2ecf20Sopenharmony_ci return refilled; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/* tx ***********************************************************************/ 6558c2ecf20Sopenharmony_cistatic inline unsigned int has_tiny_unaligned_frags(struct sk_buff *skb) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci int frag; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { 6608c2ecf20Sopenharmony_ci const skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag]; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (skb_frag_size(fragp) <= 8 && skb_frag_off(fragp) & 7) 6638c2ecf20Sopenharmony_ci return 1; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic int skb_tx_csum(struct mv643xx_eth_private *mp, struct sk_buff *skb, 6708c2ecf20Sopenharmony_ci u16 *l4i_chk, u32 *command, int length) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci int ret; 6738c2ecf20Sopenharmony_ci u32 cmd = 0; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 6768c2ecf20Sopenharmony_ci int hdr_len; 6778c2ecf20Sopenharmony_ci int tag_bytes; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci BUG_ON(skb->protocol != htons(ETH_P_IP) && 6808c2ecf20Sopenharmony_ci skb->protocol != htons(ETH_P_8021Q)); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci hdr_len = (void *)ip_hdr(skb) - (void *)skb->data; 6838c2ecf20Sopenharmony_ci tag_bytes = hdr_len - ETH_HLEN; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (length - hdr_len > mp->shared->tx_csum_limit || 6868c2ecf20Sopenharmony_ci unlikely(tag_bytes & ~12)) { 6878c2ecf20Sopenharmony_ci ret = skb_checksum_help(skb); 6888c2ecf20Sopenharmony_ci if (!ret) 6898c2ecf20Sopenharmony_ci goto no_csum; 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (tag_bytes & 4) 6948c2ecf20Sopenharmony_ci cmd |= MAC_HDR_EXTRA_4_BYTES; 6958c2ecf20Sopenharmony_ci if (tag_bytes & 8) 6968c2ecf20Sopenharmony_ci cmd |= MAC_HDR_EXTRA_8_BYTES; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci cmd |= GEN_TCP_UDP_CHECKSUM | GEN_TCP_UDP_CHK_FULL | 6998c2ecf20Sopenharmony_ci GEN_IP_V4_CHECKSUM | 7008c2ecf20Sopenharmony_ci ip_hdr(skb)->ihl << TX_IHL_SHIFT; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* TODO: Revisit this. With the usage of GEN_TCP_UDP_CHK_FULL 7038c2ecf20Sopenharmony_ci * it seems we don't need to pass the initial checksum. */ 7048c2ecf20Sopenharmony_ci switch (ip_hdr(skb)->protocol) { 7058c2ecf20Sopenharmony_ci case IPPROTO_UDP: 7068c2ecf20Sopenharmony_ci cmd |= UDP_FRAME; 7078c2ecf20Sopenharmony_ci *l4i_chk = 0; 7088c2ecf20Sopenharmony_ci break; 7098c2ecf20Sopenharmony_ci case IPPROTO_TCP: 7108c2ecf20Sopenharmony_ci *l4i_chk = 0; 7118c2ecf20Sopenharmony_ci break; 7128c2ecf20Sopenharmony_ci default: 7138c2ecf20Sopenharmony_ci WARN(1, "protocol not supported"); 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci } else { 7168c2ecf20Sopenharmony_cino_csum: 7178c2ecf20Sopenharmony_ci /* Errata BTS #50, IHL must be 5 if no HW checksum */ 7188c2ecf20Sopenharmony_ci cmd |= 5 << TX_IHL_SHIFT; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci *command = cmd; 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic inline int 7258c2ecf20Sopenharmony_citxq_put_data_tso(struct net_device *dev, struct tx_queue *txq, 7268c2ecf20Sopenharmony_ci struct sk_buff *skb, char *data, int length, 7278c2ecf20Sopenharmony_ci bool last_tcp, bool is_last) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci int tx_index; 7308c2ecf20Sopenharmony_ci u32 cmd_sts; 7318c2ecf20Sopenharmony_ci struct tx_desc *desc; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci tx_index = txq->tx_curr_desc++; 7348c2ecf20Sopenharmony_ci if (txq->tx_curr_desc == txq->tx_ring_size) 7358c2ecf20Sopenharmony_ci txq->tx_curr_desc = 0; 7368c2ecf20Sopenharmony_ci desc = &txq->tx_desc_area[tx_index]; 7378c2ecf20Sopenharmony_ci txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_SINGLE; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci desc->l4i_chk = 0; 7408c2ecf20Sopenharmony_ci desc->byte_cnt = length; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (length <= 8 && (uintptr_t)data & 0x7) { 7438c2ecf20Sopenharmony_ci /* Copy unaligned small data fragment to TSO header data area */ 7448c2ecf20Sopenharmony_ci memcpy(txq->tso_hdrs + tx_index * TSO_HEADER_SIZE, 7458c2ecf20Sopenharmony_ci data, length); 7468c2ecf20Sopenharmony_ci desc->buf_ptr = txq->tso_hdrs_dma 7478c2ecf20Sopenharmony_ci + tx_index * TSO_HEADER_SIZE; 7488c2ecf20Sopenharmony_ci } else { 7498c2ecf20Sopenharmony_ci /* Alignment is okay, map buffer and hand off to hardware */ 7508c2ecf20Sopenharmony_ci txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_SINGLE; 7518c2ecf20Sopenharmony_ci desc->buf_ptr = dma_map_single(dev->dev.parent, data, 7528c2ecf20Sopenharmony_ci length, DMA_TO_DEVICE); 7538c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(dev->dev.parent, 7548c2ecf20Sopenharmony_ci desc->buf_ptr))) { 7558c2ecf20Sopenharmony_ci WARN(1, "dma_map_single failed!\n"); 7568c2ecf20Sopenharmony_ci return -ENOMEM; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci cmd_sts = BUFFER_OWNED_BY_DMA; 7618c2ecf20Sopenharmony_ci if (last_tcp) { 7628c2ecf20Sopenharmony_ci /* last descriptor in the TCP packet */ 7638c2ecf20Sopenharmony_ci cmd_sts |= ZERO_PADDING | TX_LAST_DESC; 7648c2ecf20Sopenharmony_ci /* last descriptor in SKB */ 7658c2ecf20Sopenharmony_ci if (is_last) 7668c2ecf20Sopenharmony_ci cmd_sts |= TX_ENABLE_INTERRUPT; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci desc->cmd_sts = cmd_sts; 7698c2ecf20Sopenharmony_ci return 0; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic inline void 7738c2ecf20Sopenharmony_citxq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length, 7748c2ecf20Sopenharmony_ci u32 *first_cmd_sts, bool first_desc) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 7778c2ecf20Sopenharmony_ci int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); 7788c2ecf20Sopenharmony_ci int tx_index; 7798c2ecf20Sopenharmony_ci struct tx_desc *desc; 7808c2ecf20Sopenharmony_ci int ret; 7818c2ecf20Sopenharmony_ci u32 cmd_csum = 0; 7828c2ecf20Sopenharmony_ci u16 l4i_chk = 0; 7838c2ecf20Sopenharmony_ci u32 cmd_sts; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci tx_index = txq->tx_curr_desc; 7868c2ecf20Sopenharmony_ci desc = &txq->tx_desc_area[tx_index]; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_csum, length); 7898c2ecf20Sopenharmony_ci if (ret) 7908c2ecf20Sopenharmony_ci WARN(1, "failed to prepare checksum!"); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* Should we set this? Can't use the value from skb_tx_csum() 7938c2ecf20Sopenharmony_ci * as it's not the correct initial L4 checksum to use. */ 7948c2ecf20Sopenharmony_ci desc->l4i_chk = 0; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci desc->byte_cnt = hdr_len; 7978c2ecf20Sopenharmony_ci desc->buf_ptr = txq->tso_hdrs_dma + 7988c2ecf20Sopenharmony_ci txq->tx_curr_desc * TSO_HEADER_SIZE; 7998c2ecf20Sopenharmony_ci cmd_sts = cmd_csum | BUFFER_OWNED_BY_DMA | TX_FIRST_DESC | 8008c2ecf20Sopenharmony_ci GEN_CRC; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* Defer updating the first command descriptor until all 8038c2ecf20Sopenharmony_ci * following descriptors have been written. 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci if (first_desc) 8068c2ecf20Sopenharmony_ci *first_cmd_sts = cmd_sts; 8078c2ecf20Sopenharmony_ci else 8088c2ecf20Sopenharmony_ci desc->cmd_sts = cmd_sts; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci txq->tx_curr_desc++; 8118c2ecf20Sopenharmony_ci if (txq->tx_curr_desc == txq->tx_ring_size) 8128c2ecf20Sopenharmony_ci txq->tx_curr_desc = 0; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic int txq_submit_tso(struct tx_queue *txq, struct sk_buff *skb, 8168c2ecf20Sopenharmony_ci struct net_device *dev) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 8198c2ecf20Sopenharmony_ci int hdr_len, total_len, data_left, ret; 8208c2ecf20Sopenharmony_ci int desc_count = 0; 8218c2ecf20Sopenharmony_ci struct tso_t tso; 8228c2ecf20Sopenharmony_ci struct tx_desc *first_tx_desc; 8238c2ecf20Sopenharmony_ci u32 first_cmd_sts = 0; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* Count needed descriptors */ 8268c2ecf20Sopenharmony_ci if ((txq->tx_desc_count + tso_count_descs(skb)) >= txq->tx_ring_size) { 8278c2ecf20Sopenharmony_ci netdev_dbg(dev, "not enough descriptors for TSO!\n"); 8288c2ecf20Sopenharmony_ci return -EBUSY; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci first_tx_desc = &txq->tx_desc_area[txq->tx_curr_desc]; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* Initialize the TSO handler, and prepare the first payload */ 8348c2ecf20Sopenharmony_ci hdr_len = tso_start(skb, &tso); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci total_len = skb->len - hdr_len; 8378c2ecf20Sopenharmony_ci while (total_len > 0) { 8388c2ecf20Sopenharmony_ci bool first_desc = (desc_count == 0); 8398c2ecf20Sopenharmony_ci char *hdr; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 8428c2ecf20Sopenharmony_ci total_len -= data_left; 8438c2ecf20Sopenharmony_ci desc_count++; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* prepare packet headers: MAC + IP + TCP */ 8468c2ecf20Sopenharmony_ci hdr = txq->tso_hdrs + txq->tx_curr_desc * TSO_HEADER_SIZE; 8478c2ecf20Sopenharmony_ci tso_build_hdr(skb, hdr, &tso, data_left, total_len == 0); 8488c2ecf20Sopenharmony_ci txq_put_hdr_tso(skb, txq, data_left, &first_cmd_sts, 8498c2ecf20Sopenharmony_ci first_desc); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci while (data_left > 0) { 8528c2ecf20Sopenharmony_ci int size; 8538c2ecf20Sopenharmony_ci desc_count++; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci size = min_t(int, tso.size, data_left); 8568c2ecf20Sopenharmony_ci ret = txq_put_data_tso(dev, txq, skb, tso.data, size, 8578c2ecf20Sopenharmony_ci size == data_left, 8588c2ecf20Sopenharmony_ci total_len == 0); 8598c2ecf20Sopenharmony_ci if (ret) 8608c2ecf20Sopenharmony_ci goto err_release; 8618c2ecf20Sopenharmony_ci data_left -= size; 8628c2ecf20Sopenharmony_ci tso_build_data(skb, &tso, size); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci __skb_queue_tail(&txq->tx_skb, skb); 8678c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* ensure all other descriptors are written before first cmd_sts */ 8708c2ecf20Sopenharmony_ci wmb(); 8718c2ecf20Sopenharmony_ci first_tx_desc->cmd_sts = first_cmd_sts; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* clear TX_END status */ 8748c2ecf20Sopenharmony_ci mp->work_tx_end &= ~(1 << txq->index); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* ensure all descriptors are written before poking hardware */ 8778c2ecf20Sopenharmony_ci wmb(); 8788c2ecf20Sopenharmony_ci txq_enable(txq); 8798c2ecf20Sopenharmony_ci txq->tx_desc_count += desc_count; 8808c2ecf20Sopenharmony_ci return 0; 8818c2ecf20Sopenharmony_cierr_release: 8828c2ecf20Sopenharmony_ci /* TODO: Release all used data descriptors; header descriptors must not 8838c2ecf20Sopenharmony_ci * be DMA-unmapped. 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci return ret; 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 8918c2ecf20Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 8928c2ecf20Sopenharmony_ci int frag; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci for (frag = 0; frag < nr_frags; frag++) { 8958c2ecf20Sopenharmony_ci skb_frag_t *this_frag; 8968c2ecf20Sopenharmony_ci int tx_index; 8978c2ecf20Sopenharmony_ci struct tx_desc *desc; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci this_frag = &skb_shinfo(skb)->frags[frag]; 9008c2ecf20Sopenharmony_ci tx_index = txq->tx_curr_desc++; 9018c2ecf20Sopenharmony_ci if (txq->tx_curr_desc == txq->tx_ring_size) 9028c2ecf20Sopenharmony_ci txq->tx_curr_desc = 0; 9038c2ecf20Sopenharmony_ci desc = &txq->tx_desc_area[tx_index]; 9048c2ecf20Sopenharmony_ci txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_PAGE; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* 9078c2ecf20Sopenharmony_ci * The last fragment will generate an interrupt 9088c2ecf20Sopenharmony_ci * which will free the skb on TX completion. 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_ci if (frag == nr_frags - 1) { 9118c2ecf20Sopenharmony_ci desc->cmd_sts = BUFFER_OWNED_BY_DMA | 9128c2ecf20Sopenharmony_ci ZERO_PADDING | TX_LAST_DESC | 9138c2ecf20Sopenharmony_ci TX_ENABLE_INTERRUPT; 9148c2ecf20Sopenharmony_ci } else { 9158c2ecf20Sopenharmony_ci desc->cmd_sts = BUFFER_OWNED_BY_DMA; 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci desc->l4i_chk = 0; 9198c2ecf20Sopenharmony_ci desc->byte_cnt = skb_frag_size(this_frag); 9208c2ecf20Sopenharmony_ci desc->buf_ptr = skb_frag_dma_map(mp->dev->dev.parent, 9218c2ecf20Sopenharmony_ci this_frag, 0, desc->byte_cnt, 9228c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb, 9278c2ecf20Sopenharmony_ci struct net_device *dev) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 9308c2ecf20Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 9318c2ecf20Sopenharmony_ci int tx_index; 9328c2ecf20Sopenharmony_ci struct tx_desc *desc; 9338c2ecf20Sopenharmony_ci u32 cmd_sts; 9348c2ecf20Sopenharmony_ci u16 l4i_chk; 9358c2ecf20Sopenharmony_ci int length, ret; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci cmd_sts = 0; 9388c2ecf20Sopenharmony_ci l4i_chk = 0; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) { 9418c2ecf20Sopenharmony_ci if (net_ratelimit()) 9428c2ecf20Sopenharmony_ci netdev_err(dev, "tx queue full?!\n"); 9438c2ecf20Sopenharmony_ci return -EBUSY; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci ret = skb_tx_csum(mp, skb, &l4i_chk, &cmd_sts, skb->len); 9478c2ecf20Sopenharmony_ci if (ret) 9488c2ecf20Sopenharmony_ci return ret; 9498c2ecf20Sopenharmony_ci cmd_sts |= TX_FIRST_DESC | GEN_CRC | BUFFER_OWNED_BY_DMA; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci tx_index = txq->tx_curr_desc++; 9528c2ecf20Sopenharmony_ci if (txq->tx_curr_desc == txq->tx_ring_size) 9538c2ecf20Sopenharmony_ci txq->tx_curr_desc = 0; 9548c2ecf20Sopenharmony_ci desc = &txq->tx_desc_area[tx_index]; 9558c2ecf20Sopenharmony_ci txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_SINGLE; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (nr_frags) { 9588c2ecf20Sopenharmony_ci txq_submit_frag_skb(txq, skb); 9598c2ecf20Sopenharmony_ci length = skb_headlen(skb); 9608c2ecf20Sopenharmony_ci } else { 9618c2ecf20Sopenharmony_ci cmd_sts |= ZERO_PADDING | TX_LAST_DESC | TX_ENABLE_INTERRUPT; 9628c2ecf20Sopenharmony_ci length = skb->len; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci desc->l4i_chk = l4i_chk; 9668c2ecf20Sopenharmony_ci desc->byte_cnt = length; 9678c2ecf20Sopenharmony_ci desc->buf_ptr = dma_map_single(mp->dev->dev.parent, skb->data, 9688c2ecf20Sopenharmony_ci length, DMA_TO_DEVICE); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci __skb_queue_tail(&txq->tx_skb, skb); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* ensure all other descriptors are written before first cmd_sts */ 9758c2ecf20Sopenharmony_ci wmb(); 9768c2ecf20Sopenharmony_ci desc->cmd_sts = cmd_sts; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* clear TX_END status */ 9798c2ecf20Sopenharmony_ci mp->work_tx_end &= ~(1 << txq->index); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* ensure all descriptors are written before poking hardware */ 9828c2ecf20Sopenharmony_ci wmb(); 9838c2ecf20Sopenharmony_ci txq_enable(txq); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci txq->tx_desc_count += nr_frags + 1; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci return 0; 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 9938c2ecf20Sopenharmony_ci int length, queue, ret; 9948c2ecf20Sopenharmony_ci struct tx_queue *txq; 9958c2ecf20Sopenharmony_ci struct netdev_queue *nq; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci queue = skb_get_queue_mapping(skb); 9988c2ecf20Sopenharmony_ci txq = mp->txq + queue; 9998c2ecf20Sopenharmony_ci nq = netdev_get_tx_queue(dev, queue); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { 10028c2ecf20Sopenharmony_ci netdev_printk(KERN_DEBUG, dev, 10038c2ecf20Sopenharmony_ci "failed to linearize skb with tiny unaligned fragment\n"); 10048c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci length = skb->len; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) 10108c2ecf20Sopenharmony_ci ret = txq_submit_tso(txq, skb, dev); 10118c2ecf20Sopenharmony_ci else 10128c2ecf20Sopenharmony_ci ret = txq_submit_skb(txq, skb, dev); 10138c2ecf20Sopenharmony_ci if (!ret) { 10148c2ecf20Sopenharmony_ci txq->tx_bytes += length; 10158c2ecf20Sopenharmony_ci txq->tx_packets++; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci if (txq->tx_desc_count >= txq->tx_stop_threshold) 10188c2ecf20Sopenharmony_ci netif_tx_stop_queue(nq); 10198c2ecf20Sopenharmony_ci } else { 10208c2ecf20Sopenharmony_ci txq->tx_dropped++; 10218c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci/* tx napi ******************************************************************/ 10298c2ecf20Sopenharmony_cistatic void txq_kick(struct tx_queue *txq) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 10328c2ecf20Sopenharmony_ci struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index); 10338c2ecf20Sopenharmony_ci u32 hw_desc_ptr; 10348c2ecf20Sopenharmony_ci u32 expected_ptr; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci __netif_tx_lock(nq, smp_processor_id()); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci if (rdlp(mp, TXQ_COMMAND) & (1 << txq->index)) 10398c2ecf20Sopenharmony_ci goto out; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci hw_desc_ptr = rdlp(mp, TXQ_CURRENT_DESC_PTR(txq->index)); 10428c2ecf20Sopenharmony_ci expected_ptr = (u32)txq->tx_desc_dma + 10438c2ecf20Sopenharmony_ci txq->tx_curr_desc * sizeof(struct tx_desc); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (hw_desc_ptr != expected_ptr) 10468c2ecf20Sopenharmony_ci txq_enable(txq); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ciout: 10498c2ecf20Sopenharmony_ci __netif_tx_unlock(nq); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci mp->work_tx_end &= ~(1 << txq->index); 10528c2ecf20Sopenharmony_ci} 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cistatic int txq_reclaim(struct tx_queue *txq, int budget, int force) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 10578c2ecf20Sopenharmony_ci struct netdev_queue *nq = netdev_get_tx_queue(mp->dev, txq->index); 10588c2ecf20Sopenharmony_ci int reclaimed; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci __netif_tx_lock_bh(nq); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci reclaimed = 0; 10638c2ecf20Sopenharmony_ci while (reclaimed < budget && txq->tx_desc_count > 0) { 10648c2ecf20Sopenharmony_ci int tx_index; 10658c2ecf20Sopenharmony_ci struct tx_desc *desc; 10668c2ecf20Sopenharmony_ci u32 cmd_sts; 10678c2ecf20Sopenharmony_ci char desc_dma_map; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci tx_index = txq->tx_used_desc; 10708c2ecf20Sopenharmony_ci desc = &txq->tx_desc_area[tx_index]; 10718c2ecf20Sopenharmony_ci desc_dma_map = txq->tx_desc_mapping[tx_index]; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci cmd_sts = desc->cmd_sts; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (cmd_sts & BUFFER_OWNED_BY_DMA) { 10768c2ecf20Sopenharmony_ci if (!force) 10778c2ecf20Sopenharmony_ci break; 10788c2ecf20Sopenharmony_ci desc->cmd_sts = cmd_sts & ~BUFFER_OWNED_BY_DMA; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci txq->tx_used_desc = tx_index + 1; 10828c2ecf20Sopenharmony_ci if (txq->tx_used_desc == txq->tx_ring_size) 10838c2ecf20Sopenharmony_ci txq->tx_used_desc = 0; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci reclaimed++; 10868c2ecf20Sopenharmony_ci txq->tx_desc_count--; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (!IS_TSO_HEADER(txq, desc->buf_ptr)) { 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci if (desc_dma_map == DESC_DMA_MAP_PAGE) 10918c2ecf20Sopenharmony_ci dma_unmap_page(mp->dev->dev.parent, 10928c2ecf20Sopenharmony_ci desc->buf_ptr, 10938c2ecf20Sopenharmony_ci desc->byte_cnt, 10948c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 10958c2ecf20Sopenharmony_ci else 10968c2ecf20Sopenharmony_ci dma_unmap_single(mp->dev->dev.parent, 10978c2ecf20Sopenharmony_ci desc->buf_ptr, 10988c2ecf20Sopenharmony_ci desc->byte_cnt, 10998c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (cmd_sts & TX_ENABLE_INTERRUPT) { 11038c2ecf20Sopenharmony_ci struct sk_buff *skb = __skb_dequeue(&txq->tx_skb); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci if (!WARN_ON(!skb)) 11068c2ecf20Sopenharmony_ci dev_consume_skb_any(skb); 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (cmd_sts & ERROR_SUMMARY) { 11108c2ecf20Sopenharmony_ci netdev_info(mp->dev, "tx error\n"); 11118c2ecf20Sopenharmony_ci mp->dev->stats.tx_errors++; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci } 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci __netif_tx_unlock_bh(nq); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (reclaimed < budget) 11198c2ecf20Sopenharmony_ci mp->work_tx &= ~(1 << txq->index); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci return reclaimed; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci/* tx rate control **********************************************************/ 11268c2ecf20Sopenharmony_ci/* 11278c2ecf20Sopenharmony_ci * Set total maximum TX rate (shared by all TX queues for this port) 11288c2ecf20Sopenharmony_ci * to 'rate' bits per second, with a maximum burst of 'burst' bytes. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_cistatic void tx_set_rate(struct mv643xx_eth_private *mp, int rate, int burst) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci int token_rate; 11338c2ecf20Sopenharmony_ci int mtu; 11348c2ecf20Sopenharmony_ci int bucket_size; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci token_rate = ((rate / 1000) * 64) / (mp->t_clk / 1000); 11378c2ecf20Sopenharmony_ci if (token_rate > 1023) 11388c2ecf20Sopenharmony_ci token_rate = 1023; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci mtu = (mp->dev->mtu + 255) >> 8; 11418c2ecf20Sopenharmony_ci if (mtu > 63) 11428c2ecf20Sopenharmony_ci mtu = 63; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci bucket_size = (burst + 255) >> 8; 11458c2ecf20Sopenharmony_ci if (bucket_size > 65535) 11468c2ecf20Sopenharmony_ci bucket_size = 65535; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci switch (mp->shared->tx_bw_control) { 11498c2ecf20Sopenharmony_ci case TX_BW_CONTROL_OLD_LAYOUT: 11508c2ecf20Sopenharmony_ci wrlp(mp, TX_BW_RATE, token_rate); 11518c2ecf20Sopenharmony_ci wrlp(mp, TX_BW_MTU, mtu); 11528c2ecf20Sopenharmony_ci wrlp(mp, TX_BW_BURST, bucket_size); 11538c2ecf20Sopenharmony_ci break; 11548c2ecf20Sopenharmony_ci case TX_BW_CONTROL_NEW_LAYOUT: 11558c2ecf20Sopenharmony_ci wrlp(mp, TX_BW_RATE_MOVED, token_rate); 11568c2ecf20Sopenharmony_ci wrlp(mp, TX_BW_MTU_MOVED, mtu); 11578c2ecf20Sopenharmony_ci wrlp(mp, TX_BW_BURST_MOVED, bucket_size); 11588c2ecf20Sopenharmony_ci break; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic void txq_set_rate(struct tx_queue *txq, int rate, int burst) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 11658c2ecf20Sopenharmony_ci int token_rate; 11668c2ecf20Sopenharmony_ci int bucket_size; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci token_rate = ((rate / 1000) * 64) / (mp->t_clk / 1000); 11698c2ecf20Sopenharmony_ci if (token_rate > 1023) 11708c2ecf20Sopenharmony_ci token_rate = 1023; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci bucket_size = (burst + 255) >> 8; 11738c2ecf20Sopenharmony_ci if (bucket_size > 65535) 11748c2ecf20Sopenharmony_ci bucket_size = 65535; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci wrlp(mp, TXQ_BW_TOKENS(txq->index), token_rate << 14); 11778c2ecf20Sopenharmony_ci wrlp(mp, TXQ_BW_CONF(txq->index), (bucket_size << 10) | token_rate); 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic void txq_set_fixed_prio_mode(struct tx_queue *txq) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 11838c2ecf20Sopenharmony_ci int off; 11848c2ecf20Sopenharmony_ci u32 val; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* 11878c2ecf20Sopenharmony_ci * Turn on fixed priority mode. 11888c2ecf20Sopenharmony_ci */ 11898c2ecf20Sopenharmony_ci off = 0; 11908c2ecf20Sopenharmony_ci switch (mp->shared->tx_bw_control) { 11918c2ecf20Sopenharmony_ci case TX_BW_CONTROL_OLD_LAYOUT: 11928c2ecf20Sopenharmony_ci off = TXQ_FIX_PRIO_CONF; 11938c2ecf20Sopenharmony_ci break; 11948c2ecf20Sopenharmony_ci case TX_BW_CONTROL_NEW_LAYOUT: 11958c2ecf20Sopenharmony_ci off = TXQ_FIX_PRIO_CONF_MOVED; 11968c2ecf20Sopenharmony_ci break; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (off) { 12008c2ecf20Sopenharmony_ci val = rdlp(mp, off); 12018c2ecf20Sopenharmony_ci val |= 1 << txq->index; 12028c2ecf20Sopenharmony_ci wrlp(mp, off, val); 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci/* mii management interface *************************************************/ 12088c2ecf20Sopenharmony_cistatic void mv643xx_eth_adjust_link(struct net_device *dev) 12098c2ecf20Sopenharmony_ci{ 12108c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 12118c2ecf20Sopenharmony_ci u32 pscr = rdlp(mp, PORT_SERIAL_CONTROL); 12128c2ecf20Sopenharmony_ci u32 autoneg_disable = FORCE_LINK_PASS | 12138c2ecf20Sopenharmony_ci DISABLE_AUTO_NEG_SPEED_GMII | 12148c2ecf20Sopenharmony_ci DISABLE_AUTO_NEG_FOR_FLOW_CTRL | 12158c2ecf20Sopenharmony_ci DISABLE_AUTO_NEG_FOR_DUPLEX; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (dev->phydev->autoneg == AUTONEG_ENABLE) { 12188c2ecf20Sopenharmony_ci /* enable auto negotiation */ 12198c2ecf20Sopenharmony_ci pscr &= ~autoneg_disable; 12208c2ecf20Sopenharmony_ci goto out_write; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci pscr |= autoneg_disable; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (dev->phydev->speed == SPEED_1000) { 12268c2ecf20Sopenharmony_ci /* force gigabit, half duplex not supported */ 12278c2ecf20Sopenharmony_ci pscr |= SET_GMII_SPEED_TO_1000; 12288c2ecf20Sopenharmony_ci pscr |= SET_FULL_DUPLEX_MODE; 12298c2ecf20Sopenharmony_ci goto out_write; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci pscr &= ~SET_GMII_SPEED_TO_1000; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (dev->phydev->speed == SPEED_100) 12358c2ecf20Sopenharmony_ci pscr |= SET_MII_SPEED_TO_100; 12368c2ecf20Sopenharmony_ci else 12378c2ecf20Sopenharmony_ci pscr &= ~SET_MII_SPEED_TO_100; 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (dev->phydev->duplex == DUPLEX_FULL) 12408c2ecf20Sopenharmony_ci pscr |= SET_FULL_DUPLEX_MODE; 12418c2ecf20Sopenharmony_ci else 12428c2ecf20Sopenharmony_ci pscr &= ~SET_FULL_DUPLEX_MODE; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ciout_write: 12458c2ecf20Sopenharmony_ci wrlp(mp, PORT_SERIAL_CONTROL, pscr); 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci/* statistics ***************************************************************/ 12498c2ecf20Sopenharmony_cistatic struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 12528c2ecf20Sopenharmony_ci struct net_device_stats *stats = &dev->stats; 12538c2ecf20Sopenharmony_ci unsigned long tx_packets = 0; 12548c2ecf20Sopenharmony_ci unsigned long tx_bytes = 0; 12558c2ecf20Sopenharmony_ci unsigned long tx_dropped = 0; 12568c2ecf20Sopenharmony_ci int i; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci for (i = 0; i < mp->txq_count; i++) { 12598c2ecf20Sopenharmony_ci struct tx_queue *txq = mp->txq + i; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci tx_packets += txq->tx_packets; 12628c2ecf20Sopenharmony_ci tx_bytes += txq->tx_bytes; 12638c2ecf20Sopenharmony_ci tx_dropped += txq->tx_dropped; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci stats->tx_packets = tx_packets; 12678c2ecf20Sopenharmony_ci stats->tx_bytes = tx_bytes; 12688c2ecf20Sopenharmony_ci stats->tx_dropped = tx_dropped; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci return stats; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic inline u32 mib_read(struct mv643xx_eth_private *mp, int offset) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic void mib_counters_clear(struct mv643xx_eth_private *mp) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci int i; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci for (i = 0; i < 0x80; i += 4) 12838c2ecf20Sopenharmony_ci mib_read(mp, i); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* Clear non MIB hw counters also */ 12868c2ecf20Sopenharmony_ci rdlp(mp, RX_DISCARD_FRAME_CNT); 12878c2ecf20Sopenharmony_ci rdlp(mp, RX_OVERRUN_FRAME_CNT); 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic void mib_counters_update(struct mv643xx_eth_private *mp) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci struct mib_counters *p = &mp->mib_counters; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci spin_lock_bh(&mp->mib_counters_lock); 12958c2ecf20Sopenharmony_ci p->good_octets_received += mib_read(mp, 0x00); 12968c2ecf20Sopenharmony_ci p->bad_octets_received += mib_read(mp, 0x08); 12978c2ecf20Sopenharmony_ci p->internal_mac_transmit_err += mib_read(mp, 0x0c); 12988c2ecf20Sopenharmony_ci p->good_frames_received += mib_read(mp, 0x10); 12998c2ecf20Sopenharmony_ci p->bad_frames_received += mib_read(mp, 0x14); 13008c2ecf20Sopenharmony_ci p->broadcast_frames_received += mib_read(mp, 0x18); 13018c2ecf20Sopenharmony_ci p->multicast_frames_received += mib_read(mp, 0x1c); 13028c2ecf20Sopenharmony_ci p->frames_64_octets += mib_read(mp, 0x20); 13038c2ecf20Sopenharmony_ci p->frames_65_to_127_octets += mib_read(mp, 0x24); 13048c2ecf20Sopenharmony_ci p->frames_128_to_255_octets += mib_read(mp, 0x28); 13058c2ecf20Sopenharmony_ci p->frames_256_to_511_octets += mib_read(mp, 0x2c); 13068c2ecf20Sopenharmony_ci p->frames_512_to_1023_octets += mib_read(mp, 0x30); 13078c2ecf20Sopenharmony_ci p->frames_1024_to_max_octets += mib_read(mp, 0x34); 13088c2ecf20Sopenharmony_ci p->good_octets_sent += mib_read(mp, 0x38); 13098c2ecf20Sopenharmony_ci p->good_frames_sent += mib_read(mp, 0x40); 13108c2ecf20Sopenharmony_ci p->excessive_collision += mib_read(mp, 0x44); 13118c2ecf20Sopenharmony_ci p->multicast_frames_sent += mib_read(mp, 0x48); 13128c2ecf20Sopenharmony_ci p->broadcast_frames_sent += mib_read(mp, 0x4c); 13138c2ecf20Sopenharmony_ci p->unrec_mac_control_received += mib_read(mp, 0x50); 13148c2ecf20Sopenharmony_ci p->fc_sent += mib_read(mp, 0x54); 13158c2ecf20Sopenharmony_ci p->good_fc_received += mib_read(mp, 0x58); 13168c2ecf20Sopenharmony_ci p->bad_fc_received += mib_read(mp, 0x5c); 13178c2ecf20Sopenharmony_ci p->undersize_received += mib_read(mp, 0x60); 13188c2ecf20Sopenharmony_ci p->fragments_received += mib_read(mp, 0x64); 13198c2ecf20Sopenharmony_ci p->oversize_received += mib_read(mp, 0x68); 13208c2ecf20Sopenharmony_ci p->jabber_received += mib_read(mp, 0x6c); 13218c2ecf20Sopenharmony_ci p->mac_receive_error += mib_read(mp, 0x70); 13228c2ecf20Sopenharmony_ci p->bad_crc_event += mib_read(mp, 0x74); 13238c2ecf20Sopenharmony_ci p->collision += mib_read(mp, 0x78); 13248c2ecf20Sopenharmony_ci p->late_collision += mib_read(mp, 0x7c); 13258c2ecf20Sopenharmony_ci /* Non MIB hardware counters */ 13268c2ecf20Sopenharmony_ci p->rx_discard += rdlp(mp, RX_DISCARD_FRAME_CNT); 13278c2ecf20Sopenharmony_ci p->rx_overrun += rdlp(mp, RX_OVERRUN_FRAME_CNT); 13288c2ecf20Sopenharmony_ci spin_unlock_bh(&mp->mib_counters_lock); 13298c2ecf20Sopenharmony_ci} 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_cistatic void mib_counters_timer_wrapper(struct timer_list *t) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = from_timer(mp, t, mib_counters_timer); 13348c2ecf20Sopenharmony_ci mib_counters_update(mp); 13358c2ecf20Sopenharmony_ci mod_timer(&mp->mib_counters_timer, jiffies + 30 * HZ); 13368c2ecf20Sopenharmony_ci} 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci/* interrupt coalescing *****************************************************/ 13408c2ecf20Sopenharmony_ci/* 13418c2ecf20Sopenharmony_ci * Hardware coalescing parameters are set in units of 64 t_clk 13428c2ecf20Sopenharmony_ci * cycles. I.e.: 13438c2ecf20Sopenharmony_ci * 13448c2ecf20Sopenharmony_ci * coal_delay_in_usec = 64000000 * register_value / t_clk_rate 13458c2ecf20Sopenharmony_ci * 13468c2ecf20Sopenharmony_ci * register_value = coal_delay_in_usec * t_clk_rate / 64000000 13478c2ecf20Sopenharmony_ci * 13488c2ecf20Sopenharmony_ci * In the ->set*() methods, we round the computed register value 13498c2ecf20Sopenharmony_ci * to the nearest integer. 13508c2ecf20Sopenharmony_ci */ 13518c2ecf20Sopenharmony_cistatic unsigned int get_rx_coal(struct mv643xx_eth_private *mp) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci u32 val = rdlp(mp, SDMA_CONFIG); 13548c2ecf20Sopenharmony_ci u64 temp; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if (mp->shared->extended_rx_coal_limit) 13578c2ecf20Sopenharmony_ci temp = ((val & 0x02000000) >> 10) | ((val & 0x003fff80) >> 7); 13588c2ecf20Sopenharmony_ci else 13598c2ecf20Sopenharmony_ci temp = (val & 0x003fff00) >> 8; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci temp *= 64000000; 13628c2ecf20Sopenharmony_ci temp += mp->t_clk / 2; 13638c2ecf20Sopenharmony_ci do_div(temp, mp->t_clk); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci return (unsigned int)temp; 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int usec) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci u64 temp; 13718c2ecf20Sopenharmony_ci u32 val; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci temp = (u64)usec * mp->t_clk; 13748c2ecf20Sopenharmony_ci temp += 31999999; 13758c2ecf20Sopenharmony_ci do_div(temp, 64000000); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci val = rdlp(mp, SDMA_CONFIG); 13788c2ecf20Sopenharmony_ci if (mp->shared->extended_rx_coal_limit) { 13798c2ecf20Sopenharmony_ci if (temp > 0xffff) 13808c2ecf20Sopenharmony_ci temp = 0xffff; 13818c2ecf20Sopenharmony_ci val &= ~0x023fff80; 13828c2ecf20Sopenharmony_ci val |= (temp & 0x8000) << 10; 13838c2ecf20Sopenharmony_ci val |= (temp & 0x7fff) << 7; 13848c2ecf20Sopenharmony_ci } else { 13858c2ecf20Sopenharmony_ci if (temp > 0x3fff) 13868c2ecf20Sopenharmony_ci temp = 0x3fff; 13878c2ecf20Sopenharmony_ci val &= ~0x003fff00; 13888c2ecf20Sopenharmony_ci val |= (temp & 0x3fff) << 8; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci wrlp(mp, SDMA_CONFIG, val); 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistatic unsigned int get_tx_coal(struct mv643xx_eth_private *mp) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci u64 temp; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci temp = (rdlp(mp, TX_FIFO_URGENT_THRESHOLD) & 0x3fff0) >> 4; 13988c2ecf20Sopenharmony_ci temp *= 64000000; 13998c2ecf20Sopenharmony_ci temp += mp->t_clk / 2; 14008c2ecf20Sopenharmony_ci do_div(temp, mp->t_clk); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci return (unsigned int)temp; 14038c2ecf20Sopenharmony_ci} 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int usec) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci u64 temp; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci temp = (u64)usec * mp->t_clk; 14108c2ecf20Sopenharmony_ci temp += 31999999; 14118c2ecf20Sopenharmony_ci do_div(temp, 64000000); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci if (temp > 0x3fff) 14148c2ecf20Sopenharmony_ci temp = 0x3fff; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci wrlp(mp, TX_FIFO_URGENT_THRESHOLD, temp << 4); 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci/* ethtool ******************************************************************/ 14218c2ecf20Sopenharmony_cistruct mv643xx_eth_stats { 14228c2ecf20Sopenharmony_ci char stat_string[ETH_GSTRING_LEN]; 14238c2ecf20Sopenharmony_ci int sizeof_stat; 14248c2ecf20Sopenharmony_ci int netdev_off; 14258c2ecf20Sopenharmony_ci int mp_off; 14268c2ecf20Sopenharmony_ci}; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci#define SSTAT(m) \ 14298c2ecf20Sopenharmony_ci { #m, sizeof_field(struct net_device_stats, m), \ 14308c2ecf20Sopenharmony_ci offsetof(struct net_device, stats.m), -1 } 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci#define MIBSTAT(m) \ 14338c2ecf20Sopenharmony_ci { #m, sizeof_field(struct mib_counters, m), \ 14348c2ecf20Sopenharmony_ci -1, offsetof(struct mv643xx_eth_private, mib_counters.m) } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic const struct mv643xx_eth_stats mv643xx_eth_stats[] = { 14378c2ecf20Sopenharmony_ci SSTAT(rx_packets), 14388c2ecf20Sopenharmony_ci SSTAT(tx_packets), 14398c2ecf20Sopenharmony_ci SSTAT(rx_bytes), 14408c2ecf20Sopenharmony_ci SSTAT(tx_bytes), 14418c2ecf20Sopenharmony_ci SSTAT(rx_errors), 14428c2ecf20Sopenharmony_ci SSTAT(tx_errors), 14438c2ecf20Sopenharmony_ci SSTAT(rx_dropped), 14448c2ecf20Sopenharmony_ci SSTAT(tx_dropped), 14458c2ecf20Sopenharmony_ci MIBSTAT(good_octets_received), 14468c2ecf20Sopenharmony_ci MIBSTAT(bad_octets_received), 14478c2ecf20Sopenharmony_ci MIBSTAT(internal_mac_transmit_err), 14488c2ecf20Sopenharmony_ci MIBSTAT(good_frames_received), 14498c2ecf20Sopenharmony_ci MIBSTAT(bad_frames_received), 14508c2ecf20Sopenharmony_ci MIBSTAT(broadcast_frames_received), 14518c2ecf20Sopenharmony_ci MIBSTAT(multicast_frames_received), 14528c2ecf20Sopenharmony_ci MIBSTAT(frames_64_octets), 14538c2ecf20Sopenharmony_ci MIBSTAT(frames_65_to_127_octets), 14548c2ecf20Sopenharmony_ci MIBSTAT(frames_128_to_255_octets), 14558c2ecf20Sopenharmony_ci MIBSTAT(frames_256_to_511_octets), 14568c2ecf20Sopenharmony_ci MIBSTAT(frames_512_to_1023_octets), 14578c2ecf20Sopenharmony_ci MIBSTAT(frames_1024_to_max_octets), 14588c2ecf20Sopenharmony_ci MIBSTAT(good_octets_sent), 14598c2ecf20Sopenharmony_ci MIBSTAT(good_frames_sent), 14608c2ecf20Sopenharmony_ci MIBSTAT(excessive_collision), 14618c2ecf20Sopenharmony_ci MIBSTAT(multicast_frames_sent), 14628c2ecf20Sopenharmony_ci MIBSTAT(broadcast_frames_sent), 14638c2ecf20Sopenharmony_ci MIBSTAT(unrec_mac_control_received), 14648c2ecf20Sopenharmony_ci MIBSTAT(fc_sent), 14658c2ecf20Sopenharmony_ci MIBSTAT(good_fc_received), 14668c2ecf20Sopenharmony_ci MIBSTAT(bad_fc_received), 14678c2ecf20Sopenharmony_ci MIBSTAT(undersize_received), 14688c2ecf20Sopenharmony_ci MIBSTAT(fragments_received), 14698c2ecf20Sopenharmony_ci MIBSTAT(oversize_received), 14708c2ecf20Sopenharmony_ci MIBSTAT(jabber_received), 14718c2ecf20Sopenharmony_ci MIBSTAT(mac_receive_error), 14728c2ecf20Sopenharmony_ci MIBSTAT(bad_crc_event), 14738c2ecf20Sopenharmony_ci MIBSTAT(collision), 14748c2ecf20Sopenharmony_ci MIBSTAT(late_collision), 14758c2ecf20Sopenharmony_ci MIBSTAT(rx_discard), 14768c2ecf20Sopenharmony_ci MIBSTAT(rx_overrun), 14778c2ecf20Sopenharmony_ci}; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_cistatic int 14808c2ecf20Sopenharmony_cimv643xx_eth_get_link_ksettings_phy(struct mv643xx_eth_private *mp, 14818c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci struct net_device *dev = mp->dev; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci phy_ethtool_ksettings_get(dev->phydev, cmd); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci /* 14888c2ecf20Sopenharmony_ci * The MAC does not support 1000baseT_Half. 14898c2ecf20Sopenharmony_ci */ 14908c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 14918c2ecf20Sopenharmony_ci cmd->link_modes.supported); 14928c2ecf20Sopenharmony_ci linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 14938c2ecf20Sopenharmony_ci cmd->link_modes.advertising); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return 0; 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic int 14998c2ecf20Sopenharmony_cimv643xx_eth_get_link_ksettings_phyless(struct mv643xx_eth_private *mp, 15008c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 15018c2ecf20Sopenharmony_ci{ 15028c2ecf20Sopenharmony_ci u32 port_status; 15038c2ecf20Sopenharmony_ci u32 supported, advertising; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci port_status = rdlp(mp, PORT_STATUS); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci supported = SUPPORTED_MII; 15088c2ecf20Sopenharmony_ci advertising = ADVERTISED_MII; 15098c2ecf20Sopenharmony_ci switch (port_status & PORT_SPEED_MASK) { 15108c2ecf20Sopenharmony_ci case PORT_SPEED_10: 15118c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10; 15128c2ecf20Sopenharmony_ci break; 15138c2ecf20Sopenharmony_ci case PORT_SPEED_100: 15148c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_100; 15158c2ecf20Sopenharmony_ci break; 15168c2ecf20Sopenharmony_ci case PORT_SPEED_1000: 15178c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_1000; 15188c2ecf20Sopenharmony_ci break; 15198c2ecf20Sopenharmony_ci default: 15208c2ecf20Sopenharmony_ci cmd->base.speed = -1; 15218c2ecf20Sopenharmony_ci break; 15228c2ecf20Sopenharmony_ci } 15238c2ecf20Sopenharmony_ci cmd->base.duplex = (port_status & FULL_DUPLEX) ? 15248c2ecf20Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 15258c2ecf20Sopenharmony_ci cmd->base.port = PORT_MII; 15268c2ecf20Sopenharmony_ci cmd->base.phy_address = 0; 15278c2ecf20Sopenharmony_ci cmd->base.autoneg = AUTONEG_DISABLE; 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 15308c2ecf20Sopenharmony_ci supported); 15318c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 15328c2ecf20Sopenharmony_ci advertising); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci return 0; 15358c2ecf20Sopenharmony_ci} 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_cistatic void 15388c2ecf20Sopenharmony_cimv643xx_eth_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci wol->supported = 0; 15418c2ecf20Sopenharmony_ci wol->wolopts = 0; 15428c2ecf20Sopenharmony_ci if (dev->phydev) 15438c2ecf20Sopenharmony_ci phy_ethtool_get_wol(dev->phydev, wol); 15448c2ecf20Sopenharmony_ci} 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_cistatic int 15478c2ecf20Sopenharmony_cimv643xx_eth_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 15488c2ecf20Sopenharmony_ci{ 15498c2ecf20Sopenharmony_ci int err; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci if (!dev->phydev) 15528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci err = phy_ethtool_set_wol(dev->phydev, wol); 15558c2ecf20Sopenharmony_ci /* Given that mv643xx_eth works without the marvell-specific PHY driver, 15568c2ecf20Sopenharmony_ci * this debugging hint is useful to have. 15578c2ecf20Sopenharmony_ci */ 15588c2ecf20Sopenharmony_ci if (err == -EOPNOTSUPP) 15598c2ecf20Sopenharmony_ci netdev_info(dev, "The PHY does not support set_wol, was CONFIG_MARVELL_PHY enabled?\n"); 15608c2ecf20Sopenharmony_ci return err; 15618c2ecf20Sopenharmony_ci} 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_cistatic int 15648c2ecf20Sopenharmony_cimv643xx_eth_get_link_ksettings(struct net_device *dev, 15658c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (dev->phydev) 15708c2ecf20Sopenharmony_ci return mv643xx_eth_get_link_ksettings_phy(mp, cmd); 15718c2ecf20Sopenharmony_ci else 15728c2ecf20Sopenharmony_ci return mv643xx_eth_get_link_ksettings_phyless(mp, cmd); 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_cistatic int 15768c2ecf20Sopenharmony_cimv643xx_eth_set_link_ksettings(struct net_device *dev, 15778c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 15788c2ecf20Sopenharmony_ci{ 15798c2ecf20Sopenharmony_ci struct ethtool_link_ksettings c = *cmd; 15808c2ecf20Sopenharmony_ci u32 advertising; 15818c2ecf20Sopenharmony_ci int ret; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (!dev->phydev) 15848c2ecf20Sopenharmony_ci return -EINVAL; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci /* 15878c2ecf20Sopenharmony_ci * The MAC does not support 1000baseT_Half. 15888c2ecf20Sopenharmony_ci */ 15898c2ecf20Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&advertising, 15908c2ecf20Sopenharmony_ci c.link_modes.advertising); 15918c2ecf20Sopenharmony_ci advertising &= ~ADVERTISED_1000baseT_Half; 15928c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(c.link_modes.advertising, 15938c2ecf20Sopenharmony_ci advertising); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci ret = phy_ethtool_ksettings_set(dev->phydev, &c); 15968c2ecf20Sopenharmony_ci if (!ret) 15978c2ecf20Sopenharmony_ci mv643xx_eth_adjust_link(dev); 15988c2ecf20Sopenharmony_ci return ret; 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cistatic void mv643xx_eth_get_drvinfo(struct net_device *dev, 16028c2ecf20Sopenharmony_ci struct ethtool_drvinfo *drvinfo) 16038c2ecf20Sopenharmony_ci{ 16048c2ecf20Sopenharmony_ci strlcpy(drvinfo->driver, mv643xx_eth_driver_name, 16058c2ecf20Sopenharmony_ci sizeof(drvinfo->driver)); 16068c2ecf20Sopenharmony_ci strlcpy(drvinfo->version, mv643xx_eth_driver_version, 16078c2ecf20Sopenharmony_ci sizeof(drvinfo->version)); 16088c2ecf20Sopenharmony_ci strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); 16098c2ecf20Sopenharmony_ci strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); 16108c2ecf20Sopenharmony_ci} 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_cistatic int 16138c2ecf20Sopenharmony_cimv643xx_eth_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) 16148c2ecf20Sopenharmony_ci{ 16158c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci ec->rx_coalesce_usecs = get_rx_coal(mp); 16188c2ecf20Sopenharmony_ci ec->tx_coalesce_usecs = get_tx_coal(mp); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci return 0; 16218c2ecf20Sopenharmony_ci} 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_cistatic int 16248c2ecf20Sopenharmony_cimv643xx_eth_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci set_rx_coal(mp, ec->rx_coalesce_usecs); 16298c2ecf20Sopenharmony_ci set_tx_coal(mp, ec->tx_coalesce_usecs); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci return 0; 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_cistatic void 16358c2ecf20Sopenharmony_cimv643xx_eth_get_ringparam(struct net_device *dev, struct ethtool_ringparam *er) 16368c2ecf20Sopenharmony_ci{ 16378c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci er->rx_max_pending = 4096; 16408c2ecf20Sopenharmony_ci er->tx_max_pending = 4096; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci er->rx_pending = mp->rx_ring_size; 16438c2ecf20Sopenharmony_ci er->tx_pending = mp->tx_ring_size; 16448c2ecf20Sopenharmony_ci} 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_cistatic int 16478c2ecf20Sopenharmony_cimv643xx_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er) 16488c2ecf20Sopenharmony_ci{ 16498c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (er->rx_mini_pending || er->rx_jumbo_pending) 16528c2ecf20Sopenharmony_ci return -EINVAL; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci mp->rx_ring_size = er->rx_pending < 4096 ? er->rx_pending : 4096; 16558c2ecf20Sopenharmony_ci mp->tx_ring_size = clamp_t(unsigned int, er->tx_pending, 16568c2ecf20Sopenharmony_ci MV643XX_MAX_SKB_DESCS * 2, 4096); 16578c2ecf20Sopenharmony_ci if (mp->tx_ring_size != er->tx_pending) 16588c2ecf20Sopenharmony_ci netdev_warn(dev, "TX queue size set to %u (requested %u)\n", 16598c2ecf20Sopenharmony_ci mp->tx_ring_size, er->tx_pending); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci if (netif_running(dev)) { 16628c2ecf20Sopenharmony_ci mv643xx_eth_stop(dev); 16638c2ecf20Sopenharmony_ci if (mv643xx_eth_open(dev)) { 16648c2ecf20Sopenharmony_ci netdev_err(dev, 16658c2ecf20Sopenharmony_ci "fatal error on re-opening device after ring param change\n"); 16668c2ecf20Sopenharmony_ci return -ENOMEM; 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci } 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci return 0; 16718c2ecf20Sopenharmony_ci} 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_cistatic int 16758c2ecf20Sopenharmony_cimv643xx_eth_set_features(struct net_device *dev, netdev_features_t features) 16768c2ecf20Sopenharmony_ci{ 16778c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 16788c2ecf20Sopenharmony_ci bool rx_csum = features & NETIF_F_RXCSUM; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci wrlp(mp, PORT_CONFIG, rx_csum ? 0x02000000 : 0x00000000); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci return 0; 16838c2ecf20Sopenharmony_ci} 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_cistatic void mv643xx_eth_get_strings(struct net_device *dev, 16868c2ecf20Sopenharmony_ci uint32_t stringset, uint8_t *data) 16878c2ecf20Sopenharmony_ci{ 16888c2ecf20Sopenharmony_ci int i; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci if (stringset == ETH_SS_STATS) { 16918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { 16928c2ecf20Sopenharmony_ci memcpy(data + i * ETH_GSTRING_LEN, 16938c2ecf20Sopenharmony_ci mv643xx_eth_stats[i].stat_string, 16948c2ecf20Sopenharmony_ci ETH_GSTRING_LEN); 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci} 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_cistatic void mv643xx_eth_get_ethtool_stats(struct net_device *dev, 17008c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 17018c2ecf20Sopenharmony_ci uint64_t *data) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 17048c2ecf20Sopenharmony_ci int i; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci mv643xx_eth_get_stats(dev); 17078c2ecf20Sopenharmony_ci mib_counters_update(mp); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { 17108c2ecf20Sopenharmony_ci const struct mv643xx_eth_stats *stat; 17118c2ecf20Sopenharmony_ci void *p; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci stat = mv643xx_eth_stats + i; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if (stat->netdev_off >= 0) 17168c2ecf20Sopenharmony_ci p = ((void *)mp->dev) + stat->netdev_off; 17178c2ecf20Sopenharmony_ci else 17188c2ecf20Sopenharmony_ci p = ((void *)mp) + stat->mp_off; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci data[i] = (stat->sizeof_stat == 8) ? 17218c2ecf20Sopenharmony_ci *(uint64_t *)p : *(uint32_t *)p; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistatic int mv643xx_eth_get_sset_count(struct net_device *dev, int sset) 17268c2ecf20Sopenharmony_ci{ 17278c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) 17288c2ecf20Sopenharmony_ci return ARRAY_SIZE(mv643xx_eth_stats); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic const struct ethtool_ops mv643xx_eth_ethtool_ops = { 17348c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS, 17358c2ecf20Sopenharmony_ci .get_drvinfo = mv643xx_eth_get_drvinfo, 17368c2ecf20Sopenharmony_ci .nway_reset = phy_ethtool_nway_reset, 17378c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 17388c2ecf20Sopenharmony_ci .get_coalesce = mv643xx_eth_get_coalesce, 17398c2ecf20Sopenharmony_ci .set_coalesce = mv643xx_eth_set_coalesce, 17408c2ecf20Sopenharmony_ci .get_ringparam = mv643xx_eth_get_ringparam, 17418c2ecf20Sopenharmony_ci .set_ringparam = mv643xx_eth_set_ringparam, 17428c2ecf20Sopenharmony_ci .get_strings = mv643xx_eth_get_strings, 17438c2ecf20Sopenharmony_ci .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, 17448c2ecf20Sopenharmony_ci .get_sset_count = mv643xx_eth_get_sset_count, 17458c2ecf20Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 17468c2ecf20Sopenharmony_ci .get_wol = mv643xx_eth_get_wol, 17478c2ecf20Sopenharmony_ci .set_wol = mv643xx_eth_set_wol, 17488c2ecf20Sopenharmony_ci .get_link_ksettings = mv643xx_eth_get_link_ksettings, 17498c2ecf20Sopenharmony_ci .set_link_ksettings = mv643xx_eth_set_link_ksettings, 17508c2ecf20Sopenharmony_ci}; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci/* address handling *********************************************************/ 17548c2ecf20Sopenharmony_cistatic void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) 17558c2ecf20Sopenharmony_ci{ 17568c2ecf20Sopenharmony_ci unsigned int mac_h = rdlp(mp, MAC_ADDR_HIGH); 17578c2ecf20Sopenharmony_ci unsigned int mac_l = rdlp(mp, MAC_ADDR_LOW); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci addr[0] = (mac_h >> 24) & 0xff; 17608c2ecf20Sopenharmony_ci addr[1] = (mac_h >> 16) & 0xff; 17618c2ecf20Sopenharmony_ci addr[2] = (mac_h >> 8) & 0xff; 17628c2ecf20Sopenharmony_ci addr[3] = mac_h & 0xff; 17638c2ecf20Sopenharmony_ci addr[4] = (mac_l >> 8) & 0xff; 17648c2ecf20Sopenharmony_ci addr[5] = mac_l & 0xff; 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_cistatic void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr) 17688c2ecf20Sopenharmony_ci{ 17698c2ecf20Sopenharmony_ci wrlp(mp, MAC_ADDR_HIGH, 17708c2ecf20Sopenharmony_ci (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]); 17718c2ecf20Sopenharmony_ci wrlp(mp, MAC_ADDR_LOW, (addr[4] << 8) | addr[5]); 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistatic u32 uc_addr_filter_mask(struct net_device *dev) 17758c2ecf20Sopenharmony_ci{ 17768c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 17778c2ecf20Sopenharmony_ci u32 nibbles; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) 17808c2ecf20Sopenharmony_ci return 0; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci nibbles = 1 << (dev->dev_addr[5] & 0x0f); 17838c2ecf20Sopenharmony_ci netdev_for_each_uc_addr(ha, dev) { 17848c2ecf20Sopenharmony_ci if (memcmp(dev->dev_addr, ha->addr, 5)) 17858c2ecf20Sopenharmony_ci return 0; 17868c2ecf20Sopenharmony_ci if ((dev->dev_addr[5] ^ ha->addr[5]) & 0xf0) 17878c2ecf20Sopenharmony_ci return 0; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci nibbles |= 1 << (ha->addr[5] & 0x0f); 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci return nibbles; 17938c2ecf20Sopenharmony_ci} 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_cistatic void mv643xx_eth_program_unicast_filter(struct net_device *dev) 17968c2ecf20Sopenharmony_ci{ 17978c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 17988c2ecf20Sopenharmony_ci u32 port_config; 17998c2ecf20Sopenharmony_ci u32 nibbles; 18008c2ecf20Sopenharmony_ci int i; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci uc_addr_set(mp, dev->dev_addr); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci port_config = rdlp(mp, PORT_CONFIG) & ~UNICAST_PROMISCUOUS_MODE; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci nibbles = uc_addr_filter_mask(dev); 18078c2ecf20Sopenharmony_ci if (!nibbles) { 18088c2ecf20Sopenharmony_ci port_config |= UNICAST_PROMISCUOUS_MODE; 18098c2ecf20Sopenharmony_ci nibbles = 0xffff; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci for (i = 0; i < 16; i += 4) { 18138c2ecf20Sopenharmony_ci int off = UNICAST_TABLE(mp->port_num) + i; 18148c2ecf20Sopenharmony_ci u32 v; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci v = 0; 18178c2ecf20Sopenharmony_ci if (nibbles & 1) 18188c2ecf20Sopenharmony_ci v |= 0x00000001; 18198c2ecf20Sopenharmony_ci if (nibbles & 2) 18208c2ecf20Sopenharmony_ci v |= 0x00000100; 18218c2ecf20Sopenharmony_ci if (nibbles & 4) 18228c2ecf20Sopenharmony_ci v |= 0x00010000; 18238c2ecf20Sopenharmony_ci if (nibbles & 8) 18248c2ecf20Sopenharmony_ci v |= 0x01000000; 18258c2ecf20Sopenharmony_ci nibbles >>= 4; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci wrl(mp, off, v); 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci wrlp(mp, PORT_CONFIG, port_config); 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_cistatic int addr_crc(unsigned char *addr) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci int crc = 0; 18368c2ecf20Sopenharmony_ci int i; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 18398c2ecf20Sopenharmony_ci int j; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci crc = (crc ^ addr[i]) << 8; 18428c2ecf20Sopenharmony_ci for (j = 7; j >= 0; j--) { 18438c2ecf20Sopenharmony_ci if (crc & (0x100 << j)) 18448c2ecf20Sopenharmony_ci crc ^= 0x107 << j; 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci } 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci return crc; 18498c2ecf20Sopenharmony_ci} 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_cistatic void mv643xx_eth_program_multicast_filter(struct net_device *dev) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 18548c2ecf20Sopenharmony_ci u32 *mc_spec; 18558c2ecf20Sopenharmony_ci u32 *mc_other; 18568c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 18578c2ecf20Sopenharmony_ci int i; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) 18608c2ecf20Sopenharmony_ci goto promiscuous; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci /* Allocate both mc_spec and mc_other tables */ 18638c2ecf20Sopenharmony_ci mc_spec = kcalloc(128, sizeof(u32), GFP_ATOMIC); 18648c2ecf20Sopenharmony_ci if (!mc_spec) 18658c2ecf20Sopenharmony_ci goto promiscuous; 18668c2ecf20Sopenharmony_ci mc_other = &mc_spec[64]; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 18698c2ecf20Sopenharmony_ci u8 *a = ha->addr; 18708c2ecf20Sopenharmony_ci u32 *table; 18718c2ecf20Sopenharmony_ci u8 entry; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) { 18748c2ecf20Sopenharmony_ci table = mc_spec; 18758c2ecf20Sopenharmony_ci entry = a[5]; 18768c2ecf20Sopenharmony_ci } else { 18778c2ecf20Sopenharmony_ci table = mc_other; 18788c2ecf20Sopenharmony_ci entry = addr_crc(a); 18798c2ecf20Sopenharmony_ci } 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci table[entry >> 2] |= 1 << (8 * (entry & 3)); 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci for (i = 0; i < 64; i++) { 18858c2ecf20Sopenharmony_ci wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i * sizeof(u32), 18868c2ecf20Sopenharmony_ci mc_spec[i]); 18878c2ecf20Sopenharmony_ci wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i * sizeof(u32), 18888c2ecf20Sopenharmony_ci mc_other[i]); 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci kfree(mc_spec); 18928c2ecf20Sopenharmony_ci return; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cipromiscuous: 18958c2ecf20Sopenharmony_ci for (i = 0; i < 64; i++) { 18968c2ecf20Sopenharmony_ci wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i * sizeof(u32), 18978c2ecf20Sopenharmony_ci 0x01010101u); 18988c2ecf20Sopenharmony_ci wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i * sizeof(u32), 18998c2ecf20Sopenharmony_ci 0x01010101u); 19008c2ecf20Sopenharmony_ci } 19018c2ecf20Sopenharmony_ci} 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_cistatic void mv643xx_eth_set_rx_mode(struct net_device *dev) 19048c2ecf20Sopenharmony_ci{ 19058c2ecf20Sopenharmony_ci mv643xx_eth_program_unicast_filter(dev); 19068c2ecf20Sopenharmony_ci mv643xx_eth_program_multicast_filter(dev); 19078c2ecf20Sopenharmony_ci} 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_cistatic int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) 19108c2ecf20Sopenharmony_ci{ 19118c2ecf20Sopenharmony_ci struct sockaddr *sa = addr; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(sa->sa_data)) 19148c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci netif_addr_lock_bh(dev); 19198c2ecf20Sopenharmony_ci mv643xx_eth_program_unicast_filter(dev); 19208c2ecf20Sopenharmony_ci netif_addr_unlock_bh(dev); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci return 0; 19238c2ecf20Sopenharmony_ci} 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci/* rx/tx queue initialisation ***********************************************/ 19278c2ecf20Sopenharmony_cistatic int rxq_init(struct mv643xx_eth_private *mp, int index) 19288c2ecf20Sopenharmony_ci{ 19298c2ecf20Sopenharmony_ci struct rx_queue *rxq = mp->rxq + index; 19308c2ecf20Sopenharmony_ci struct rx_desc *rx_desc; 19318c2ecf20Sopenharmony_ci int size; 19328c2ecf20Sopenharmony_ci int i; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci rxq->index = index; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci rxq->rx_ring_size = mp->rx_ring_size; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci rxq->rx_desc_count = 0; 19398c2ecf20Sopenharmony_ci rxq->rx_curr_desc = 0; 19408c2ecf20Sopenharmony_ci rxq->rx_used_desc = 0; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci size = rxq->rx_ring_size * sizeof(struct rx_desc); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if (index == 0 && size <= mp->rx_desc_sram_size) { 19458c2ecf20Sopenharmony_ci rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr, 19468c2ecf20Sopenharmony_ci mp->rx_desc_sram_size); 19478c2ecf20Sopenharmony_ci rxq->rx_desc_dma = mp->rx_desc_sram_addr; 19488c2ecf20Sopenharmony_ci } else { 19498c2ecf20Sopenharmony_ci rxq->rx_desc_area = dma_alloc_coherent(mp->dev->dev.parent, 19508c2ecf20Sopenharmony_ci size, &rxq->rx_desc_dma, 19518c2ecf20Sopenharmony_ci GFP_KERNEL); 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (rxq->rx_desc_area == NULL) { 19558c2ecf20Sopenharmony_ci netdev_err(mp->dev, 19568c2ecf20Sopenharmony_ci "can't allocate rx ring (%d bytes)\n", size); 19578c2ecf20Sopenharmony_ci goto out; 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci memset(rxq->rx_desc_area, 0, size); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci rxq->rx_desc_area_size = size; 19628c2ecf20Sopenharmony_ci rxq->rx_skb = kcalloc(rxq->rx_ring_size, sizeof(*rxq->rx_skb), 19638c2ecf20Sopenharmony_ci GFP_KERNEL); 19648c2ecf20Sopenharmony_ci if (rxq->rx_skb == NULL) 19658c2ecf20Sopenharmony_ci goto out_free; 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci rx_desc = rxq->rx_desc_area; 19688c2ecf20Sopenharmony_ci for (i = 0; i < rxq->rx_ring_size; i++) { 19698c2ecf20Sopenharmony_ci int nexti; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci nexti = i + 1; 19728c2ecf20Sopenharmony_ci if (nexti == rxq->rx_ring_size) 19738c2ecf20Sopenharmony_ci nexti = 0; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci rx_desc[i].next_desc_ptr = rxq->rx_desc_dma + 19768c2ecf20Sopenharmony_ci nexti * sizeof(struct rx_desc); 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci return 0; 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ciout_free: 19838c2ecf20Sopenharmony_ci if (index == 0 && size <= mp->rx_desc_sram_size) 19848c2ecf20Sopenharmony_ci iounmap(rxq->rx_desc_area); 19858c2ecf20Sopenharmony_ci else 19868c2ecf20Sopenharmony_ci dma_free_coherent(mp->dev->dev.parent, size, 19878c2ecf20Sopenharmony_ci rxq->rx_desc_area, 19888c2ecf20Sopenharmony_ci rxq->rx_desc_dma); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ciout: 19918c2ecf20Sopenharmony_ci return -ENOMEM; 19928c2ecf20Sopenharmony_ci} 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_cistatic void rxq_deinit(struct rx_queue *rxq) 19958c2ecf20Sopenharmony_ci{ 19968c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = rxq_to_mp(rxq); 19978c2ecf20Sopenharmony_ci int i; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci rxq_disable(rxq); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci for (i = 0; i < rxq->rx_ring_size; i++) { 20028c2ecf20Sopenharmony_ci if (rxq->rx_skb[i]) { 20038c2ecf20Sopenharmony_ci dev_consume_skb_any(rxq->rx_skb[i]); 20048c2ecf20Sopenharmony_ci rxq->rx_desc_count--; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci if (rxq->rx_desc_count) { 20098c2ecf20Sopenharmony_ci netdev_err(mp->dev, "error freeing rx ring -- %d skbs stuck\n", 20108c2ecf20Sopenharmony_ci rxq->rx_desc_count); 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci if (rxq->index == 0 && 20148c2ecf20Sopenharmony_ci rxq->rx_desc_area_size <= mp->rx_desc_sram_size) 20158c2ecf20Sopenharmony_ci iounmap(rxq->rx_desc_area); 20168c2ecf20Sopenharmony_ci else 20178c2ecf20Sopenharmony_ci dma_free_coherent(mp->dev->dev.parent, rxq->rx_desc_area_size, 20188c2ecf20Sopenharmony_ci rxq->rx_desc_area, rxq->rx_desc_dma); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci kfree(rxq->rx_skb); 20218c2ecf20Sopenharmony_ci} 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cistatic int txq_init(struct mv643xx_eth_private *mp, int index) 20248c2ecf20Sopenharmony_ci{ 20258c2ecf20Sopenharmony_ci struct tx_queue *txq = mp->txq + index; 20268c2ecf20Sopenharmony_ci struct tx_desc *tx_desc; 20278c2ecf20Sopenharmony_ci int size; 20288c2ecf20Sopenharmony_ci int ret; 20298c2ecf20Sopenharmony_ci int i; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci txq->index = index; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci txq->tx_ring_size = mp->tx_ring_size; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci /* A queue must always have room for at least one skb. 20368c2ecf20Sopenharmony_ci * Therefore, stop the queue when the free entries reaches 20378c2ecf20Sopenharmony_ci * the maximum number of descriptors per skb. 20388c2ecf20Sopenharmony_ci */ 20398c2ecf20Sopenharmony_ci txq->tx_stop_threshold = txq->tx_ring_size - MV643XX_MAX_SKB_DESCS; 20408c2ecf20Sopenharmony_ci txq->tx_wake_threshold = txq->tx_stop_threshold / 2; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci txq->tx_desc_count = 0; 20438c2ecf20Sopenharmony_ci txq->tx_curr_desc = 0; 20448c2ecf20Sopenharmony_ci txq->tx_used_desc = 0; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci size = txq->tx_ring_size * sizeof(struct tx_desc); 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci if (index == 0 && size <= mp->tx_desc_sram_size) { 20498c2ecf20Sopenharmony_ci txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr, 20508c2ecf20Sopenharmony_ci mp->tx_desc_sram_size); 20518c2ecf20Sopenharmony_ci txq->tx_desc_dma = mp->tx_desc_sram_addr; 20528c2ecf20Sopenharmony_ci } else { 20538c2ecf20Sopenharmony_ci txq->tx_desc_area = dma_alloc_coherent(mp->dev->dev.parent, 20548c2ecf20Sopenharmony_ci size, &txq->tx_desc_dma, 20558c2ecf20Sopenharmony_ci GFP_KERNEL); 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci if (txq->tx_desc_area == NULL) { 20598c2ecf20Sopenharmony_ci netdev_err(mp->dev, 20608c2ecf20Sopenharmony_ci "can't allocate tx ring (%d bytes)\n", size); 20618c2ecf20Sopenharmony_ci return -ENOMEM; 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci memset(txq->tx_desc_area, 0, size); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci txq->tx_desc_area_size = size; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci tx_desc = txq->tx_desc_area; 20688c2ecf20Sopenharmony_ci for (i = 0; i < txq->tx_ring_size; i++) { 20698c2ecf20Sopenharmony_ci struct tx_desc *txd = tx_desc + i; 20708c2ecf20Sopenharmony_ci int nexti; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci nexti = i + 1; 20738c2ecf20Sopenharmony_ci if (nexti == txq->tx_ring_size) 20748c2ecf20Sopenharmony_ci nexti = 0; 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci txd->cmd_sts = 0; 20778c2ecf20Sopenharmony_ci txd->next_desc_ptr = txq->tx_desc_dma + 20788c2ecf20Sopenharmony_ci nexti * sizeof(struct tx_desc); 20798c2ecf20Sopenharmony_ci } 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci txq->tx_desc_mapping = kcalloc(txq->tx_ring_size, sizeof(char), 20828c2ecf20Sopenharmony_ci GFP_KERNEL); 20838c2ecf20Sopenharmony_ci if (!txq->tx_desc_mapping) { 20848c2ecf20Sopenharmony_ci ret = -ENOMEM; 20858c2ecf20Sopenharmony_ci goto err_free_desc_area; 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci /* Allocate DMA buffers for TSO MAC/IP/TCP headers */ 20898c2ecf20Sopenharmony_ci txq->tso_hdrs = dma_alloc_coherent(mp->dev->dev.parent, 20908c2ecf20Sopenharmony_ci txq->tx_ring_size * TSO_HEADER_SIZE, 20918c2ecf20Sopenharmony_ci &txq->tso_hdrs_dma, GFP_KERNEL); 20928c2ecf20Sopenharmony_ci if (txq->tso_hdrs == NULL) { 20938c2ecf20Sopenharmony_ci ret = -ENOMEM; 20948c2ecf20Sopenharmony_ci goto err_free_desc_mapping; 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci skb_queue_head_init(&txq->tx_skb); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci return 0; 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_cierr_free_desc_mapping: 21018c2ecf20Sopenharmony_ci kfree(txq->tx_desc_mapping); 21028c2ecf20Sopenharmony_cierr_free_desc_area: 21038c2ecf20Sopenharmony_ci if (index == 0 && size <= mp->tx_desc_sram_size) 21048c2ecf20Sopenharmony_ci iounmap(txq->tx_desc_area); 21058c2ecf20Sopenharmony_ci else 21068c2ecf20Sopenharmony_ci dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, 21078c2ecf20Sopenharmony_ci txq->tx_desc_area, txq->tx_desc_dma); 21088c2ecf20Sopenharmony_ci return ret; 21098c2ecf20Sopenharmony_ci} 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_cistatic void txq_deinit(struct tx_queue *txq) 21128c2ecf20Sopenharmony_ci{ 21138c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = txq_to_mp(txq); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci txq_disable(txq); 21168c2ecf20Sopenharmony_ci txq_reclaim(txq, txq->tx_ring_size, 1); 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci if (txq->index == 0 && 21218c2ecf20Sopenharmony_ci txq->tx_desc_area_size <= mp->tx_desc_sram_size) 21228c2ecf20Sopenharmony_ci iounmap(txq->tx_desc_area); 21238c2ecf20Sopenharmony_ci else 21248c2ecf20Sopenharmony_ci dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, 21258c2ecf20Sopenharmony_ci txq->tx_desc_area, txq->tx_desc_dma); 21268c2ecf20Sopenharmony_ci kfree(txq->tx_desc_mapping); 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci if (txq->tso_hdrs) 21298c2ecf20Sopenharmony_ci dma_free_coherent(mp->dev->dev.parent, 21308c2ecf20Sopenharmony_ci txq->tx_ring_size * TSO_HEADER_SIZE, 21318c2ecf20Sopenharmony_ci txq->tso_hdrs, txq->tso_hdrs_dma); 21328c2ecf20Sopenharmony_ci} 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci/* netdev ops and related ***************************************************/ 21368c2ecf20Sopenharmony_cistatic int mv643xx_eth_collect_events(struct mv643xx_eth_private *mp) 21378c2ecf20Sopenharmony_ci{ 21388c2ecf20Sopenharmony_ci u32 int_cause; 21398c2ecf20Sopenharmony_ci u32 int_cause_ext; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci int_cause = rdlp(mp, INT_CAUSE) & mp->int_mask; 21428c2ecf20Sopenharmony_ci if (int_cause == 0) 21438c2ecf20Sopenharmony_ci return 0; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci int_cause_ext = 0; 21468c2ecf20Sopenharmony_ci if (int_cause & INT_EXT) { 21478c2ecf20Sopenharmony_ci int_cause &= ~INT_EXT; 21488c2ecf20Sopenharmony_ci int_cause_ext = rdlp(mp, INT_CAUSE_EXT); 21498c2ecf20Sopenharmony_ci } 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci if (int_cause) { 21528c2ecf20Sopenharmony_ci wrlp(mp, INT_CAUSE, ~int_cause); 21538c2ecf20Sopenharmony_ci mp->work_tx_end |= ((int_cause & INT_TX_END) >> 19) & 21548c2ecf20Sopenharmony_ci ~(rdlp(mp, TXQ_COMMAND) & 0xff); 21558c2ecf20Sopenharmony_ci mp->work_rx |= (int_cause & INT_RX) >> 2; 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci int_cause_ext &= INT_EXT_LINK_PHY | INT_EXT_TX; 21598c2ecf20Sopenharmony_ci if (int_cause_ext) { 21608c2ecf20Sopenharmony_ci wrlp(mp, INT_CAUSE_EXT, ~int_cause_ext); 21618c2ecf20Sopenharmony_ci if (int_cause_ext & INT_EXT_LINK_PHY) 21628c2ecf20Sopenharmony_ci mp->work_link = 1; 21638c2ecf20Sopenharmony_ci mp->work_tx |= int_cause_ext & INT_EXT_TX; 21648c2ecf20Sopenharmony_ci } 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci return 1; 21678c2ecf20Sopenharmony_ci} 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_cistatic irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) 21708c2ecf20Sopenharmony_ci{ 21718c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_id; 21728c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci if (unlikely(!mv643xx_eth_collect_events(mp))) 21758c2ecf20Sopenharmony_ci return IRQ_NONE; 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK, 0); 21788c2ecf20Sopenharmony_ci napi_schedule(&mp->napi); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci return IRQ_HANDLED; 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_cistatic void handle_link_event(struct mv643xx_eth_private *mp) 21848c2ecf20Sopenharmony_ci{ 21858c2ecf20Sopenharmony_ci struct net_device *dev = mp->dev; 21868c2ecf20Sopenharmony_ci u32 port_status; 21878c2ecf20Sopenharmony_ci int speed; 21888c2ecf20Sopenharmony_ci int duplex; 21898c2ecf20Sopenharmony_ci int fc; 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci port_status = rdlp(mp, PORT_STATUS); 21928c2ecf20Sopenharmony_ci if (!(port_status & LINK_UP)) { 21938c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev)) { 21948c2ecf20Sopenharmony_ci int i; 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci netdev_info(dev, "link down\n"); 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci netif_carrier_off(dev); 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci for (i = 0; i < mp->txq_count; i++) { 22018c2ecf20Sopenharmony_ci struct tx_queue *txq = mp->txq + i; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci txq_reclaim(txq, txq->tx_ring_size, 1); 22048c2ecf20Sopenharmony_ci txq_reset_hw_ptr(txq); 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci } 22078c2ecf20Sopenharmony_ci return; 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci switch (port_status & PORT_SPEED_MASK) { 22118c2ecf20Sopenharmony_ci case PORT_SPEED_10: 22128c2ecf20Sopenharmony_ci speed = 10; 22138c2ecf20Sopenharmony_ci break; 22148c2ecf20Sopenharmony_ci case PORT_SPEED_100: 22158c2ecf20Sopenharmony_ci speed = 100; 22168c2ecf20Sopenharmony_ci break; 22178c2ecf20Sopenharmony_ci case PORT_SPEED_1000: 22188c2ecf20Sopenharmony_ci speed = 1000; 22198c2ecf20Sopenharmony_ci break; 22208c2ecf20Sopenharmony_ci default: 22218c2ecf20Sopenharmony_ci speed = -1; 22228c2ecf20Sopenharmony_ci break; 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci duplex = (port_status & FULL_DUPLEX) ? 1 : 0; 22258c2ecf20Sopenharmony_ci fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0; 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci netdev_info(dev, "link up, %d Mb/s, %s duplex, flow control %sabled\n", 22288c2ecf20Sopenharmony_ci speed, duplex ? "full" : "half", fc ? "en" : "dis"); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci if (!netif_carrier_ok(dev)) 22318c2ecf20Sopenharmony_ci netif_carrier_on(dev); 22328c2ecf20Sopenharmony_ci} 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_cistatic int mv643xx_eth_poll(struct napi_struct *napi, int budget) 22358c2ecf20Sopenharmony_ci{ 22368c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp; 22378c2ecf20Sopenharmony_ci int work_done; 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci mp = container_of(napi, struct mv643xx_eth_private, napi); 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci if (unlikely(mp->oom)) { 22428c2ecf20Sopenharmony_ci mp->oom = 0; 22438c2ecf20Sopenharmony_ci del_timer(&mp->rx_oom); 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci work_done = 0; 22478c2ecf20Sopenharmony_ci while (work_done < budget) { 22488c2ecf20Sopenharmony_ci u8 queue_mask; 22498c2ecf20Sopenharmony_ci int queue; 22508c2ecf20Sopenharmony_ci int work_tbd; 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci if (mp->work_link) { 22538c2ecf20Sopenharmony_ci mp->work_link = 0; 22548c2ecf20Sopenharmony_ci handle_link_event(mp); 22558c2ecf20Sopenharmony_ci work_done++; 22568c2ecf20Sopenharmony_ci continue; 22578c2ecf20Sopenharmony_ci } 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci queue_mask = mp->work_tx | mp->work_tx_end | mp->work_rx; 22608c2ecf20Sopenharmony_ci if (likely(!mp->oom)) 22618c2ecf20Sopenharmony_ci queue_mask |= mp->work_rx_refill; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci if (!queue_mask) { 22648c2ecf20Sopenharmony_ci if (mv643xx_eth_collect_events(mp)) 22658c2ecf20Sopenharmony_ci continue; 22668c2ecf20Sopenharmony_ci break; 22678c2ecf20Sopenharmony_ci } 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci queue = fls(queue_mask) - 1; 22708c2ecf20Sopenharmony_ci queue_mask = 1 << queue; 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci work_tbd = budget - work_done; 22738c2ecf20Sopenharmony_ci if (work_tbd > 16) 22748c2ecf20Sopenharmony_ci work_tbd = 16; 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci if (mp->work_tx_end & queue_mask) { 22778c2ecf20Sopenharmony_ci txq_kick(mp->txq + queue); 22788c2ecf20Sopenharmony_ci } else if (mp->work_tx & queue_mask) { 22798c2ecf20Sopenharmony_ci work_done += txq_reclaim(mp->txq + queue, work_tbd, 0); 22808c2ecf20Sopenharmony_ci txq_maybe_wake(mp->txq + queue); 22818c2ecf20Sopenharmony_ci } else if (mp->work_rx & queue_mask) { 22828c2ecf20Sopenharmony_ci work_done += rxq_process(mp->rxq + queue, work_tbd); 22838c2ecf20Sopenharmony_ci } else if (!mp->oom && (mp->work_rx_refill & queue_mask)) { 22848c2ecf20Sopenharmony_ci work_done += rxq_refill(mp->rxq + queue, work_tbd); 22858c2ecf20Sopenharmony_ci } else { 22868c2ecf20Sopenharmony_ci BUG(); 22878c2ecf20Sopenharmony_ci } 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci if (work_done < budget) { 22918c2ecf20Sopenharmony_ci if (mp->oom) 22928c2ecf20Sopenharmony_ci mod_timer(&mp->rx_oom, jiffies + (HZ / 10)); 22938c2ecf20Sopenharmony_ci napi_complete_done(napi, work_done); 22948c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK, mp->int_mask); 22958c2ecf20Sopenharmony_ci } 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci return work_done; 22988c2ecf20Sopenharmony_ci} 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_cistatic inline void oom_timer_wrapper(struct timer_list *t) 23018c2ecf20Sopenharmony_ci{ 23028c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = from_timer(mp, t, rx_oom); 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci napi_schedule(&mp->napi); 23058c2ecf20Sopenharmony_ci} 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_cistatic void port_start(struct mv643xx_eth_private *mp) 23088c2ecf20Sopenharmony_ci{ 23098c2ecf20Sopenharmony_ci struct net_device *dev = mp->dev; 23108c2ecf20Sopenharmony_ci u32 pscr; 23118c2ecf20Sopenharmony_ci int i; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci /* 23148c2ecf20Sopenharmony_ci * Perform PHY reset, if there is a PHY. 23158c2ecf20Sopenharmony_ci */ 23168c2ecf20Sopenharmony_ci if (dev->phydev) { 23178c2ecf20Sopenharmony_ci struct ethtool_link_ksettings cmd; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci mv643xx_eth_get_link_ksettings(dev, &cmd); 23208c2ecf20Sopenharmony_ci phy_init_hw(dev->phydev); 23218c2ecf20Sopenharmony_ci mv643xx_eth_set_link_ksettings( 23228c2ecf20Sopenharmony_ci dev, (const struct ethtool_link_ksettings *)&cmd); 23238c2ecf20Sopenharmony_ci phy_start(dev->phydev); 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci /* 23278c2ecf20Sopenharmony_ci * Configure basic link parameters. 23288c2ecf20Sopenharmony_ci */ 23298c2ecf20Sopenharmony_ci pscr = rdlp(mp, PORT_SERIAL_CONTROL); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci pscr |= SERIAL_PORT_ENABLE; 23328c2ecf20Sopenharmony_ci wrlp(mp, PORT_SERIAL_CONTROL, pscr); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci pscr |= DO_NOT_FORCE_LINK_FAIL; 23358c2ecf20Sopenharmony_ci if (!dev->phydev) 23368c2ecf20Sopenharmony_ci pscr |= FORCE_LINK_PASS; 23378c2ecf20Sopenharmony_ci wrlp(mp, PORT_SERIAL_CONTROL, pscr); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci /* 23408c2ecf20Sopenharmony_ci * Configure TX path and queues. 23418c2ecf20Sopenharmony_ci */ 23428c2ecf20Sopenharmony_ci tx_set_rate(mp, 1000000000, 16777216); 23438c2ecf20Sopenharmony_ci for (i = 0; i < mp->txq_count; i++) { 23448c2ecf20Sopenharmony_ci struct tx_queue *txq = mp->txq + i; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci txq_reset_hw_ptr(txq); 23478c2ecf20Sopenharmony_ci txq_set_rate(txq, 1000000000, 16777216); 23488c2ecf20Sopenharmony_ci txq_set_fixed_prio_mode(txq); 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci /* 23528c2ecf20Sopenharmony_ci * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast 23538c2ecf20Sopenharmony_ci * frames to RX queue #0, and include the pseudo-header when 23548c2ecf20Sopenharmony_ci * calculating receive checksums. 23558c2ecf20Sopenharmony_ci */ 23568c2ecf20Sopenharmony_ci mv643xx_eth_set_features(mp->dev, mp->dev->features); 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci /* 23598c2ecf20Sopenharmony_ci * Treat BPDUs as normal multicasts, and disable partition mode. 23608c2ecf20Sopenharmony_ci */ 23618c2ecf20Sopenharmony_ci wrlp(mp, PORT_CONFIG_EXT, 0x00000000); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci /* 23648c2ecf20Sopenharmony_ci * Add configured unicast addresses to address filter table. 23658c2ecf20Sopenharmony_ci */ 23668c2ecf20Sopenharmony_ci mv643xx_eth_program_unicast_filter(mp->dev); 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci /* 23698c2ecf20Sopenharmony_ci * Enable the receive queues. 23708c2ecf20Sopenharmony_ci */ 23718c2ecf20Sopenharmony_ci for (i = 0; i < mp->rxq_count; i++) { 23728c2ecf20Sopenharmony_ci struct rx_queue *rxq = mp->rxq + i; 23738c2ecf20Sopenharmony_ci u32 addr; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_ci addr = (u32)rxq->rx_desc_dma; 23768c2ecf20Sopenharmony_ci addr += rxq->rx_curr_desc * sizeof(struct rx_desc); 23778c2ecf20Sopenharmony_ci wrlp(mp, RXQ_CURRENT_DESC_PTR(i), addr); 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci rxq_enable(rxq); 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci} 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_cistatic void mv643xx_eth_recalc_skb_size(struct mv643xx_eth_private *mp) 23848c2ecf20Sopenharmony_ci{ 23858c2ecf20Sopenharmony_ci int skb_size; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci /* 23888c2ecf20Sopenharmony_ci * Reserve 2+14 bytes for an ethernet header (the hardware 23898c2ecf20Sopenharmony_ci * automatically prepends 2 bytes of dummy data to each 23908c2ecf20Sopenharmony_ci * received packet), 16 bytes for up to four VLAN tags, and 23918c2ecf20Sopenharmony_ci * 4 bytes for the trailing FCS -- 36 bytes total. 23928c2ecf20Sopenharmony_ci */ 23938c2ecf20Sopenharmony_ci skb_size = mp->dev->mtu + 36; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci /* 23968c2ecf20Sopenharmony_ci * Make sure that the skb size is a multiple of 8 bytes, as 23978c2ecf20Sopenharmony_ci * the lower three bits of the receive descriptor's buffer 23988c2ecf20Sopenharmony_ci * size field are ignored by the hardware. 23998c2ecf20Sopenharmony_ci */ 24008c2ecf20Sopenharmony_ci mp->skb_size = (skb_size + 7) & ~7; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci /* 24038c2ecf20Sopenharmony_ci * If NET_SKB_PAD is smaller than a cache line, 24048c2ecf20Sopenharmony_ci * netdev_alloc_skb() will cause skb->data to be misaligned 24058c2ecf20Sopenharmony_ci * to a cache line boundary. If this is the case, include 24068c2ecf20Sopenharmony_ci * some extra space to allow re-aligning the data area. 24078c2ecf20Sopenharmony_ci */ 24088c2ecf20Sopenharmony_ci mp->skb_size += SKB_DMA_REALIGN; 24098c2ecf20Sopenharmony_ci} 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_cistatic int mv643xx_eth_open(struct net_device *dev) 24128c2ecf20Sopenharmony_ci{ 24138c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 24148c2ecf20Sopenharmony_ci int err; 24158c2ecf20Sopenharmony_ci int i; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci wrlp(mp, INT_CAUSE, 0); 24188c2ecf20Sopenharmony_ci wrlp(mp, INT_CAUSE_EXT, 0); 24198c2ecf20Sopenharmony_ci rdlp(mp, INT_CAUSE_EXT); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci err = request_irq(dev->irq, mv643xx_eth_irq, 24228c2ecf20Sopenharmony_ci IRQF_SHARED, dev->name, dev); 24238c2ecf20Sopenharmony_ci if (err) { 24248c2ecf20Sopenharmony_ci netdev_err(dev, "can't assign irq\n"); 24258c2ecf20Sopenharmony_ci return -EAGAIN; 24268c2ecf20Sopenharmony_ci } 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci mv643xx_eth_recalc_skb_size(mp); 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci napi_enable(&mp->napi); 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci mp->int_mask = INT_EXT; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci for (i = 0; i < mp->rxq_count; i++) { 24358c2ecf20Sopenharmony_ci err = rxq_init(mp, i); 24368c2ecf20Sopenharmony_ci if (err) { 24378c2ecf20Sopenharmony_ci while (--i >= 0) 24388c2ecf20Sopenharmony_ci rxq_deinit(mp->rxq + i); 24398c2ecf20Sopenharmony_ci goto out; 24408c2ecf20Sopenharmony_ci } 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci rxq_refill(mp->rxq + i, INT_MAX); 24438c2ecf20Sopenharmony_ci mp->int_mask |= INT_RX_0 << i; 24448c2ecf20Sopenharmony_ci } 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci if (mp->oom) { 24478c2ecf20Sopenharmony_ci mp->rx_oom.expires = jiffies + (HZ / 10); 24488c2ecf20Sopenharmony_ci add_timer(&mp->rx_oom); 24498c2ecf20Sopenharmony_ci } 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci for (i = 0; i < mp->txq_count; i++) { 24528c2ecf20Sopenharmony_ci err = txq_init(mp, i); 24538c2ecf20Sopenharmony_ci if (err) { 24548c2ecf20Sopenharmony_ci while (--i >= 0) 24558c2ecf20Sopenharmony_ci txq_deinit(mp->txq + i); 24568c2ecf20Sopenharmony_ci goto out_free; 24578c2ecf20Sopenharmony_ci } 24588c2ecf20Sopenharmony_ci mp->int_mask |= INT_TX_END_0 << i; 24598c2ecf20Sopenharmony_ci } 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci add_timer(&mp->mib_counters_timer); 24628c2ecf20Sopenharmony_ci port_start(mp); 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK_EXT, INT_EXT_LINK_PHY | INT_EXT_TX); 24658c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK, mp->int_mask); 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci return 0; 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ciout_free: 24718c2ecf20Sopenharmony_ci for (i = 0; i < mp->rxq_count; i++) 24728c2ecf20Sopenharmony_ci rxq_deinit(mp->rxq + i); 24738c2ecf20Sopenharmony_ciout: 24748c2ecf20Sopenharmony_ci napi_disable(&mp->napi); 24758c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci return err; 24788c2ecf20Sopenharmony_ci} 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_cistatic void port_reset(struct mv643xx_eth_private *mp) 24818c2ecf20Sopenharmony_ci{ 24828c2ecf20Sopenharmony_ci unsigned int data; 24838c2ecf20Sopenharmony_ci int i; 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci for (i = 0; i < mp->rxq_count; i++) 24868c2ecf20Sopenharmony_ci rxq_disable(mp->rxq + i); 24878c2ecf20Sopenharmony_ci for (i = 0; i < mp->txq_count; i++) 24888c2ecf20Sopenharmony_ci txq_disable(mp->txq + i); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci while (1) { 24918c2ecf20Sopenharmony_ci u32 ps = rdlp(mp, PORT_STATUS); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci if ((ps & (TX_IN_PROGRESS | TX_FIFO_EMPTY)) == TX_FIFO_EMPTY) 24948c2ecf20Sopenharmony_ci break; 24958c2ecf20Sopenharmony_ci udelay(10); 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci /* Reset the Enable bit in the Configuration Register */ 24998c2ecf20Sopenharmony_ci data = rdlp(mp, PORT_SERIAL_CONTROL); 25008c2ecf20Sopenharmony_ci data &= ~(SERIAL_PORT_ENABLE | 25018c2ecf20Sopenharmony_ci DO_NOT_FORCE_LINK_FAIL | 25028c2ecf20Sopenharmony_ci FORCE_LINK_PASS); 25038c2ecf20Sopenharmony_ci wrlp(mp, PORT_SERIAL_CONTROL, data); 25048c2ecf20Sopenharmony_ci} 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_cistatic int mv643xx_eth_stop(struct net_device *dev) 25078c2ecf20Sopenharmony_ci{ 25088c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 25098c2ecf20Sopenharmony_ci int i; 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK_EXT, 0x00000000); 25128c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK, 0x00000000); 25138c2ecf20Sopenharmony_ci rdlp(mp, INT_MASK); 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci napi_disable(&mp->napi); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci del_timer_sync(&mp->rx_oom); 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci netif_carrier_off(dev); 25208c2ecf20Sopenharmony_ci if (dev->phydev) 25218c2ecf20Sopenharmony_ci phy_stop(dev->phydev); 25228c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci port_reset(mp); 25258c2ecf20Sopenharmony_ci mv643xx_eth_get_stats(dev); 25268c2ecf20Sopenharmony_ci mib_counters_update(mp); 25278c2ecf20Sopenharmony_ci del_timer_sync(&mp->mib_counters_timer); 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci for (i = 0; i < mp->rxq_count; i++) 25308c2ecf20Sopenharmony_ci rxq_deinit(mp->rxq + i); 25318c2ecf20Sopenharmony_ci for (i = 0; i < mp->txq_count; i++) 25328c2ecf20Sopenharmony_ci txq_deinit(mp->txq + i); 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci return 0; 25358c2ecf20Sopenharmony_ci} 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_cistatic int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 25388c2ecf20Sopenharmony_ci{ 25398c2ecf20Sopenharmony_ci int ret; 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci if (!dev->phydev) 25428c2ecf20Sopenharmony_ci return -ENOTSUPP; 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci ret = phy_mii_ioctl(dev->phydev, ifr, cmd); 25458c2ecf20Sopenharmony_ci if (!ret) 25468c2ecf20Sopenharmony_ci mv643xx_eth_adjust_link(dev); 25478c2ecf20Sopenharmony_ci return ret; 25488c2ecf20Sopenharmony_ci} 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_cistatic int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) 25518c2ecf20Sopenharmony_ci{ 25528c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 25558c2ecf20Sopenharmony_ci mv643xx_eth_recalc_skb_size(mp); 25568c2ecf20Sopenharmony_ci tx_set_rate(mp, 1000000000, 16777216); 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci if (!netif_running(dev)) 25598c2ecf20Sopenharmony_ci return 0; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci /* 25628c2ecf20Sopenharmony_ci * Stop and then re-open the interface. This will allocate RX 25638c2ecf20Sopenharmony_ci * skbs of the new MTU. 25648c2ecf20Sopenharmony_ci * There is a possible danger that the open will not succeed, 25658c2ecf20Sopenharmony_ci * due to memory being full. 25668c2ecf20Sopenharmony_ci */ 25678c2ecf20Sopenharmony_ci mv643xx_eth_stop(dev); 25688c2ecf20Sopenharmony_ci if (mv643xx_eth_open(dev)) { 25698c2ecf20Sopenharmony_ci netdev_err(dev, 25708c2ecf20Sopenharmony_ci "fatal error on re-opening device after MTU change\n"); 25718c2ecf20Sopenharmony_ci } 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci return 0; 25748c2ecf20Sopenharmony_ci} 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_cistatic void tx_timeout_task(struct work_struct *ugly) 25778c2ecf20Sopenharmony_ci{ 25788c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci mp = container_of(ugly, struct mv643xx_eth_private, tx_timeout_task); 25818c2ecf20Sopenharmony_ci if (netif_running(mp->dev)) { 25828c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(mp->dev); 25838c2ecf20Sopenharmony_ci port_reset(mp); 25848c2ecf20Sopenharmony_ci port_start(mp); 25858c2ecf20Sopenharmony_ci netif_tx_wake_all_queues(mp->dev); 25868c2ecf20Sopenharmony_ci } 25878c2ecf20Sopenharmony_ci} 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_cistatic void mv643xx_eth_tx_timeout(struct net_device *dev, unsigned int txqueue) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci netdev_info(dev, "tx timeout\n"); 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci schedule_work(&mp->tx_timeout_task); 25968c2ecf20Sopenharmony_ci} 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 25998c2ecf20Sopenharmony_cistatic void mv643xx_eth_netpoll(struct net_device *dev) 26008c2ecf20Sopenharmony_ci{ 26018c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = netdev_priv(dev); 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK, 0x00000000); 26048c2ecf20Sopenharmony_ci rdlp(mp, INT_MASK); 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci mv643xx_eth_irq(dev->irq, dev); 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK, mp->int_mask); 26098c2ecf20Sopenharmony_ci} 26108c2ecf20Sopenharmony_ci#endif 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci/* platform glue ************************************************************/ 26148c2ecf20Sopenharmony_cistatic void 26158c2ecf20Sopenharmony_cimv643xx_eth_conf_mbus_windows(struct mv643xx_eth_shared_private *msp, 26168c2ecf20Sopenharmony_ci const struct mbus_dram_target_info *dram) 26178c2ecf20Sopenharmony_ci{ 26188c2ecf20Sopenharmony_ci void __iomem *base = msp->base; 26198c2ecf20Sopenharmony_ci u32 win_enable; 26208c2ecf20Sopenharmony_ci u32 win_protect; 26218c2ecf20Sopenharmony_ci int i; 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 26248c2ecf20Sopenharmony_ci writel(0, base + WINDOW_BASE(i)); 26258c2ecf20Sopenharmony_ci writel(0, base + WINDOW_SIZE(i)); 26268c2ecf20Sopenharmony_ci if (i < 4) 26278c2ecf20Sopenharmony_ci writel(0, base + WINDOW_REMAP_HIGH(i)); 26288c2ecf20Sopenharmony_ci } 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci win_enable = 0x3f; 26318c2ecf20Sopenharmony_ci win_protect = 0; 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci for (i = 0; i < dram->num_cs; i++) { 26348c2ecf20Sopenharmony_ci const struct mbus_dram_window *cs = dram->cs + i; 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci writel((cs->base & 0xffff0000) | 26378c2ecf20Sopenharmony_ci (cs->mbus_attr << 8) | 26388c2ecf20Sopenharmony_ci dram->mbus_dram_target_id, base + WINDOW_BASE(i)); 26398c2ecf20Sopenharmony_ci writel((cs->size - 1) & 0xffff0000, base + WINDOW_SIZE(i)); 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci win_enable &= ~(1 << i); 26428c2ecf20Sopenharmony_ci win_protect |= 3 << (2 * i); 26438c2ecf20Sopenharmony_ci } 26448c2ecf20Sopenharmony_ci 26458c2ecf20Sopenharmony_ci writel(win_enable, base + WINDOW_BAR_ENABLE); 26468c2ecf20Sopenharmony_ci msp->win_protect = win_protect; 26478c2ecf20Sopenharmony_ci} 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_cistatic void infer_hw_params(struct mv643xx_eth_shared_private *msp) 26508c2ecf20Sopenharmony_ci{ 26518c2ecf20Sopenharmony_ci /* 26528c2ecf20Sopenharmony_ci * Check whether we have a 14-bit coal limit field in bits 26538c2ecf20Sopenharmony_ci * [21:8], or a 16-bit coal limit in bits [25,21:7] of the 26548c2ecf20Sopenharmony_ci * SDMA config register. 26558c2ecf20Sopenharmony_ci */ 26568c2ecf20Sopenharmony_ci writel(0x02000000, msp->base + 0x0400 + SDMA_CONFIG); 26578c2ecf20Sopenharmony_ci if (readl(msp->base + 0x0400 + SDMA_CONFIG) & 0x02000000) 26588c2ecf20Sopenharmony_ci msp->extended_rx_coal_limit = 1; 26598c2ecf20Sopenharmony_ci else 26608c2ecf20Sopenharmony_ci msp->extended_rx_coal_limit = 0; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci /* 26638c2ecf20Sopenharmony_ci * Check whether the MAC supports TX rate control, and if 26648c2ecf20Sopenharmony_ci * yes, whether its associated registers are in the old or 26658c2ecf20Sopenharmony_ci * the new place. 26668c2ecf20Sopenharmony_ci */ 26678c2ecf20Sopenharmony_ci writel(1, msp->base + 0x0400 + TX_BW_MTU_MOVED); 26688c2ecf20Sopenharmony_ci if (readl(msp->base + 0x0400 + TX_BW_MTU_MOVED) & 1) { 26698c2ecf20Sopenharmony_ci msp->tx_bw_control = TX_BW_CONTROL_NEW_LAYOUT; 26708c2ecf20Sopenharmony_ci } else { 26718c2ecf20Sopenharmony_ci writel(7, msp->base + 0x0400 + TX_BW_RATE); 26728c2ecf20Sopenharmony_ci if (readl(msp->base + 0x0400 + TX_BW_RATE) & 7) 26738c2ecf20Sopenharmony_ci msp->tx_bw_control = TX_BW_CONTROL_OLD_LAYOUT; 26748c2ecf20Sopenharmony_ci else 26758c2ecf20Sopenharmony_ci msp->tx_bw_control = TX_BW_CONTROL_ABSENT; 26768c2ecf20Sopenharmony_ci } 26778c2ecf20Sopenharmony_ci} 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci#if defined(CONFIG_OF) 26808c2ecf20Sopenharmony_cistatic const struct of_device_id mv643xx_eth_shared_ids[] = { 26818c2ecf20Sopenharmony_ci { .compatible = "marvell,orion-eth", }, 26828c2ecf20Sopenharmony_ci { .compatible = "marvell,kirkwood-eth", }, 26838c2ecf20Sopenharmony_ci { } 26848c2ecf20Sopenharmony_ci}; 26858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mv643xx_eth_shared_ids); 26868c2ecf20Sopenharmony_ci#endif 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci#if defined(CONFIG_OF_IRQ) && !defined(CONFIG_MV64X60) 26898c2ecf20Sopenharmony_ci#define mv643xx_eth_property(_np, _name, _v) \ 26908c2ecf20Sopenharmony_ci do { \ 26918c2ecf20Sopenharmony_ci u32 tmp; \ 26928c2ecf20Sopenharmony_ci if (!of_property_read_u32(_np, "marvell," _name, &tmp)) \ 26938c2ecf20Sopenharmony_ci _v = tmp; \ 26948c2ecf20Sopenharmony_ci } while (0) 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_cistatic struct platform_device *port_platdev[3]; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_cistatic int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, 26998c2ecf20Sopenharmony_ci struct device_node *pnp) 27008c2ecf20Sopenharmony_ci{ 27018c2ecf20Sopenharmony_ci struct platform_device *ppdev; 27028c2ecf20Sopenharmony_ci struct mv643xx_eth_platform_data ppd; 27038c2ecf20Sopenharmony_ci struct resource res; 27048c2ecf20Sopenharmony_ci const char *mac_addr; 27058c2ecf20Sopenharmony_ci int ret; 27068c2ecf20Sopenharmony_ci int dev_num = 0; 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci memset(&ppd, 0, sizeof(ppd)); 27098c2ecf20Sopenharmony_ci ppd.shared = pdev; 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci memset(&res, 0, sizeof(res)); 27128c2ecf20Sopenharmony_ci if (of_irq_to_resource(pnp, 0, &res) <= 0) { 27138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing interrupt on %pOFn\n", pnp); 27148c2ecf20Sopenharmony_ci return -EINVAL; 27158c2ecf20Sopenharmony_ci } 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci if (of_property_read_u32(pnp, "reg", &ppd.port_number)) { 27188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing reg property on %pOFn\n", pnp); 27198c2ecf20Sopenharmony_ci return -EINVAL; 27208c2ecf20Sopenharmony_ci } 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci if (ppd.port_number >= 3) { 27238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid reg property on %pOFn\n", pnp); 27248c2ecf20Sopenharmony_ci return -EINVAL; 27258c2ecf20Sopenharmony_ci } 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci while (dev_num < 3 && port_platdev[dev_num]) 27288c2ecf20Sopenharmony_ci dev_num++; 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci if (dev_num == 3) { 27318c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "too many ports registered\n"); 27328c2ecf20Sopenharmony_ci return -EINVAL; 27338c2ecf20Sopenharmony_ci } 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci mac_addr = of_get_mac_address(pnp); 27368c2ecf20Sopenharmony_ci if (!IS_ERR(mac_addr)) 27378c2ecf20Sopenharmony_ci ether_addr_copy(ppd.mac_addr, mac_addr); 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size); 27408c2ecf20Sopenharmony_ci mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr); 27418c2ecf20Sopenharmony_ci mv643xx_eth_property(pnp, "tx-sram-size", ppd.tx_sram_size); 27428c2ecf20Sopenharmony_ci mv643xx_eth_property(pnp, "rx-queue-size", ppd.rx_queue_size); 27438c2ecf20Sopenharmony_ci mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr); 27448c2ecf20Sopenharmony_ci mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size); 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0); 27478c2ecf20Sopenharmony_ci if (!ppd.phy_node) { 27488c2ecf20Sopenharmony_ci ppd.phy_addr = MV643XX_ETH_PHY_NONE; 27498c2ecf20Sopenharmony_ci of_property_read_u32(pnp, "speed", &ppd.speed); 27508c2ecf20Sopenharmony_ci of_property_read_u32(pnp, "duplex", &ppd.duplex); 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci ppdev = platform_device_alloc(MV643XX_ETH_NAME, dev_num); 27548c2ecf20Sopenharmony_ci if (!ppdev) 27558c2ecf20Sopenharmony_ci return -ENOMEM; 27568c2ecf20Sopenharmony_ci ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); 27578c2ecf20Sopenharmony_ci ppdev->dev.of_node = pnp; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci ret = platform_device_add_resources(ppdev, &res, 1); 27608c2ecf20Sopenharmony_ci if (ret) 27618c2ecf20Sopenharmony_ci goto port_err; 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd)); 27648c2ecf20Sopenharmony_ci if (ret) 27658c2ecf20Sopenharmony_ci goto port_err; 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_ci ret = platform_device_add(ppdev); 27688c2ecf20Sopenharmony_ci if (ret) 27698c2ecf20Sopenharmony_ci goto port_err; 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci port_platdev[dev_num] = ppdev; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci return 0; 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ciport_err: 27768c2ecf20Sopenharmony_ci platform_device_put(ppdev); 27778c2ecf20Sopenharmony_ci return ret; 27788c2ecf20Sopenharmony_ci} 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_cistatic int mv643xx_eth_shared_of_probe(struct platform_device *pdev) 27818c2ecf20Sopenharmony_ci{ 27828c2ecf20Sopenharmony_ci struct mv643xx_eth_shared_platform_data *pd; 27838c2ecf20Sopenharmony_ci struct device_node *pnp, *np = pdev->dev.of_node; 27848c2ecf20Sopenharmony_ci int ret; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci /* bail out if not registered from DT */ 27878c2ecf20Sopenharmony_ci if (!np) 27888c2ecf20Sopenharmony_ci return 0; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL); 27918c2ecf20Sopenharmony_ci if (!pd) 27928c2ecf20Sopenharmony_ci return -ENOMEM; 27938c2ecf20Sopenharmony_ci pdev->dev.platform_data = pd; 27948c2ecf20Sopenharmony_ci 27958c2ecf20Sopenharmony_ci mv643xx_eth_property(np, "tx-checksum-limit", pd->tx_csum_limit); 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, pnp) { 27988c2ecf20Sopenharmony_ci ret = mv643xx_eth_shared_of_add_port(pdev, pnp); 27998c2ecf20Sopenharmony_ci if (ret) { 28008c2ecf20Sopenharmony_ci of_node_put(pnp); 28018c2ecf20Sopenharmony_ci return ret; 28028c2ecf20Sopenharmony_ci } 28038c2ecf20Sopenharmony_ci } 28048c2ecf20Sopenharmony_ci return 0; 28058c2ecf20Sopenharmony_ci} 28068c2ecf20Sopenharmony_ci 28078c2ecf20Sopenharmony_cistatic void mv643xx_eth_shared_of_remove(void) 28088c2ecf20Sopenharmony_ci{ 28098c2ecf20Sopenharmony_ci int n; 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci for (n = 0; n < 3; n++) { 28128c2ecf20Sopenharmony_ci platform_device_del(port_platdev[n]); 28138c2ecf20Sopenharmony_ci port_platdev[n] = NULL; 28148c2ecf20Sopenharmony_ci } 28158c2ecf20Sopenharmony_ci} 28168c2ecf20Sopenharmony_ci#else 28178c2ecf20Sopenharmony_cistatic inline int mv643xx_eth_shared_of_probe(struct platform_device *pdev) 28188c2ecf20Sopenharmony_ci{ 28198c2ecf20Sopenharmony_ci return 0; 28208c2ecf20Sopenharmony_ci} 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_cistatic inline void mv643xx_eth_shared_of_remove(void) 28238c2ecf20Sopenharmony_ci{ 28248c2ecf20Sopenharmony_ci} 28258c2ecf20Sopenharmony_ci#endif 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_cistatic int mv643xx_eth_shared_probe(struct platform_device *pdev) 28288c2ecf20Sopenharmony_ci{ 28298c2ecf20Sopenharmony_ci static int mv643xx_eth_version_printed; 28308c2ecf20Sopenharmony_ci struct mv643xx_eth_shared_platform_data *pd; 28318c2ecf20Sopenharmony_ci struct mv643xx_eth_shared_private *msp; 28328c2ecf20Sopenharmony_ci const struct mbus_dram_target_info *dram; 28338c2ecf20Sopenharmony_ci struct resource *res; 28348c2ecf20Sopenharmony_ci int ret; 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci if (!mv643xx_eth_version_printed++) 28378c2ecf20Sopenharmony_ci pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n", 28388c2ecf20Sopenharmony_ci mv643xx_eth_driver_version); 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 28418c2ecf20Sopenharmony_ci if (res == NULL) 28428c2ecf20Sopenharmony_ci return -EINVAL; 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL); 28458c2ecf20Sopenharmony_ci if (msp == NULL) 28468c2ecf20Sopenharmony_ci return -ENOMEM; 28478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, msp); 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); 28508c2ecf20Sopenharmony_ci if (msp->base == NULL) 28518c2ecf20Sopenharmony_ci return -ENOMEM; 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci msp->clk = devm_clk_get(&pdev->dev, NULL); 28548c2ecf20Sopenharmony_ci if (!IS_ERR(msp->clk)) 28558c2ecf20Sopenharmony_ci clk_prepare_enable(msp->clk); 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci /* 28588c2ecf20Sopenharmony_ci * (Re-)program MBUS remapping windows if we are asked to. 28598c2ecf20Sopenharmony_ci */ 28608c2ecf20Sopenharmony_ci dram = mv_mbus_dram_info(); 28618c2ecf20Sopenharmony_ci if (dram) 28628c2ecf20Sopenharmony_ci mv643xx_eth_conf_mbus_windows(msp, dram); 28638c2ecf20Sopenharmony_ci 28648c2ecf20Sopenharmony_ci ret = mv643xx_eth_shared_of_probe(pdev); 28658c2ecf20Sopenharmony_ci if (ret) 28668c2ecf20Sopenharmony_ci goto err_put_clk; 28678c2ecf20Sopenharmony_ci pd = dev_get_platdata(&pdev->dev); 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ? 28708c2ecf20Sopenharmony_ci pd->tx_csum_limit : 9 * 1024; 28718c2ecf20Sopenharmony_ci infer_hw_params(msp); 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci return 0; 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_cierr_put_clk: 28768c2ecf20Sopenharmony_ci if (!IS_ERR(msp->clk)) 28778c2ecf20Sopenharmony_ci clk_disable_unprepare(msp->clk); 28788c2ecf20Sopenharmony_ci return ret; 28798c2ecf20Sopenharmony_ci} 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_cistatic int mv643xx_eth_shared_remove(struct platform_device *pdev) 28828c2ecf20Sopenharmony_ci{ 28838c2ecf20Sopenharmony_ci struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci mv643xx_eth_shared_of_remove(); 28868c2ecf20Sopenharmony_ci if (!IS_ERR(msp->clk)) 28878c2ecf20Sopenharmony_ci clk_disable_unprepare(msp->clk); 28888c2ecf20Sopenharmony_ci return 0; 28898c2ecf20Sopenharmony_ci} 28908c2ecf20Sopenharmony_ci 28918c2ecf20Sopenharmony_cistatic struct platform_driver mv643xx_eth_shared_driver = { 28928c2ecf20Sopenharmony_ci .probe = mv643xx_eth_shared_probe, 28938c2ecf20Sopenharmony_ci .remove = mv643xx_eth_shared_remove, 28948c2ecf20Sopenharmony_ci .driver = { 28958c2ecf20Sopenharmony_ci .name = MV643XX_ETH_SHARED_NAME, 28968c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mv643xx_eth_shared_ids), 28978c2ecf20Sopenharmony_ci }, 28988c2ecf20Sopenharmony_ci}; 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_cistatic void phy_addr_set(struct mv643xx_eth_private *mp, int phy_addr) 29018c2ecf20Sopenharmony_ci{ 29028c2ecf20Sopenharmony_ci int addr_shift = 5 * mp->port_num; 29038c2ecf20Sopenharmony_ci u32 data; 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci data = rdl(mp, PHY_ADDR); 29068c2ecf20Sopenharmony_ci data &= ~(0x1f << addr_shift); 29078c2ecf20Sopenharmony_ci data |= (phy_addr & 0x1f) << addr_shift; 29088c2ecf20Sopenharmony_ci wrl(mp, PHY_ADDR, data); 29098c2ecf20Sopenharmony_ci} 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_cistatic int phy_addr_get(struct mv643xx_eth_private *mp) 29128c2ecf20Sopenharmony_ci{ 29138c2ecf20Sopenharmony_ci unsigned int data; 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci data = rdl(mp, PHY_ADDR); 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci return (data >> (5 * mp->port_num)) & 0x1f; 29188c2ecf20Sopenharmony_ci} 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_cistatic void set_params(struct mv643xx_eth_private *mp, 29218c2ecf20Sopenharmony_ci struct mv643xx_eth_platform_data *pd) 29228c2ecf20Sopenharmony_ci{ 29238c2ecf20Sopenharmony_ci struct net_device *dev = mp->dev; 29248c2ecf20Sopenharmony_ci unsigned int tx_ring_size; 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci if (is_valid_ether_addr(pd->mac_addr)) 29278c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN); 29288c2ecf20Sopenharmony_ci else 29298c2ecf20Sopenharmony_ci uc_addr_get(mp, dev->dev_addr); 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci mp->rx_ring_size = DEFAULT_RX_QUEUE_SIZE; 29328c2ecf20Sopenharmony_ci if (pd->rx_queue_size) 29338c2ecf20Sopenharmony_ci mp->rx_ring_size = pd->rx_queue_size; 29348c2ecf20Sopenharmony_ci mp->rx_desc_sram_addr = pd->rx_sram_addr; 29358c2ecf20Sopenharmony_ci mp->rx_desc_sram_size = pd->rx_sram_size; 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci mp->rxq_count = pd->rx_queue_count ? : 1; 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci tx_ring_size = DEFAULT_TX_QUEUE_SIZE; 29408c2ecf20Sopenharmony_ci if (pd->tx_queue_size) 29418c2ecf20Sopenharmony_ci tx_ring_size = pd->tx_queue_size; 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_ci mp->tx_ring_size = clamp_t(unsigned int, tx_ring_size, 29448c2ecf20Sopenharmony_ci MV643XX_MAX_SKB_DESCS * 2, 4096); 29458c2ecf20Sopenharmony_ci if (mp->tx_ring_size != tx_ring_size) 29468c2ecf20Sopenharmony_ci netdev_warn(dev, "TX queue size set to %u (requested %u)\n", 29478c2ecf20Sopenharmony_ci mp->tx_ring_size, tx_ring_size); 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci mp->tx_desc_sram_addr = pd->tx_sram_addr; 29508c2ecf20Sopenharmony_ci mp->tx_desc_sram_size = pd->tx_sram_size; 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci mp->txq_count = pd->tx_queue_count ? : 1; 29538c2ecf20Sopenharmony_ci} 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_cistatic int get_phy_mode(struct mv643xx_eth_private *mp) 29568c2ecf20Sopenharmony_ci{ 29578c2ecf20Sopenharmony_ci struct device *dev = mp->dev->dev.parent; 29588c2ecf20Sopenharmony_ci phy_interface_t iface; 29598c2ecf20Sopenharmony_ci int err; 29608c2ecf20Sopenharmony_ci 29618c2ecf20Sopenharmony_ci if (dev->of_node) 29628c2ecf20Sopenharmony_ci err = of_get_phy_mode(dev->of_node, &iface); 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci /* Historical default if unspecified. We could also read/write 29658c2ecf20Sopenharmony_ci * the interface state in the PSC1 29668c2ecf20Sopenharmony_ci */ 29678c2ecf20Sopenharmony_ci if (!dev->of_node || err) 29688c2ecf20Sopenharmony_ci iface = PHY_INTERFACE_MODE_GMII; 29698c2ecf20Sopenharmony_ci return iface; 29708c2ecf20Sopenharmony_ci} 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_cistatic struct phy_device *phy_scan(struct mv643xx_eth_private *mp, 29738c2ecf20Sopenharmony_ci int phy_addr) 29748c2ecf20Sopenharmony_ci{ 29758c2ecf20Sopenharmony_ci struct phy_device *phydev; 29768c2ecf20Sopenharmony_ci int start; 29778c2ecf20Sopenharmony_ci int num; 29788c2ecf20Sopenharmony_ci int i; 29798c2ecf20Sopenharmony_ci char phy_id[MII_BUS_ID_SIZE + 3]; 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci if (phy_addr == MV643XX_ETH_PHY_ADDR_DEFAULT) { 29828c2ecf20Sopenharmony_ci start = phy_addr_get(mp) & 0x1f; 29838c2ecf20Sopenharmony_ci num = 32; 29848c2ecf20Sopenharmony_ci } else { 29858c2ecf20Sopenharmony_ci start = phy_addr & 0x1f; 29868c2ecf20Sopenharmony_ci num = 1; 29878c2ecf20Sopenharmony_ci } 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci /* Attempt to connect to the PHY using orion-mdio */ 29908c2ecf20Sopenharmony_ci phydev = ERR_PTR(-ENODEV); 29918c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 29928c2ecf20Sopenharmony_ci int addr = (start + i) & 0x1f; 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, 29958c2ecf20Sopenharmony_ci "orion-mdio-mii", addr); 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_ci phydev = phy_connect(mp->dev, phy_id, mv643xx_eth_adjust_link, 29988c2ecf20Sopenharmony_ci get_phy_mode(mp)); 29998c2ecf20Sopenharmony_ci if (!IS_ERR(phydev)) { 30008c2ecf20Sopenharmony_ci phy_addr_set(mp, addr); 30018c2ecf20Sopenharmony_ci break; 30028c2ecf20Sopenharmony_ci } 30038c2ecf20Sopenharmony_ci } 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci return phydev; 30068c2ecf20Sopenharmony_ci} 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_cistatic void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex) 30098c2ecf20Sopenharmony_ci{ 30108c2ecf20Sopenharmony_ci struct net_device *dev = mp->dev; 30118c2ecf20Sopenharmony_ci struct phy_device *phy = dev->phydev; 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci if (speed == 0) { 30148c2ecf20Sopenharmony_ci phy->autoneg = AUTONEG_ENABLE; 30158c2ecf20Sopenharmony_ci phy->speed = 0; 30168c2ecf20Sopenharmony_ci phy->duplex = 0; 30178c2ecf20Sopenharmony_ci linkmode_copy(phy->advertising, phy->supported); 30188c2ecf20Sopenharmony_ci linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, 30198c2ecf20Sopenharmony_ci phy->advertising); 30208c2ecf20Sopenharmony_ci } else { 30218c2ecf20Sopenharmony_ci phy->autoneg = AUTONEG_DISABLE; 30228c2ecf20Sopenharmony_ci linkmode_zero(phy->advertising); 30238c2ecf20Sopenharmony_ci phy->speed = speed; 30248c2ecf20Sopenharmony_ci phy->duplex = duplex; 30258c2ecf20Sopenharmony_ci } 30268c2ecf20Sopenharmony_ci phy_start_aneg(phy); 30278c2ecf20Sopenharmony_ci} 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_cistatic void init_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) 30308c2ecf20Sopenharmony_ci{ 30318c2ecf20Sopenharmony_ci struct net_device *dev = mp->dev; 30328c2ecf20Sopenharmony_ci u32 pscr; 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_ci pscr = rdlp(mp, PORT_SERIAL_CONTROL); 30358c2ecf20Sopenharmony_ci if (pscr & SERIAL_PORT_ENABLE) { 30368c2ecf20Sopenharmony_ci pscr &= ~SERIAL_PORT_ENABLE; 30378c2ecf20Sopenharmony_ci wrlp(mp, PORT_SERIAL_CONTROL, pscr); 30388c2ecf20Sopenharmony_ci } 30398c2ecf20Sopenharmony_ci 30408c2ecf20Sopenharmony_ci pscr = MAX_RX_PACKET_9700BYTE | SERIAL_PORT_CONTROL_RESERVED; 30418c2ecf20Sopenharmony_ci if (!dev->phydev) { 30428c2ecf20Sopenharmony_ci pscr |= DISABLE_AUTO_NEG_SPEED_GMII; 30438c2ecf20Sopenharmony_ci if (speed == SPEED_1000) 30448c2ecf20Sopenharmony_ci pscr |= SET_GMII_SPEED_TO_1000; 30458c2ecf20Sopenharmony_ci else if (speed == SPEED_100) 30468c2ecf20Sopenharmony_ci pscr |= SET_MII_SPEED_TO_100; 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci pscr |= DISABLE_AUTO_NEG_FOR_FLOW_CTRL; 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci pscr |= DISABLE_AUTO_NEG_FOR_DUPLEX; 30518c2ecf20Sopenharmony_ci if (duplex == DUPLEX_FULL) 30528c2ecf20Sopenharmony_ci pscr |= SET_FULL_DUPLEX_MODE; 30538c2ecf20Sopenharmony_ci } 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ci wrlp(mp, PORT_SERIAL_CONTROL, pscr); 30568c2ecf20Sopenharmony_ci} 30578c2ecf20Sopenharmony_ci 30588c2ecf20Sopenharmony_cistatic const struct net_device_ops mv643xx_eth_netdev_ops = { 30598c2ecf20Sopenharmony_ci .ndo_open = mv643xx_eth_open, 30608c2ecf20Sopenharmony_ci .ndo_stop = mv643xx_eth_stop, 30618c2ecf20Sopenharmony_ci .ndo_start_xmit = mv643xx_eth_xmit, 30628c2ecf20Sopenharmony_ci .ndo_set_rx_mode = mv643xx_eth_set_rx_mode, 30638c2ecf20Sopenharmony_ci .ndo_set_mac_address = mv643xx_eth_set_mac_address, 30648c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 30658c2ecf20Sopenharmony_ci .ndo_do_ioctl = mv643xx_eth_ioctl, 30668c2ecf20Sopenharmony_ci .ndo_change_mtu = mv643xx_eth_change_mtu, 30678c2ecf20Sopenharmony_ci .ndo_set_features = mv643xx_eth_set_features, 30688c2ecf20Sopenharmony_ci .ndo_tx_timeout = mv643xx_eth_tx_timeout, 30698c2ecf20Sopenharmony_ci .ndo_get_stats = mv643xx_eth_get_stats, 30708c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 30718c2ecf20Sopenharmony_ci .ndo_poll_controller = mv643xx_eth_netpoll, 30728c2ecf20Sopenharmony_ci#endif 30738c2ecf20Sopenharmony_ci}; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_cistatic int mv643xx_eth_probe(struct platform_device *pdev) 30768c2ecf20Sopenharmony_ci{ 30778c2ecf20Sopenharmony_ci struct mv643xx_eth_platform_data *pd; 30788c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp; 30798c2ecf20Sopenharmony_ci struct net_device *dev; 30808c2ecf20Sopenharmony_ci struct phy_device *phydev = NULL; 30818c2ecf20Sopenharmony_ci struct resource *res; 30828c2ecf20Sopenharmony_ci int err; 30838c2ecf20Sopenharmony_ci 30848c2ecf20Sopenharmony_ci pd = dev_get_platdata(&pdev->dev); 30858c2ecf20Sopenharmony_ci if (pd == NULL) { 30868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no mv643xx_eth_platform_data\n"); 30878c2ecf20Sopenharmony_ci return -ENODEV; 30888c2ecf20Sopenharmony_ci } 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ci if (pd->shared == NULL) { 30918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no mv643xx_eth_platform_data->shared\n"); 30928c2ecf20Sopenharmony_ci return -ENODEV; 30938c2ecf20Sopenharmony_ci } 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci dev = alloc_etherdev_mq(sizeof(struct mv643xx_eth_private), 8); 30968c2ecf20Sopenharmony_ci if (!dev) 30978c2ecf20Sopenharmony_ci return -ENOMEM; 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 31008c2ecf20Sopenharmony_ci mp = netdev_priv(dev); 31018c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mp); 31028c2ecf20Sopenharmony_ci 31038c2ecf20Sopenharmony_ci mp->shared = platform_get_drvdata(pd->shared); 31048c2ecf20Sopenharmony_ci mp->base = mp->shared->base + 0x0400 + (pd->port_number << 10); 31058c2ecf20Sopenharmony_ci mp->port_num = pd->port_number; 31068c2ecf20Sopenharmony_ci 31078c2ecf20Sopenharmony_ci mp->dev = dev; 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci /* Kirkwood resets some registers on gated clocks. Especially 31108c2ecf20Sopenharmony_ci * CLK125_BYPASS_EN must be cleared but is not available on 31118c2ecf20Sopenharmony_ci * all other SoCs/System Controllers using this driver. 31128c2ecf20Sopenharmony_ci */ 31138c2ecf20Sopenharmony_ci if (of_device_is_compatible(pdev->dev.of_node, 31148c2ecf20Sopenharmony_ci "marvell,kirkwood-eth-port")) 31158c2ecf20Sopenharmony_ci wrlp(mp, PORT_SERIAL_CONTROL1, 31168c2ecf20Sopenharmony_ci rdlp(mp, PORT_SERIAL_CONTROL1) & ~CLK125_BYPASS_EN); 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci /* 31198c2ecf20Sopenharmony_ci * Start with a default rate, and if there is a clock, allow 31208c2ecf20Sopenharmony_ci * it to override the default. 31218c2ecf20Sopenharmony_ci */ 31228c2ecf20Sopenharmony_ci mp->t_clk = 133000000; 31238c2ecf20Sopenharmony_ci mp->clk = devm_clk_get(&pdev->dev, NULL); 31248c2ecf20Sopenharmony_ci if (!IS_ERR(mp->clk)) { 31258c2ecf20Sopenharmony_ci clk_prepare_enable(mp->clk); 31268c2ecf20Sopenharmony_ci mp->t_clk = clk_get_rate(mp->clk); 31278c2ecf20Sopenharmony_ci } else if (!IS_ERR(mp->shared->clk)) { 31288c2ecf20Sopenharmony_ci mp->t_clk = clk_get_rate(mp->shared->clk); 31298c2ecf20Sopenharmony_ci } 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci set_params(mp, pd); 31328c2ecf20Sopenharmony_ci netif_set_real_num_tx_queues(dev, mp->txq_count); 31338c2ecf20Sopenharmony_ci netif_set_real_num_rx_queues(dev, mp->rxq_count); 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci err = 0; 31368c2ecf20Sopenharmony_ci if (pd->phy_node) { 31378c2ecf20Sopenharmony_ci phydev = of_phy_connect(mp->dev, pd->phy_node, 31388c2ecf20Sopenharmony_ci mv643xx_eth_adjust_link, 0, 31398c2ecf20Sopenharmony_ci get_phy_mode(mp)); 31408c2ecf20Sopenharmony_ci if (!phydev) 31418c2ecf20Sopenharmony_ci err = -ENODEV; 31428c2ecf20Sopenharmony_ci else 31438c2ecf20Sopenharmony_ci phy_addr_set(mp, phydev->mdio.addr); 31448c2ecf20Sopenharmony_ci } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { 31458c2ecf20Sopenharmony_ci phydev = phy_scan(mp, pd->phy_addr); 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci if (IS_ERR(phydev)) 31488c2ecf20Sopenharmony_ci err = PTR_ERR(phydev); 31498c2ecf20Sopenharmony_ci else 31508c2ecf20Sopenharmony_ci phy_init(mp, pd->speed, pd->duplex); 31518c2ecf20Sopenharmony_ci } 31528c2ecf20Sopenharmony_ci if (err == -ENODEV) { 31538c2ecf20Sopenharmony_ci err = -EPROBE_DEFER; 31548c2ecf20Sopenharmony_ci goto out; 31558c2ecf20Sopenharmony_ci } 31568c2ecf20Sopenharmony_ci if (err) 31578c2ecf20Sopenharmony_ci goto out; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_ci dev->ethtool_ops = &mv643xx_eth_ethtool_ops; 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci init_pscr(mp, pd->speed, pd->duplex); 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci mib_counters_clear(mp); 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci timer_setup(&mp->mib_counters_timer, mib_counters_timer_wrapper, 0); 31678c2ecf20Sopenharmony_ci mp->mib_counters_timer.expires = jiffies + 30 * HZ; 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci spin_lock_init(&mp->mib_counters_lock); 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci INIT_WORK(&mp->tx_timeout_task, tx_timeout_task); 31728c2ecf20Sopenharmony_ci 31738c2ecf20Sopenharmony_ci netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, NAPI_POLL_WEIGHT); 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci timer_setup(&mp->rx_oom, oom_timer_wrapper, 0); 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 31798c2ecf20Sopenharmony_ci BUG_ON(!res); 31808c2ecf20Sopenharmony_ci dev->irq = res->start; 31818c2ecf20Sopenharmony_ci 31828c2ecf20Sopenharmony_ci dev->netdev_ops = &mv643xx_eth_netdev_ops; 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci dev->watchdog_timeo = 2 * HZ; 31858c2ecf20Sopenharmony_ci dev->base_addr = 0; 31868c2ecf20Sopenharmony_ci 31878c2ecf20Sopenharmony_ci dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; 31888c2ecf20Sopenharmony_ci dev->vlan_features = dev->features; 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci dev->features |= NETIF_F_RXCSUM; 31918c2ecf20Sopenharmony_ci dev->hw_features = dev->features; 31928c2ecf20Sopenharmony_ci 31938c2ecf20Sopenharmony_ci dev->priv_flags |= IFF_UNICAST_FLT; 31948c2ecf20Sopenharmony_ci dev->gso_max_segs = MV643XX_MAX_TSO_SEGS; 31958c2ecf20Sopenharmony_ci 31968c2ecf20Sopenharmony_ci /* MTU range: 64 - 9500 */ 31978c2ecf20Sopenharmony_ci dev->min_mtu = 64; 31988c2ecf20Sopenharmony_ci dev->max_mtu = 9500; 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci if (mp->shared->win_protect) 32018c2ecf20Sopenharmony_ci wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect); 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci netif_carrier_off(dev); 32048c2ecf20Sopenharmony_ci 32058c2ecf20Sopenharmony_ci wrlp(mp, SDMA_CONFIG, PORT_SDMA_CONFIG_DEFAULT_VALUE); 32068c2ecf20Sopenharmony_ci 32078c2ecf20Sopenharmony_ci set_rx_coal(mp, 250); 32088c2ecf20Sopenharmony_ci set_tx_coal(mp, 0); 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci err = register_netdev(dev); 32118c2ecf20Sopenharmony_ci if (err) 32128c2ecf20Sopenharmony_ci goto out; 32138c2ecf20Sopenharmony_ci 32148c2ecf20Sopenharmony_ci netdev_notice(dev, "port %d with MAC address %pM\n", 32158c2ecf20Sopenharmony_ci mp->port_num, dev->dev_addr); 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_ci if (mp->tx_desc_sram_size > 0) 32188c2ecf20Sopenharmony_ci netdev_notice(dev, "configured with sram\n"); 32198c2ecf20Sopenharmony_ci 32208c2ecf20Sopenharmony_ci return 0; 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ciout: 32238c2ecf20Sopenharmony_ci if (!IS_ERR(mp->clk)) 32248c2ecf20Sopenharmony_ci clk_disable_unprepare(mp->clk); 32258c2ecf20Sopenharmony_ci free_netdev(dev); 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci return err; 32288c2ecf20Sopenharmony_ci} 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_cistatic int mv643xx_eth_remove(struct platform_device *pdev) 32318c2ecf20Sopenharmony_ci{ 32328c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); 32338c2ecf20Sopenharmony_ci struct net_device *dev = mp->dev; 32348c2ecf20Sopenharmony_ci 32358c2ecf20Sopenharmony_ci unregister_netdev(mp->dev); 32368c2ecf20Sopenharmony_ci if (dev->phydev) 32378c2ecf20Sopenharmony_ci phy_disconnect(dev->phydev); 32388c2ecf20Sopenharmony_ci cancel_work_sync(&mp->tx_timeout_task); 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci if (!IS_ERR(mp->clk)) 32418c2ecf20Sopenharmony_ci clk_disable_unprepare(mp->clk); 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci free_netdev(mp->dev); 32448c2ecf20Sopenharmony_ci 32458c2ecf20Sopenharmony_ci return 0; 32468c2ecf20Sopenharmony_ci} 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_cistatic void mv643xx_eth_shutdown(struct platform_device *pdev) 32498c2ecf20Sopenharmony_ci{ 32508c2ecf20Sopenharmony_ci struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci /* Mask all interrupts on ethernet port */ 32538c2ecf20Sopenharmony_ci wrlp(mp, INT_MASK, 0); 32548c2ecf20Sopenharmony_ci rdlp(mp, INT_MASK); 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci if (netif_running(mp->dev)) 32578c2ecf20Sopenharmony_ci port_reset(mp); 32588c2ecf20Sopenharmony_ci} 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_cistatic struct platform_driver mv643xx_eth_driver = { 32618c2ecf20Sopenharmony_ci .probe = mv643xx_eth_probe, 32628c2ecf20Sopenharmony_ci .remove = mv643xx_eth_remove, 32638c2ecf20Sopenharmony_ci .shutdown = mv643xx_eth_shutdown, 32648c2ecf20Sopenharmony_ci .driver = { 32658c2ecf20Sopenharmony_ci .name = MV643XX_ETH_NAME, 32668c2ecf20Sopenharmony_ci }, 32678c2ecf20Sopenharmony_ci}; 32688c2ecf20Sopenharmony_ci 32698c2ecf20Sopenharmony_cistatic struct platform_driver * const drivers[] = { 32708c2ecf20Sopenharmony_ci &mv643xx_eth_shared_driver, 32718c2ecf20Sopenharmony_ci &mv643xx_eth_driver, 32728c2ecf20Sopenharmony_ci}; 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_cistatic int __init mv643xx_eth_init_module(void) 32758c2ecf20Sopenharmony_ci{ 32768c2ecf20Sopenharmony_ci return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 32778c2ecf20Sopenharmony_ci} 32788c2ecf20Sopenharmony_cimodule_init(mv643xx_eth_init_module); 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_cistatic void __exit mv643xx_eth_cleanup_module(void) 32818c2ecf20Sopenharmony_ci{ 32828c2ecf20Sopenharmony_ci platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 32838c2ecf20Sopenharmony_ci} 32848c2ecf20Sopenharmony_cimodule_exit(mv643xx_eth_cleanup_module); 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, " 32878c2ecf20Sopenharmony_ci "Manish Lachwani, Dale Farnsworth and Lennert Buytenhek"); 32888c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Ethernet driver for Marvell MV643XX"); 32898c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 32908c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" MV643XX_ETH_SHARED_NAME); 32918c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" MV643XX_ETH_NAME); 3292