18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2009-2012 Cavium, Inc 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/capability.h> 138c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 178c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 188c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of_net.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/phy.h> 248c2ecf20Sopenharmony_ci#include <linux/io.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/octeon/octeon.h> 278c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-mixx-defs.h> 288c2ecf20Sopenharmony_ci#include <asm/octeon/cvmx-agl-defs.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define DRV_NAME "octeon_mgmt" 318c2ecf20Sopenharmony_ci#define DRV_DESCRIPTION \ 328c2ecf20Sopenharmony_ci "Cavium Networks Octeon MII (management) port Network Driver" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define OCTEON_MGMT_NAPI_WEIGHT 16 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* Ring sizes that are powers of two allow for more efficient modulo 378c2ecf20Sopenharmony_ci * opertions. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci#define OCTEON_MGMT_RX_RING_SIZE 512 408c2ecf20Sopenharmony_ci#define OCTEON_MGMT_TX_RING_SIZE 128 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* Allow 8 bytes for vlan and FCS. */ 438c2ecf20Sopenharmony_ci#define OCTEON_MGMT_RX_HEADROOM (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ciunion mgmt_port_ring_entry { 468c2ecf20Sopenharmony_ci u64 d64; 478c2ecf20Sopenharmony_ci struct { 488c2ecf20Sopenharmony_ci#define RING_ENTRY_CODE_DONE 0xf 498c2ecf20Sopenharmony_ci#define RING_ENTRY_CODE_MORE 0x10 508c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN_BITFIELD 518c2ecf20Sopenharmony_ci u64 reserved_62_63:2; 528c2ecf20Sopenharmony_ci /* Length of the buffer/packet in bytes */ 538c2ecf20Sopenharmony_ci u64 len:14; 548c2ecf20Sopenharmony_ci /* For TX, signals that the packet should be timestamped */ 558c2ecf20Sopenharmony_ci u64 tstamp:1; 568c2ecf20Sopenharmony_ci /* The RX error code */ 578c2ecf20Sopenharmony_ci u64 code:7; 588c2ecf20Sopenharmony_ci /* Physical address of the buffer */ 598c2ecf20Sopenharmony_ci u64 addr:40; 608c2ecf20Sopenharmony_ci#else 618c2ecf20Sopenharmony_ci u64 addr:40; 628c2ecf20Sopenharmony_ci u64 code:7; 638c2ecf20Sopenharmony_ci u64 tstamp:1; 648c2ecf20Sopenharmony_ci u64 len:14; 658c2ecf20Sopenharmony_ci u64 reserved_62_63:2; 668c2ecf20Sopenharmony_ci#endif 678c2ecf20Sopenharmony_ci } s; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define MIX_ORING1 0x0 718c2ecf20Sopenharmony_ci#define MIX_ORING2 0x8 728c2ecf20Sopenharmony_ci#define MIX_IRING1 0x10 738c2ecf20Sopenharmony_ci#define MIX_IRING2 0x18 748c2ecf20Sopenharmony_ci#define MIX_CTL 0x20 758c2ecf20Sopenharmony_ci#define MIX_IRHWM 0x28 768c2ecf20Sopenharmony_ci#define MIX_IRCNT 0x30 778c2ecf20Sopenharmony_ci#define MIX_ORHWM 0x38 788c2ecf20Sopenharmony_ci#define MIX_ORCNT 0x40 798c2ecf20Sopenharmony_ci#define MIX_ISR 0x48 808c2ecf20Sopenharmony_ci#define MIX_INTENA 0x50 818c2ecf20Sopenharmony_ci#define MIX_REMCNT 0x58 828c2ecf20Sopenharmony_ci#define MIX_BIST 0x78 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define AGL_GMX_PRT_CFG 0x10 858c2ecf20Sopenharmony_ci#define AGL_GMX_RX_FRM_CTL 0x18 868c2ecf20Sopenharmony_ci#define AGL_GMX_RX_FRM_MAX 0x30 878c2ecf20Sopenharmony_ci#define AGL_GMX_RX_JABBER 0x38 888c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_CTL 0x50 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_PKTS_DRP 0xb0 918c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_OCTS_DRP 0xb8 928c2ecf20Sopenharmony_ci#define AGL_GMX_RX_STATS_PKTS_BAD 0xc0 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CTL 0x100 958c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM_EN 0x108 968c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM0 0x180 978c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM1 0x188 988c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM2 0x190 998c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM3 0x198 1008c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM4 0x1a0 1018c2ecf20Sopenharmony_ci#define AGL_GMX_RX_ADR_CAM5 0x1a8 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define AGL_GMX_TX_CLK 0x208 1048c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STATS_CTL 0x268 1058c2ecf20Sopenharmony_ci#define AGL_GMX_TX_CTL 0x270 1068c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT0 0x280 1078c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT1 0x288 1088c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT2 0x290 1098c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT3 0x298 1108c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT4 0x2a0 1118c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT5 0x2a8 1128c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT6 0x2b0 1138c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT7 0x2b8 1148c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT8 0x2c0 1158c2ecf20Sopenharmony_ci#define AGL_GMX_TX_STAT9 0x2c8 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct octeon_mgmt { 1188c2ecf20Sopenharmony_ci struct net_device *netdev; 1198c2ecf20Sopenharmony_ci u64 mix; 1208c2ecf20Sopenharmony_ci u64 agl; 1218c2ecf20Sopenharmony_ci u64 agl_prt_ctl; 1228c2ecf20Sopenharmony_ci int port; 1238c2ecf20Sopenharmony_ci int irq; 1248c2ecf20Sopenharmony_ci bool has_rx_tstamp; 1258c2ecf20Sopenharmony_ci u64 *tx_ring; 1268c2ecf20Sopenharmony_ci dma_addr_t tx_ring_handle; 1278c2ecf20Sopenharmony_ci unsigned int tx_next; 1288c2ecf20Sopenharmony_ci unsigned int tx_next_clean; 1298c2ecf20Sopenharmony_ci unsigned int tx_current_fill; 1308c2ecf20Sopenharmony_ci /* The tx_list lock also protects the ring related variables */ 1318c2ecf20Sopenharmony_ci struct sk_buff_head tx_list; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* RX variables only touched in napi_poll. No locking necessary. */ 1348c2ecf20Sopenharmony_ci u64 *rx_ring; 1358c2ecf20Sopenharmony_ci dma_addr_t rx_ring_handle; 1368c2ecf20Sopenharmony_ci unsigned int rx_next; 1378c2ecf20Sopenharmony_ci unsigned int rx_next_fill; 1388c2ecf20Sopenharmony_ci unsigned int rx_current_fill; 1398c2ecf20Sopenharmony_ci struct sk_buff_head rx_list; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spinlock_t lock; 1428c2ecf20Sopenharmony_ci unsigned int last_duplex; 1438c2ecf20Sopenharmony_ci unsigned int last_link; 1448c2ecf20Sopenharmony_ci unsigned int last_speed; 1458c2ecf20Sopenharmony_ci struct device *dev; 1468c2ecf20Sopenharmony_ci struct napi_struct napi; 1478c2ecf20Sopenharmony_ci struct tasklet_struct tx_clean_tasklet; 1488c2ecf20Sopenharmony_ci struct device_node *phy_np; 1498c2ecf20Sopenharmony_ci resource_size_t mix_phys; 1508c2ecf20Sopenharmony_ci resource_size_t mix_size; 1518c2ecf20Sopenharmony_ci resource_size_t agl_phys; 1528c2ecf20Sopenharmony_ci resource_size_t agl_size; 1538c2ecf20Sopenharmony_ci resource_size_t agl_prt_ctl_phys; 1548c2ecf20Sopenharmony_ci resource_size_t agl_prt_ctl_size; 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void octeon_mgmt_set_rx_irq(struct octeon_mgmt *p, int enable) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci union cvmx_mixx_intena mix_intena; 1608c2ecf20Sopenharmony_ci unsigned long flags; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->lock, flags); 1638c2ecf20Sopenharmony_ci mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA); 1648c2ecf20Sopenharmony_ci mix_intena.s.ithena = enable ? 1 : 0; 1658c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64); 1668c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->lock, flags); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void octeon_mgmt_set_tx_irq(struct octeon_mgmt *p, int enable) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci union cvmx_mixx_intena mix_intena; 1728c2ecf20Sopenharmony_ci unsigned long flags; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->lock, flags); 1758c2ecf20Sopenharmony_ci mix_intena.u64 = cvmx_read_csr(p->mix + MIX_INTENA); 1768c2ecf20Sopenharmony_ci mix_intena.s.othena = enable ? 1 : 0; 1778c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64); 1788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->lock, flags); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void octeon_mgmt_enable_rx_irq(struct octeon_mgmt *p) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci octeon_mgmt_set_rx_irq(p, 1); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void octeon_mgmt_disable_rx_irq(struct octeon_mgmt *p) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci octeon_mgmt_set_rx_irq(p, 0); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void octeon_mgmt_enable_tx_irq(struct octeon_mgmt *p) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci octeon_mgmt_set_tx_irq(p, 1); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void octeon_mgmt_disable_tx_irq(struct octeon_mgmt *p) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci octeon_mgmt_set_tx_irq(p, 0); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic unsigned int ring_max_fill(unsigned int ring_size) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci return ring_size - 8; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic unsigned int ring_size_to_bytes(unsigned int ring_size) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci return ring_size * sizeof(union mgmt_port_ring_entry); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void octeon_mgmt_rx_fill_ring(struct net_device *netdev) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci while (p->rx_current_fill < ring_max_fill(OCTEON_MGMT_RX_RING_SIZE)) { 2168c2ecf20Sopenharmony_ci unsigned int size; 2178c2ecf20Sopenharmony_ci union mgmt_port_ring_entry re; 2188c2ecf20Sopenharmony_ci struct sk_buff *skb; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* CN56XX pass 1 needs 8 bytes of padding. */ 2218c2ecf20Sopenharmony_ci size = netdev->mtu + OCTEON_MGMT_RX_HEADROOM + 8 + NET_IP_ALIGN; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(netdev, size); 2248c2ecf20Sopenharmony_ci if (!skb) 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci skb_reserve(skb, NET_IP_ALIGN); 2278c2ecf20Sopenharmony_ci __skb_queue_tail(&p->rx_list, skb); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci re.d64 = 0; 2308c2ecf20Sopenharmony_ci re.s.len = size; 2318c2ecf20Sopenharmony_ci re.s.addr = dma_map_single(p->dev, skb->data, 2328c2ecf20Sopenharmony_ci size, 2338c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Put it in the ring. */ 2368c2ecf20Sopenharmony_ci p->rx_ring[p->rx_next_fill] = re.d64; 2378c2ecf20Sopenharmony_ci /* Make sure there is no reorder of filling the ring and ringing 2388c2ecf20Sopenharmony_ci * the bell 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci wmb(); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci dma_sync_single_for_device(p->dev, p->rx_ring_handle, 2438c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), 2448c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 2458c2ecf20Sopenharmony_ci p->rx_next_fill = 2468c2ecf20Sopenharmony_ci (p->rx_next_fill + 1) % OCTEON_MGMT_RX_RING_SIZE; 2478c2ecf20Sopenharmony_ci p->rx_current_fill++; 2488c2ecf20Sopenharmony_ci /* Ring the bell. */ 2498c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_IRING2, 1); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic void octeon_mgmt_clean_tx_buffers(struct octeon_mgmt *p) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci union cvmx_mixx_orcnt mix_orcnt; 2568c2ecf20Sopenharmony_ci union mgmt_port_ring_entry re; 2578c2ecf20Sopenharmony_ci struct sk_buff *skb; 2588c2ecf20Sopenharmony_ci int cleaned = 0; 2598c2ecf20Sopenharmony_ci unsigned long flags; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT); 2628c2ecf20Sopenharmony_ci while (mix_orcnt.s.orcnt) { 2638c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->tx_list.lock, flags); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (mix_orcnt.s.orcnt == 0) { 2688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->tx_list.lock, flags); 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(p->dev, p->tx_ring_handle, 2738c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), 2748c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci re.d64 = p->tx_ring[p->tx_next_clean]; 2778c2ecf20Sopenharmony_ci p->tx_next_clean = 2788c2ecf20Sopenharmony_ci (p->tx_next_clean + 1) % OCTEON_MGMT_TX_RING_SIZE; 2798c2ecf20Sopenharmony_ci skb = __skb_dequeue(&p->tx_list); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mix_orcnt.u64 = 0; 2828c2ecf20Sopenharmony_ci mix_orcnt.s.orcnt = 1; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Acknowledge to hardware that we have the buffer. */ 2858c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_ORCNT, mix_orcnt.u64); 2868c2ecf20Sopenharmony_ci p->tx_current_fill--; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->tx_list.lock, flags); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci dma_unmap_single(p->dev, re.s.addr, re.s.len, 2918c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci /* Read the hardware TX timestamp if one was recorded */ 2948c2ecf20Sopenharmony_ci if (unlikely(re.s.tstamp)) { 2958c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps ts; 2968c2ecf20Sopenharmony_ci u64 ns; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci memset(&ts, 0, sizeof(ts)); 2998c2ecf20Sopenharmony_ci /* Read the timestamp */ 3008c2ecf20Sopenharmony_ci ns = cvmx_read_csr(CVMX_MIXX_TSTAMP(p->port)); 3018c2ecf20Sopenharmony_ci /* Remove the timestamp from the FIFO */ 3028c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_MIXX_TSCTL(p->port), 0); 3038c2ecf20Sopenharmony_ci /* Tell the kernel about the timestamp */ 3048c2ecf20Sopenharmony_ci ts.hwtstamp = ns_to_ktime(ns); 3058c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, &ts); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 3098c2ecf20Sopenharmony_ci cleaned++; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mix_orcnt.u64 = cvmx_read_csr(p->mix + MIX_ORCNT); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (cleaned && netif_queue_stopped(p->netdev)) 3158c2ecf20Sopenharmony_ci netif_wake_queue(p->netdev); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void octeon_mgmt_clean_tx_tasklet(struct tasklet_struct *t) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct octeon_mgmt *p = from_tasklet(p, t, tx_clean_tasklet); 3218c2ecf20Sopenharmony_ci octeon_mgmt_clean_tx_buffers(p); 3228c2ecf20Sopenharmony_ci octeon_mgmt_enable_tx_irq(p); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void octeon_mgmt_update_rx_stats(struct net_device *netdev) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 3288c2ecf20Sopenharmony_ci unsigned long flags; 3298c2ecf20Sopenharmony_ci u64 drop, bad; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* These reads also clear the count registers. */ 3328c2ecf20Sopenharmony_ci drop = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP); 3338c2ecf20Sopenharmony_ci bad = cvmx_read_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (drop || bad) { 3368c2ecf20Sopenharmony_ci /* Do an atomic update. */ 3378c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->lock, flags); 3388c2ecf20Sopenharmony_ci netdev->stats.rx_errors += bad; 3398c2ecf20Sopenharmony_ci netdev->stats.rx_dropped += drop; 3408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->lock, flags); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void octeon_mgmt_update_tx_stats(struct net_device *netdev) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 3478c2ecf20Sopenharmony_ci unsigned long flags; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci union cvmx_agl_gmx_txx_stat0 s0; 3508c2ecf20Sopenharmony_ci union cvmx_agl_gmx_txx_stat1 s1; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* These reads also clear the count registers. */ 3538c2ecf20Sopenharmony_ci s0.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT0); 3548c2ecf20Sopenharmony_ci s1.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_STAT1); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (s0.s.xsdef || s0.s.xscol || s1.s.scol || s1.s.mcol) { 3578c2ecf20Sopenharmony_ci /* Do an atomic update. */ 3588c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->lock, flags); 3598c2ecf20Sopenharmony_ci netdev->stats.tx_errors += s0.s.xsdef + s0.s.xscol; 3608c2ecf20Sopenharmony_ci netdev->stats.collisions += s1.s.scol + s1.s.mcol; 3618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->lock, flags); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/* 3668c2ecf20Sopenharmony_ci * Dequeue a receive skb and its corresponding ring entry. The ring 3678c2ecf20Sopenharmony_ci * entry is returned, *pskb is updated to point to the skb. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_cistatic u64 octeon_mgmt_dequeue_rx_buffer(struct octeon_mgmt *p, 3708c2ecf20Sopenharmony_ci struct sk_buff **pskb) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci union mgmt_port_ring_entry re; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(p->dev, p->rx_ring_handle, 3758c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), 3768c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci re.d64 = p->rx_ring[p->rx_next]; 3798c2ecf20Sopenharmony_ci p->rx_next = (p->rx_next + 1) % OCTEON_MGMT_RX_RING_SIZE; 3808c2ecf20Sopenharmony_ci p->rx_current_fill--; 3818c2ecf20Sopenharmony_ci *pskb = __skb_dequeue(&p->rx_list); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci dma_unmap_single(p->dev, re.s.addr, 3848c2ecf20Sopenharmony_ci ETH_FRAME_LEN + OCTEON_MGMT_RX_HEADROOM, 3858c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci return re.d64; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int octeon_mgmt_receive_one(struct octeon_mgmt *p) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct net_device *netdev = p->netdev; 3948c2ecf20Sopenharmony_ci union cvmx_mixx_ircnt mix_ircnt; 3958c2ecf20Sopenharmony_ci union mgmt_port_ring_entry re; 3968c2ecf20Sopenharmony_ci struct sk_buff *skb; 3978c2ecf20Sopenharmony_ci struct sk_buff *skb2; 3988c2ecf20Sopenharmony_ci struct sk_buff *skb_new; 3998c2ecf20Sopenharmony_ci union mgmt_port_ring_entry re2; 4008c2ecf20Sopenharmony_ci int rc = 1; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci re.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb); 4048c2ecf20Sopenharmony_ci if (likely(re.s.code == RING_ENTRY_CODE_DONE)) { 4058c2ecf20Sopenharmony_ci /* A good packet, send it up. */ 4068c2ecf20Sopenharmony_ci skb_put(skb, re.s.len); 4078c2ecf20Sopenharmony_cigood: 4088c2ecf20Sopenharmony_ci /* Process the RX timestamp if it was recorded */ 4098c2ecf20Sopenharmony_ci if (p->has_rx_tstamp) { 4108c2ecf20Sopenharmony_ci /* The first 8 bytes are the timestamp */ 4118c2ecf20Sopenharmony_ci u64 ns = *(u64 *)skb->data; 4128c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *ts; 4138c2ecf20Sopenharmony_ci ts = skb_hwtstamps(skb); 4148c2ecf20Sopenharmony_ci ts->hwtstamp = ns_to_ktime(ns); 4158c2ecf20Sopenharmony_ci __skb_pull(skb, 8); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 4188c2ecf20Sopenharmony_ci netdev->stats.rx_packets++; 4198c2ecf20Sopenharmony_ci netdev->stats.rx_bytes += skb->len; 4208c2ecf20Sopenharmony_ci netif_receive_skb(skb); 4218c2ecf20Sopenharmony_ci rc = 0; 4228c2ecf20Sopenharmony_ci } else if (re.s.code == RING_ENTRY_CODE_MORE) { 4238c2ecf20Sopenharmony_ci /* Packet split across skbs. This can happen if we 4248c2ecf20Sopenharmony_ci * increase the MTU. Buffers that are already in the 4258c2ecf20Sopenharmony_ci * rx ring can then end up being too small. As the rx 4268c2ecf20Sopenharmony_ci * ring is refilled, buffers sized for the new MTU 4278c2ecf20Sopenharmony_ci * will be used and we should go back to the normal 4288c2ecf20Sopenharmony_ci * non-split case. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci skb_put(skb, re.s.len); 4318c2ecf20Sopenharmony_ci do { 4328c2ecf20Sopenharmony_ci re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2); 4338c2ecf20Sopenharmony_ci if (re2.s.code != RING_ENTRY_CODE_MORE 4348c2ecf20Sopenharmony_ci && re2.s.code != RING_ENTRY_CODE_DONE) 4358c2ecf20Sopenharmony_ci goto split_error; 4368c2ecf20Sopenharmony_ci skb_put(skb2, re2.s.len); 4378c2ecf20Sopenharmony_ci skb_new = skb_copy_expand(skb, 0, skb2->len, 4388c2ecf20Sopenharmony_ci GFP_ATOMIC); 4398c2ecf20Sopenharmony_ci if (!skb_new) 4408c2ecf20Sopenharmony_ci goto split_error; 4418c2ecf20Sopenharmony_ci if (skb_copy_bits(skb2, 0, skb_tail_pointer(skb_new), 4428c2ecf20Sopenharmony_ci skb2->len)) 4438c2ecf20Sopenharmony_ci goto split_error; 4448c2ecf20Sopenharmony_ci skb_put(skb_new, skb2->len); 4458c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4468c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb2); 4478c2ecf20Sopenharmony_ci skb = skb_new; 4488c2ecf20Sopenharmony_ci } while (re2.s.code == RING_ENTRY_CODE_MORE); 4498c2ecf20Sopenharmony_ci goto good; 4508c2ecf20Sopenharmony_ci } else { 4518c2ecf20Sopenharmony_ci /* Some other error, discard it. */ 4528c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4538c2ecf20Sopenharmony_ci /* Error statistics are accumulated in 4548c2ecf20Sopenharmony_ci * octeon_mgmt_update_rx_stats. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci goto done; 4588c2ecf20Sopenharmony_cisplit_error: 4598c2ecf20Sopenharmony_ci /* Discard the whole mess. */ 4608c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4618c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb2); 4628c2ecf20Sopenharmony_ci while (re2.s.code == RING_ENTRY_CODE_MORE) { 4638c2ecf20Sopenharmony_ci re2.d64 = octeon_mgmt_dequeue_rx_buffer(p, &skb2); 4648c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb2); 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci netdev->stats.rx_errors++; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cidone: 4698c2ecf20Sopenharmony_ci /* Tell the hardware we processed a packet. */ 4708c2ecf20Sopenharmony_ci mix_ircnt.u64 = 0; 4718c2ecf20Sopenharmony_ci mix_ircnt.s.ircnt = 1; 4728c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_IRCNT, mix_ircnt.u64); 4738c2ecf20Sopenharmony_ci return rc; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic int octeon_mgmt_receive_packets(struct octeon_mgmt *p, int budget) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci unsigned int work_done = 0; 4798c2ecf20Sopenharmony_ci union cvmx_mixx_ircnt mix_ircnt; 4808c2ecf20Sopenharmony_ci int rc; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT); 4838c2ecf20Sopenharmony_ci while (work_done < budget && mix_ircnt.s.ircnt) { 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci rc = octeon_mgmt_receive_one(p); 4868c2ecf20Sopenharmony_ci if (!rc) 4878c2ecf20Sopenharmony_ci work_done++; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Check for more packets. */ 4908c2ecf20Sopenharmony_ci mix_ircnt.u64 = cvmx_read_csr(p->mix + MIX_IRCNT); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci octeon_mgmt_rx_fill_ring(p->netdev); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return work_done; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int octeon_mgmt_napi_poll(struct napi_struct *napi, int budget) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct octeon_mgmt *p = container_of(napi, struct octeon_mgmt, napi); 5018c2ecf20Sopenharmony_ci struct net_device *netdev = p->netdev; 5028c2ecf20Sopenharmony_ci unsigned int work_done = 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci work_done = octeon_mgmt_receive_packets(p, budget); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (work_done < budget) { 5078c2ecf20Sopenharmony_ci /* We stopped because no more packets were available. */ 5088c2ecf20Sopenharmony_ci napi_complete_done(napi, work_done); 5098c2ecf20Sopenharmony_ci octeon_mgmt_enable_rx_irq(p); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci octeon_mgmt_update_rx_stats(netdev); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return work_done; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* Reset the hardware to clean state. */ 5178c2ecf20Sopenharmony_cistatic void octeon_mgmt_reset_hw(struct octeon_mgmt *p) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci union cvmx_mixx_ctl mix_ctl; 5208c2ecf20Sopenharmony_ci union cvmx_mixx_bist mix_bist; 5218c2ecf20Sopenharmony_ci union cvmx_agl_gmx_bist agl_gmx_bist; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci mix_ctl.u64 = 0; 5248c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); 5258c2ecf20Sopenharmony_ci do { 5268c2ecf20Sopenharmony_ci mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL); 5278c2ecf20Sopenharmony_ci } while (mix_ctl.s.busy); 5288c2ecf20Sopenharmony_ci mix_ctl.s.reset = 1; 5298c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); 5308c2ecf20Sopenharmony_ci cvmx_read_csr(p->mix + MIX_CTL); 5318c2ecf20Sopenharmony_ci octeon_io_clk_delay(64); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci mix_bist.u64 = cvmx_read_csr(p->mix + MIX_BIST); 5348c2ecf20Sopenharmony_ci if (mix_bist.u64) 5358c2ecf20Sopenharmony_ci dev_warn(p->dev, "MIX failed BIST (0x%016llx)\n", 5368c2ecf20Sopenharmony_ci (unsigned long long)mix_bist.u64); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci agl_gmx_bist.u64 = cvmx_read_csr(CVMX_AGL_GMX_BIST); 5398c2ecf20Sopenharmony_ci if (agl_gmx_bist.u64) 5408c2ecf20Sopenharmony_ci dev_warn(p->dev, "AGL failed BIST (0x%016llx)\n", 5418c2ecf20Sopenharmony_ci (unsigned long long)agl_gmx_bist.u64); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistruct octeon_mgmt_cam_state { 5458c2ecf20Sopenharmony_ci u64 cam[6]; 5468c2ecf20Sopenharmony_ci u64 cam_mask; 5478c2ecf20Sopenharmony_ci int cam_index; 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void octeon_mgmt_cam_state_add(struct octeon_mgmt_cam_state *cs, 5518c2ecf20Sopenharmony_ci unsigned char *addr) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci int i; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 5568c2ecf20Sopenharmony_ci cs->cam[i] |= (u64)addr[i] << (8 * (cs->cam_index)); 5578c2ecf20Sopenharmony_ci cs->cam_mask |= (1ULL << cs->cam_index); 5588c2ecf20Sopenharmony_ci cs->cam_index++; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic void octeon_mgmt_set_rx_filtering(struct net_device *netdev) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 5648c2ecf20Sopenharmony_ci union cvmx_agl_gmx_rxx_adr_ctl adr_ctl; 5658c2ecf20Sopenharmony_ci union cvmx_agl_gmx_prtx_cfg agl_gmx_prtx; 5668c2ecf20Sopenharmony_ci unsigned long flags; 5678c2ecf20Sopenharmony_ci unsigned int prev_packet_enable; 5688c2ecf20Sopenharmony_ci unsigned int cam_mode = 1; /* 1 - Accept on CAM match */ 5698c2ecf20Sopenharmony_ci unsigned int multicast_mode = 1; /* 1 - Reject all multicast. */ 5708c2ecf20Sopenharmony_ci struct octeon_mgmt_cam_state cam_state; 5718c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 5728c2ecf20Sopenharmony_ci int available_cam_entries; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci memset(&cam_state, 0, sizeof(cam_state)); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if ((netdev->flags & IFF_PROMISC) || netdev->uc.count > 7) { 5778c2ecf20Sopenharmony_ci cam_mode = 0; 5788c2ecf20Sopenharmony_ci available_cam_entries = 8; 5798c2ecf20Sopenharmony_ci } else { 5808c2ecf20Sopenharmony_ci /* One CAM entry for the primary address, leaves seven 5818c2ecf20Sopenharmony_ci * for the secondary addresses. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci available_cam_entries = 7 - netdev->uc.count; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (netdev->flags & IFF_MULTICAST) { 5878c2ecf20Sopenharmony_ci if (cam_mode == 0 || (netdev->flags & IFF_ALLMULTI) || 5888c2ecf20Sopenharmony_ci netdev_mc_count(netdev) > available_cam_entries) 5898c2ecf20Sopenharmony_ci multicast_mode = 2; /* 2 - Accept all multicast. */ 5908c2ecf20Sopenharmony_ci else 5918c2ecf20Sopenharmony_ci multicast_mode = 0; /* 0 - Use CAM. */ 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (cam_mode == 1) { 5958c2ecf20Sopenharmony_ci /* Add primary address. */ 5968c2ecf20Sopenharmony_ci octeon_mgmt_cam_state_add(&cam_state, netdev->dev_addr); 5978c2ecf20Sopenharmony_ci netdev_for_each_uc_addr(ha, netdev) 5988c2ecf20Sopenharmony_ci octeon_mgmt_cam_state_add(&cam_state, ha->addr); 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci if (multicast_mode == 0) { 6018c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) 6028c2ecf20Sopenharmony_ci octeon_mgmt_cam_state_add(&cam_state, ha->addr); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->lock, flags); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Disable packet I/O. */ 6088c2ecf20Sopenharmony_ci agl_gmx_prtx.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); 6098c2ecf20Sopenharmony_ci prev_packet_enable = agl_gmx_prtx.s.en; 6108c2ecf20Sopenharmony_ci agl_gmx_prtx.s.en = 0; 6118c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci adr_ctl.u64 = 0; 6148c2ecf20Sopenharmony_ci adr_ctl.s.cam_mode = cam_mode; 6158c2ecf20Sopenharmony_ci adr_ctl.s.mcst = multicast_mode; 6168c2ecf20Sopenharmony_ci adr_ctl.s.bcst = 1; /* Allow broadcast */ 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CTL, adr_ctl.u64); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM0, cam_state.cam[0]); 6218c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM1, cam_state.cam[1]); 6228c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM2, cam_state.cam[2]); 6238c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM3, cam_state.cam[3]); 6248c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM4, cam_state.cam[4]); 6258c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM5, cam_state.cam[5]); 6268c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_ADR_CAM_EN, cam_state.cam_mask); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* Restore packet I/O. */ 6298c2ecf20Sopenharmony_ci agl_gmx_prtx.s.en = prev_packet_enable; 6308c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, agl_gmx_prtx.u64); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->lock, flags); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic int octeon_mgmt_set_mac_address(struct net_device *netdev, void *addr) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci int r = eth_mac_addr(netdev, addr); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (r) 6408c2ecf20Sopenharmony_ci return r; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci octeon_mgmt_set_rx_filtering(netdev); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int octeon_mgmt_change_mtu(struct net_device *netdev, int new_mtu) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 6508c2ecf20Sopenharmony_ci int max_packet = new_mtu + ETH_HLEN + ETH_FCS_LEN; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci netdev->mtu = new_mtu; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* HW lifts the limit if the frame is VLAN tagged 6558c2ecf20Sopenharmony_ci * (+4 bytes per each tag, up to two tags) 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_MAX, max_packet); 6588c2ecf20Sopenharmony_ci /* Set the hardware to truncate packets larger than the MTU. The jabber 6598c2ecf20Sopenharmony_ci * register must be set to a multiple of 8 bytes, so round up. JABBER is 6608c2ecf20Sopenharmony_ci * an unconditional limit, so we need to account for two possible VLAN 6618c2ecf20Sopenharmony_ci * tags. 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_JABBER, 6648c2ecf20Sopenharmony_ci (max_packet + 7 + VLAN_HLEN * 2) & 0xfff8); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct net_device *netdev = dev_id; 6728c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 6738c2ecf20Sopenharmony_ci union cvmx_mixx_isr mixx_isr; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci mixx_isr.u64 = cvmx_read_csr(p->mix + MIX_ISR); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* Clear any pending interrupts */ 6788c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_ISR, mixx_isr.u64); 6798c2ecf20Sopenharmony_ci cvmx_read_csr(p->mix + MIX_ISR); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (mixx_isr.s.irthresh) { 6828c2ecf20Sopenharmony_ci octeon_mgmt_disable_rx_irq(p); 6838c2ecf20Sopenharmony_ci napi_schedule(&p->napi); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci if (mixx_isr.s.orthresh) { 6868c2ecf20Sopenharmony_ci octeon_mgmt_disable_tx_irq(p); 6878c2ecf20Sopenharmony_ci tasklet_schedule(&p->tx_clean_tasklet); 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev, 6948c2ecf20Sopenharmony_ci struct ifreq *rq, int cmd) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 6978c2ecf20Sopenharmony_ci struct hwtstamp_config config; 6988c2ecf20Sopenharmony_ci union cvmx_mio_ptp_clock_cfg ptp; 6998c2ecf20Sopenharmony_ci union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl; 7008c2ecf20Sopenharmony_ci bool have_hw_timestamps = false; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (copy_from_user(&config, rq->ifr_data, sizeof(config))) 7038c2ecf20Sopenharmony_ci return -EFAULT; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (config.flags) /* reserved for future extensions */ 7068c2ecf20Sopenharmony_ci return -EINVAL; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Check the status of hardware for tiemstamps */ 7098c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 7108c2ecf20Sopenharmony_ci /* Get the current state of the PTP clock */ 7118c2ecf20Sopenharmony_ci ptp.u64 = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_CFG); 7128c2ecf20Sopenharmony_ci if (!ptp.s.ext_clk_en) { 7138c2ecf20Sopenharmony_ci /* The clock has not been configured to use an 7148c2ecf20Sopenharmony_ci * external source. Program it to use the main clock 7158c2ecf20Sopenharmony_ci * reference. 7168c2ecf20Sopenharmony_ci */ 7178c2ecf20Sopenharmony_ci u64 clock_comp = (NSEC_PER_SEC << 32) / octeon_get_io_clock_rate(); 7188c2ecf20Sopenharmony_ci if (!ptp.s.ptp_en) 7198c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_MIO_PTP_CLOCK_COMP, clock_comp); 7208c2ecf20Sopenharmony_ci netdev_info(netdev, 7218c2ecf20Sopenharmony_ci "PTP Clock using sclk reference @ %lldHz\n", 7228c2ecf20Sopenharmony_ci (NSEC_PER_SEC << 32) / clock_comp); 7238c2ecf20Sopenharmony_ci } else { 7248c2ecf20Sopenharmony_ci /* The clock is already programmed to use a GPIO */ 7258c2ecf20Sopenharmony_ci u64 clock_comp = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_COMP); 7268c2ecf20Sopenharmony_ci netdev_info(netdev, 7278c2ecf20Sopenharmony_ci "PTP Clock using GPIO%d @ %lld Hz\n", 7288c2ecf20Sopenharmony_ci ptp.s.ext_clk_in, (NSEC_PER_SEC << 32) / clock_comp); 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* Enable the clock if it wasn't done already */ 7328c2ecf20Sopenharmony_ci if (!ptp.s.ptp_en) { 7338c2ecf20Sopenharmony_ci ptp.s.ptp_en = 1; 7348c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_MIO_PTP_CLOCK_CFG, ptp.u64); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci have_hw_timestamps = true; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (!have_hw_timestamps) 7408c2ecf20Sopenharmony_ci return -EINVAL; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci switch (config.tx_type) { 7438c2ecf20Sopenharmony_ci case HWTSTAMP_TX_OFF: 7448c2ecf20Sopenharmony_ci case HWTSTAMP_TX_ON: 7458c2ecf20Sopenharmony_ci break; 7468c2ecf20Sopenharmony_ci default: 7478c2ecf20Sopenharmony_ci return -ERANGE; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci switch (config.rx_filter) { 7518c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 7528c2ecf20Sopenharmony_ci p->has_rx_tstamp = false; 7538c2ecf20Sopenharmony_ci rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL); 7548c2ecf20Sopenharmony_ci rxx_frm_ctl.s.ptp_mode = 0; 7558c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64); 7568c2ecf20Sopenharmony_ci break; 7578c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_ALL: 7588c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_SOME: 7598c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: 7608c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 7618c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 7628c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: 7638c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: 7648c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: 7658c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: 7668c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: 7678c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: 7688c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_EVENT: 7698c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_SYNC: 7708c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: 7718c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NTP_ALL: 7728c2ecf20Sopenharmony_ci p->has_rx_tstamp = have_hw_timestamps; 7738c2ecf20Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_ALL; 7748c2ecf20Sopenharmony_ci if (p->has_rx_tstamp) { 7758c2ecf20Sopenharmony_ci rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL); 7768c2ecf20Sopenharmony_ci rxx_frm_ctl.s.ptp_mode = 1; 7778c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64); 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci default: 7818c2ecf20Sopenharmony_ci return -ERANGE; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (copy_to_user(rq->ifr_data, &config, sizeof(config))) 7858c2ecf20Sopenharmony_ci return -EFAULT; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci return 0; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic int octeon_mgmt_ioctl(struct net_device *netdev, 7918c2ecf20Sopenharmony_ci struct ifreq *rq, int cmd) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci switch (cmd) { 7948c2ecf20Sopenharmony_ci case SIOCSHWTSTAMP: 7958c2ecf20Sopenharmony_ci return octeon_mgmt_ioctl_hwtstamp(netdev, rq, cmd); 7968c2ecf20Sopenharmony_ci default: 7978c2ecf20Sopenharmony_ci return phy_do_ioctl(netdev, rq, cmd); 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic void octeon_mgmt_disable_link(struct octeon_mgmt *p) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci union cvmx_agl_gmx_prtx_cfg prtx_cfg; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* Disable GMX before we make any changes. */ 8068c2ecf20Sopenharmony_ci prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); 8078c2ecf20Sopenharmony_ci prtx_cfg.s.en = 0; 8088c2ecf20Sopenharmony_ci prtx_cfg.s.tx_en = 0; 8098c2ecf20Sopenharmony_ci prtx_cfg.s.rx_en = 0; 8108c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 8138c2ecf20Sopenharmony_ci int i; 8148c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 8158c2ecf20Sopenharmony_ci prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); 8168c2ecf20Sopenharmony_ci if (prtx_cfg.s.tx_idle == 1 || prtx_cfg.s.rx_idle == 1) 8178c2ecf20Sopenharmony_ci break; 8188c2ecf20Sopenharmony_ci mdelay(1); 8198c2ecf20Sopenharmony_ci i++; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic void octeon_mgmt_enable_link(struct octeon_mgmt *p) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci union cvmx_agl_gmx_prtx_cfg prtx_cfg; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Restore the GMX enable state only if link is set */ 8298c2ecf20Sopenharmony_ci prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); 8308c2ecf20Sopenharmony_ci prtx_cfg.s.tx_en = 1; 8318c2ecf20Sopenharmony_ci prtx_cfg.s.rx_en = 1; 8328c2ecf20Sopenharmony_ci prtx_cfg.s.en = 1; 8338c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64); 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic void octeon_mgmt_update_link(struct octeon_mgmt *p) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct net_device *ndev = p->netdev; 8398c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 8408c2ecf20Sopenharmony_ci union cvmx_agl_gmx_prtx_cfg prtx_cfg; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci if (!phydev->link) 8458c2ecf20Sopenharmony_ci prtx_cfg.s.duplex = 1; 8468c2ecf20Sopenharmony_ci else 8478c2ecf20Sopenharmony_ci prtx_cfg.s.duplex = phydev->duplex; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci switch (phydev->speed) { 8508c2ecf20Sopenharmony_ci case 10: 8518c2ecf20Sopenharmony_ci prtx_cfg.s.speed = 0; 8528c2ecf20Sopenharmony_ci prtx_cfg.s.slottime = 0; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 8558c2ecf20Sopenharmony_ci prtx_cfg.s.burst = 1; 8568c2ecf20Sopenharmony_ci prtx_cfg.s.speed_msb = 1; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci break; 8598c2ecf20Sopenharmony_ci case 100: 8608c2ecf20Sopenharmony_ci prtx_cfg.s.speed = 0; 8618c2ecf20Sopenharmony_ci prtx_cfg.s.slottime = 0; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 8648c2ecf20Sopenharmony_ci prtx_cfg.s.burst = 1; 8658c2ecf20Sopenharmony_ci prtx_cfg.s.speed_msb = 0; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci break; 8688c2ecf20Sopenharmony_ci case 1000: 8698c2ecf20Sopenharmony_ci /* 1000 MBits is only supported on 6XXX chips */ 8708c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 8718c2ecf20Sopenharmony_ci prtx_cfg.s.speed = 1; 8728c2ecf20Sopenharmony_ci prtx_cfg.s.speed_msb = 0; 8738c2ecf20Sopenharmony_ci /* Only matters for half-duplex */ 8748c2ecf20Sopenharmony_ci prtx_cfg.s.slottime = 1; 8758c2ecf20Sopenharmony_ci prtx_cfg.s.burst = phydev->duplex; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci case 0: /* No link */ 8798c2ecf20Sopenharmony_ci default: 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* Write the new GMX setting with the port still disabled. */ 8848c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_PRT_CFG, prtx_cfg.u64); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* Read GMX CFG again to make sure the config is completed. */ 8878c2ecf20Sopenharmony_ci prtx_cfg.u64 = cvmx_read_csr(p->agl + AGL_GMX_PRT_CFG); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) { 8908c2ecf20Sopenharmony_ci union cvmx_agl_gmx_txx_clk agl_clk; 8918c2ecf20Sopenharmony_ci union cvmx_agl_prtx_ctl prtx_ctl; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl); 8948c2ecf20Sopenharmony_ci agl_clk.u64 = cvmx_read_csr(p->agl + AGL_GMX_TX_CLK); 8958c2ecf20Sopenharmony_ci /* MII (both speeds) and RGMII 1000 speed. */ 8968c2ecf20Sopenharmony_ci agl_clk.s.clk_cnt = 1; 8978c2ecf20Sopenharmony_ci if (prtx_ctl.s.mode == 0) { /* RGMII mode */ 8988c2ecf20Sopenharmony_ci if (phydev->speed == 10) 8998c2ecf20Sopenharmony_ci agl_clk.s.clk_cnt = 50; 9008c2ecf20Sopenharmony_ci else if (phydev->speed == 100) 9018c2ecf20Sopenharmony_ci agl_clk.s.clk_cnt = 5; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_TX_CLK, agl_clk.u64); 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic void octeon_mgmt_adjust_link(struct net_device *netdev) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 9108c2ecf20Sopenharmony_ci struct phy_device *phydev = netdev->phydev; 9118c2ecf20Sopenharmony_ci unsigned long flags; 9128c2ecf20Sopenharmony_ci int link_changed = 0; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (!phydev) 9158c2ecf20Sopenharmony_ci return; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->lock, flags); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (!phydev->link && p->last_link) 9218c2ecf20Sopenharmony_ci link_changed = -1; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (phydev->link && 9248c2ecf20Sopenharmony_ci (p->last_duplex != phydev->duplex || 9258c2ecf20Sopenharmony_ci p->last_link != phydev->link || 9268c2ecf20Sopenharmony_ci p->last_speed != phydev->speed)) { 9278c2ecf20Sopenharmony_ci octeon_mgmt_disable_link(p); 9288c2ecf20Sopenharmony_ci link_changed = 1; 9298c2ecf20Sopenharmony_ci octeon_mgmt_update_link(p); 9308c2ecf20Sopenharmony_ci octeon_mgmt_enable_link(p); 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci p->last_link = phydev->link; 9348c2ecf20Sopenharmony_ci p->last_speed = phydev->speed; 9358c2ecf20Sopenharmony_ci p->last_duplex = phydev->duplex; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->lock, flags); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (link_changed != 0) { 9408c2ecf20Sopenharmony_ci if (link_changed > 0) 9418c2ecf20Sopenharmony_ci netdev_info(netdev, "Link is up - %d/%s\n", 9428c2ecf20Sopenharmony_ci phydev->speed, phydev->duplex == DUPLEX_FULL ? "Full" : "Half"); 9438c2ecf20Sopenharmony_ci else 9448c2ecf20Sopenharmony_ci netdev_info(netdev, "Link is down\n"); 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic int octeon_mgmt_init_phy(struct net_device *netdev) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 9518c2ecf20Sopenharmony_ci struct phy_device *phydev = NULL; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci if (octeon_is_simulation() || p->phy_np == NULL) { 9548c2ecf20Sopenharmony_ci /* No PHYs in the simulator. */ 9558c2ecf20Sopenharmony_ci netif_carrier_on(netdev); 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci phydev = of_phy_connect(netdev, p->phy_np, 9608c2ecf20Sopenharmony_ci octeon_mgmt_adjust_link, 0, 9618c2ecf20Sopenharmony_ci PHY_INTERFACE_MODE_MII); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci if (!phydev) 9648c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return 0; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic int octeon_mgmt_open(struct net_device *netdev) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 9728c2ecf20Sopenharmony_ci union cvmx_mixx_ctl mix_ctl; 9738c2ecf20Sopenharmony_ci union cvmx_agl_gmx_inf_mode agl_gmx_inf_mode; 9748c2ecf20Sopenharmony_ci union cvmx_mixx_oring1 oring1; 9758c2ecf20Sopenharmony_ci union cvmx_mixx_iring1 iring1; 9768c2ecf20Sopenharmony_ci union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl; 9778c2ecf20Sopenharmony_ci union cvmx_mixx_irhwm mix_irhwm; 9788c2ecf20Sopenharmony_ci union cvmx_mixx_orhwm mix_orhwm; 9798c2ecf20Sopenharmony_ci union cvmx_mixx_intena mix_intena; 9808c2ecf20Sopenharmony_ci struct sockaddr sa; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* Allocate ring buffers. */ 9838c2ecf20Sopenharmony_ci p->tx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), 9848c2ecf20Sopenharmony_ci GFP_KERNEL); 9858c2ecf20Sopenharmony_ci if (!p->tx_ring) 9868c2ecf20Sopenharmony_ci return -ENOMEM; 9878c2ecf20Sopenharmony_ci p->tx_ring_handle = 9888c2ecf20Sopenharmony_ci dma_map_single(p->dev, p->tx_ring, 9898c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), 9908c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 9918c2ecf20Sopenharmony_ci p->tx_next = 0; 9928c2ecf20Sopenharmony_ci p->tx_next_clean = 0; 9938c2ecf20Sopenharmony_ci p->tx_current_fill = 0; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci p->rx_ring = kzalloc(ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), 9978c2ecf20Sopenharmony_ci GFP_KERNEL); 9988c2ecf20Sopenharmony_ci if (!p->rx_ring) 9998c2ecf20Sopenharmony_ci goto err_nomem; 10008c2ecf20Sopenharmony_ci p->rx_ring_handle = 10018c2ecf20Sopenharmony_ci dma_map_single(p->dev, p->rx_ring, 10028c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), 10038c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci p->rx_next = 0; 10068c2ecf20Sopenharmony_ci p->rx_next_fill = 0; 10078c2ecf20Sopenharmony_ci p->rx_current_fill = 0; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci octeon_mgmt_reset_hw(p); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci /* Bring it out of reset if needed. */ 10148c2ecf20Sopenharmony_ci if (mix_ctl.s.reset) { 10158c2ecf20Sopenharmony_ci mix_ctl.s.reset = 0; 10168c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); 10178c2ecf20Sopenharmony_ci do { 10188c2ecf20Sopenharmony_ci mix_ctl.u64 = cvmx_read_csr(p->mix + MIX_CTL); 10198c2ecf20Sopenharmony_ci } while (mix_ctl.s.reset); 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN5XXX)) { 10238c2ecf20Sopenharmony_ci agl_gmx_inf_mode.u64 = 0; 10248c2ecf20Sopenharmony_ci agl_gmx_inf_mode.s.en = 1; 10258c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_AGL_GMX_INF_MODE, agl_gmx_inf_mode.u64); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN56XX_PASS1_X) 10288c2ecf20Sopenharmony_ci || OCTEON_IS_MODEL(OCTEON_CN52XX_PASS1_X)) { 10298c2ecf20Sopenharmony_ci /* Force compensation values, as they are not 10308c2ecf20Sopenharmony_ci * determined properly by HW 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_ci union cvmx_agl_gmx_drv_ctl drv_ctl; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci drv_ctl.u64 = cvmx_read_csr(CVMX_AGL_GMX_DRV_CTL); 10358c2ecf20Sopenharmony_ci if (p->port) { 10368c2ecf20Sopenharmony_ci drv_ctl.s.byp_en1 = 1; 10378c2ecf20Sopenharmony_ci drv_ctl.s.nctl1 = 6; 10388c2ecf20Sopenharmony_ci drv_ctl.s.pctl1 = 6; 10398c2ecf20Sopenharmony_ci } else { 10408c2ecf20Sopenharmony_ci drv_ctl.s.byp_en = 1; 10418c2ecf20Sopenharmony_ci drv_ctl.s.nctl = 6; 10428c2ecf20Sopenharmony_ci drv_ctl.s.pctl = 6; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_AGL_GMX_DRV_CTL, drv_ctl.u64); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci oring1.u64 = 0; 10488c2ecf20Sopenharmony_ci oring1.s.obase = p->tx_ring_handle >> 3; 10498c2ecf20Sopenharmony_ci oring1.s.osize = OCTEON_MGMT_TX_RING_SIZE; 10508c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_ORING1, oring1.u64); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci iring1.u64 = 0; 10538c2ecf20Sopenharmony_ci iring1.s.ibase = p->rx_ring_handle >> 3; 10548c2ecf20Sopenharmony_ci iring1.s.isize = OCTEON_MGMT_RX_RING_SIZE; 10558c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_IRING1, iring1.u64); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci memcpy(sa.sa_data, netdev->dev_addr, ETH_ALEN); 10588c2ecf20Sopenharmony_ci octeon_mgmt_set_mac_address(netdev, &sa); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci octeon_mgmt_change_mtu(netdev, netdev->mtu); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* Enable the port HW. Packets are not allowed until 10638c2ecf20Sopenharmony_ci * cvmx_mgmt_port_enable() is called. 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_ci mix_ctl.u64 = 0; 10668c2ecf20Sopenharmony_ci mix_ctl.s.crc_strip = 1; /* Strip the ending CRC */ 10678c2ecf20Sopenharmony_ci mix_ctl.s.en = 1; /* Enable the port */ 10688c2ecf20Sopenharmony_ci mix_ctl.s.nbtarb = 0; /* Arbitration mode */ 10698c2ecf20Sopenharmony_ci /* MII CB-request FIFO programmable high watermark */ 10708c2ecf20Sopenharmony_ci mix_ctl.s.mrq_hwm = 1; 10718c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN 10728c2ecf20Sopenharmony_ci mix_ctl.s.lendian = 1; 10738c2ecf20Sopenharmony_ci#endif 10748c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_CTL, mix_ctl.u64); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Read the PHY to find the mode of the interface. */ 10778c2ecf20Sopenharmony_ci if (octeon_mgmt_init_phy(netdev)) { 10788c2ecf20Sopenharmony_ci dev_err(p->dev, "Cannot initialize PHY on MIX%d.\n", p->port); 10798c2ecf20Sopenharmony_ci goto err_noirq; 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* Set the mode of the interface, RGMII/MII. */ 10838c2ecf20Sopenharmony_ci if (OCTEON_IS_MODEL(OCTEON_CN6XXX) && netdev->phydev) { 10848c2ecf20Sopenharmony_ci union cvmx_agl_prtx_ctl agl_prtx_ctl; 10858c2ecf20Sopenharmony_ci int rgmii_mode = 10868c2ecf20Sopenharmony_ci (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 10878c2ecf20Sopenharmony_ci netdev->phydev->supported) | 10888c2ecf20Sopenharmony_ci linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 10898c2ecf20Sopenharmony_ci netdev->phydev->supported)) != 0; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl); 10928c2ecf20Sopenharmony_ci agl_prtx_ctl.s.mode = rgmii_mode ? 0 : 1; 10938c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl_prt_ctl, agl_prtx_ctl.u64); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* MII clocks counts are based on the 125Mhz 10968c2ecf20Sopenharmony_ci * reference, which has an 8nS period. So our delays 10978c2ecf20Sopenharmony_ci * need to be multiplied by this factor. 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ci#define NS_PER_PHY_CLK 8 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* Take the DLL and clock tree out of reset */ 11028c2ecf20Sopenharmony_ci agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl); 11038c2ecf20Sopenharmony_ci agl_prtx_ctl.s.clkrst = 0; 11048c2ecf20Sopenharmony_ci if (rgmii_mode) { 11058c2ecf20Sopenharmony_ci agl_prtx_ctl.s.dllrst = 0; 11068c2ecf20Sopenharmony_ci agl_prtx_ctl.s.clktx_byp = 0; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl_prt_ctl, agl_prtx_ctl.u64); 11098c2ecf20Sopenharmony_ci cvmx_read_csr(p->agl_prt_ctl); /* Force write out before wait */ 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* Wait for the DLL to lock. External 125 MHz 11128c2ecf20Sopenharmony_ci * reference clock must be stable at this point. 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci ndelay(256 * NS_PER_PHY_CLK); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* Enable the interface */ 11178c2ecf20Sopenharmony_ci agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl); 11188c2ecf20Sopenharmony_ci agl_prtx_ctl.s.enable = 1; 11198c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl_prt_ctl, agl_prtx_ctl.u64); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* Read the value back to force the previous write */ 11228c2ecf20Sopenharmony_ci agl_prtx_ctl.u64 = cvmx_read_csr(p->agl_prt_ctl); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci /* Enable the compensation controller */ 11258c2ecf20Sopenharmony_ci agl_prtx_ctl.s.comp = 1; 11268c2ecf20Sopenharmony_ci agl_prtx_ctl.s.drv_byp = 0; 11278c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl_prt_ctl, agl_prtx_ctl.u64); 11288c2ecf20Sopenharmony_ci /* Force write out before wait. */ 11298c2ecf20Sopenharmony_ci cvmx_read_csr(p->agl_prt_ctl); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /* For compensation state to lock. */ 11328c2ecf20Sopenharmony_ci ndelay(1040 * NS_PER_PHY_CLK); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* Default Interframe Gaps are too small. Recommended 11358c2ecf20Sopenharmony_ci * workaround is. 11368c2ecf20Sopenharmony_ci * 11378c2ecf20Sopenharmony_ci * AGL_GMX_TX_IFG[IFG1]=14 11388c2ecf20Sopenharmony_ci * AGL_GMX_TX_IFG[IFG2]=10 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ci cvmx_write_csr(CVMX_AGL_GMX_TX_IFG, 0xae); 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci octeon_mgmt_rx_fill_ring(netdev); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* Clear statistics. */ 11468c2ecf20Sopenharmony_ci /* Clear on read. */ 11478c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_CTL, 1); 11488c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_DRP, 0); 11498c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_STATS_PKTS_BAD, 0); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_TX_STATS_CTL, 1); 11528c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_TX_STAT0, 0); 11538c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_TX_STAT1, 0); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* Clear any pending interrupts */ 11568c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_ISR, cvmx_read_csr(p->mix + MIX_ISR)); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (request_irq(p->irq, octeon_mgmt_interrupt, 0, netdev->name, 11598c2ecf20Sopenharmony_ci netdev)) { 11608c2ecf20Sopenharmony_ci dev_err(p->dev, "request_irq(%d) failed.\n", p->irq); 11618c2ecf20Sopenharmony_ci goto err_noirq; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* Interrupt every single RX packet */ 11658c2ecf20Sopenharmony_ci mix_irhwm.u64 = 0; 11668c2ecf20Sopenharmony_ci mix_irhwm.s.irhwm = 0; 11678c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_IRHWM, mix_irhwm.u64); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* Interrupt when we have 1 or more packets to clean. */ 11708c2ecf20Sopenharmony_ci mix_orhwm.u64 = 0; 11718c2ecf20Sopenharmony_ci mix_orhwm.s.orhwm = 0; 11728c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_ORHWM, mix_orhwm.u64); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* Enable receive and transmit interrupts */ 11758c2ecf20Sopenharmony_ci mix_intena.u64 = 0; 11768c2ecf20Sopenharmony_ci mix_intena.s.ithena = 1; 11778c2ecf20Sopenharmony_ci mix_intena.s.othena = 1; 11788c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_INTENA, mix_intena.u64); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Enable packet I/O. */ 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci rxx_frm_ctl.u64 = 0; 11838c2ecf20Sopenharmony_ci rxx_frm_ctl.s.ptp_mode = p->has_rx_tstamp ? 1 : 0; 11848c2ecf20Sopenharmony_ci rxx_frm_ctl.s.pre_align = 1; 11858c2ecf20Sopenharmony_ci /* When set, disables the length check for non-min sized pkts 11868c2ecf20Sopenharmony_ci * with padding in the client data. 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_ci rxx_frm_ctl.s.pad_len = 1; 11898c2ecf20Sopenharmony_ci /* When set, disables the length check for VLAN pkts */ 11908c2ecf20Sopenharmony_ci rxx_frm_ctl.s.vlan_len = 1; 11918c2ecf20Sopenharmony_ci /* When set, PREAMBLE checking is less strict */ 11928c2ecf20Sopenharmony_ci rxx_frm_ctl.s.pre_free = 1; 11938c2ecf20Sopenharmony_ci /* Control Pause Frames can match station SMAC */ 11948c2ecf20Sopenharmony_ci rxx_frm_ctl.s.ctl_smac = 0; 11958c2ecf20Sopenharmony_ci /* Control Pause Frames can match globally assign Multicast address */ 11968c2ecf20Sopenharmony_ci rxx_frm_ctl.s.ctl_mcst = 1; 11978c2ecf20Sopenharmony_ci /* Forward pause information to TX block */ 11988c2ecf20Sopenharmony_ci rxx_frm_ctl.s.ctl_bck = 1; 11998c2ecf20Sopenharmony_ci /* Drop Control Pause Frames */ 12008c2ecf20Sopenharmony_ci rxx_frm_ctl.s.ctl_drp = 1; 12018c2ecf20Sopenharmony_ci /* Strip off the preamble */ 12028c2ecf20Sopenharmony_ci rxx_frm_ctl.s.pre_strp = 1; 12038c2ecf20Sopenharmony_ci /* This port is configured to send PREAMBLE+SFD to begin every 12048c2ecf20Sopenharmony_ci * frame. GMX checks that the PREAMBLE is sent correctly. 12058c2ecf20Sopenharmony_ci */ 12068c2ecf20Sopenharmony_ci rxx_frm_ctl.s.pre_chk = 1; 12078c2ecf20Sopenharmony_ci cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* Configure the port duplex, speed and enables */ 12108c2ecf20Sopenharmony_ci octeon_mgmt_disable_link(p); 12118c2ecf20Sopenharmony_ci if (netdev->phydev) 12128c2ecf20Sopenharmony_ci octeon_mgmt_update_link(p); 12138c2ecf20Sopenharmony_ci octeon_mgmt_enable_link(p); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci p->last_link = 0; 12168c2ecf20Sopenharmony_ci p->last_speed = 0; 12178c2ecf20Sopenharmony_ci /* PHY is not present in simulator. The carrier is enabled 12188c2ecf20Sopenharmony_ci * while initializing the phy for simulator, leave it enabled. 12198c2ecf20Sopenharmony_ci */ 12208c2ecf20Sopenharmony_ci if (netdev->phydev) { 12218c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 12228c2ecf20Sopenharmony_ci phy_start(netdev->phydev); 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci netif_wake_queue(netdev); 12268c2ecf20Sopenharmony_ci napi_enable(&p->napi); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci return 0; 12298c2ecf20Sopenharmony_cierr_noirq: 12308c2ecf20Sopenharmony_ci octeon_mgmt_reset_hw(p); 12318c2ecf20Sopenharmony_ci dma_unmap_single(p->dev, p->rx_ring_handle, 12328c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), 12338c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 12348c2ecf20Sopenharmony_ci kfree(p->rx_ring); 12358c2ecf20Sopenharmony_cierr_nomem: 12368c2ecf20Sopenharmony_ci dma_unmap_single(p->dev, p->tx_ring_handle, 12378c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), 12388c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 12398c2ecf20Sopenharmony_ci kfree(p->tx_ring); 12408c2ecf20Sopenharmony_ci return -ENOMEM; 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic int octeon_mgmt_stop(struct net_device *netdev) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci napi_disable(&p->napi); 12488c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci if (netdev->phydev) { 12518c2ecf20Sopenharmony_ci phy_stop(netdev->phydev); 12528c2ecf20Sopenharmony_ci phy_disconnect(netdev->phydev); 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci octeon_mgmt_reset_hw(p); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci free_irq(p->irq, netdev); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci /* dma_unmap is a nop on Octeon, so just free everything. */ 12628c2ecf20Sopenharmony_ci skb_queue_purge(&p->tx_list); 12638c2ecf20Sopenharmony_ci skb_queue_purge(&p->rx_list); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci dma_unmap_single(p->dev, p->rx_ring_handle, 12668c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_RX_RING_SIZE), 12678c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 12688c2ecf20Sopenharmony_ci kfree(p->rx_ring); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci dma_unmap_single(p->dev, p->tx_ring_handle, 12718c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), 12728c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 12738c2ecf20Sopenharmony_ci kfree(p->tx_ring); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return 0; 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic netdev_tx_t 12798c2ecf20Sopenharmony_ciocteon_mgmt_xmit(struct sk_buff *skb, struct net_device *netdev) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 12828c2ecf20Sopenharmony_ci union mgmt_port_ring_entry re; 12838c2ecf20Sopenharmony_ci unsigned long flags; 12848c2ecf20Sopenharmony_ci netdev_tx_t rv = NETDEV_TX_BUSY; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci re.d64 = 0; 12878c2ecf20Sopenharmony_ci re.s.tstamp = ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) != 0); 12888c2ecf20Sopenharmony_ci re.s.len = skb->len; 12898c2ecf20Sopenharmony_ci re.s.addr = dma_map_single(p->dev, skb->data, 12908c2ecf20Sopenharmony_ci skb->len, 12918c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->tx_list.lock, flags); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (unlikely(p->tx_current_fill >= ring_max_fill(OCTEON_MGMT_TX_RING_SIZE) - 1)) { 12968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->tx_list.lock, flags); 12978c2ecf20Sopenharmony_ci netif_stop_queue(netdev); 12988c2ecf20Sopenharmony_ci spin_lock_irqsave(&p->tx_list.lock, flags); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci if (unlikely(p->tx_current_fill >= 13028c2ecf20Sopenharmony_ci ring_max_fill(OCTEON_MGMT_TX_RING_SIZE))) { 13038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->tx_list.lock, flags); 13048c2ecf20Sopenharmony_ci dma_unmap_single(p->dev, re.s.addr, re.s.len, 13058c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 13068c2ecf20Sopenharmony_ci goto out; 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci __skb_queue_tail(&p->tx_list, skb); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci /* Put it in the ring. */ 13128c2ecf20Sopenharmony_ci p->tx_ring[p->tx_next] = re.d64; 13138c2ecf20Sopenharmony_ci p->tx_next = (p->tx_next + 1) % OCTEON_MGMT_TX_RING_SIZE; 13148c2ecf20Sopenharmony_ci p->tx_current_fill++; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&p->tx_list.lock, flags); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci dma_sync_single_for_device(p->dev, p->tx_ring_handle, 13198c2ecf20Sopenharmony_ci ring_size_to_bytes(OCTEON_MGMT_TX_RING_SIZE), 13208c2ecf20Sopenharmony_ci DMA_BIDIRECTIONAL); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci netdev->stats.tx_packets++; 13238c2ecf20Sopenharmony_ci netdev->stats.tx_bytes += skb->len; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* Ring the bell. */ 13268c2ecf20Sopenharmony_ci cvmx_write_csr(p->mix + MIX_ORING2, 1); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci netif_trans_update(netdev); 13298c2ecf20Sopenharmony_ci rv = NETDEV_TX_OK; 13308c2ecf20Sopenharmony_ciout: 13318c2ecf20Sopenharmony_ci octeon_mgmt_update_tx_stats(netdev); 13328c2ecf20Sopenharmony_ci return rv; 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 13368c2ecf20Sopenharmony_cistatic void octeon_mgmt_poll_controller(struct net_device *netdev) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci octeon_mgmt_receive_packets(p, 16); 13418c2ecf20Sopenharmony_ci octeon_mgmt_update_rx_stats(netdev); 13428c2ecf20Sopenharmony_ci} 13438c2ecf20Sopenharmony_ci#endif 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic void octeon_mgmt_get_drvinfo(struct net_device *netdev, 13468c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 13478c2ecf20Sopenharmony_ci{ 13488c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic int octeon_mgmt_nway_reset(struct net_device *dev) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 13548c2ecf20Sopenharmony_ci return -EPERM; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if (dev->phydev) 13578c2ecf20Sopenharmony_ci return phy_start_aneg(dev->phydev); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_cistatic const struct ethtool_ops octeon_mgmt_ethtool_ops = { 13638c2ecf20Sopenharmony_ci .get_drvinfo = octeon_mgmt_get_drvinfo, 13648c2ecf20Sopenharmony_ci .nway_reset = octeon_mgmt_nway_reset, 13658c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 13668c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 13678c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 13688c2ecf20Sopenharmony_ci}; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic const struct net_device_ops octeon_mgmt_ops = { 13718c2ecf20Sopenharmony_ci .ndo_open = octeon_mgmt_open, 13728c2ecf20Sopenharmony_ci .ndo_stop = octeon_mgmt_stop, 13738c2ecf20Sopenharmony_ci .ndo_start_xmit = octeon_mgmt_xmit, 13748c2ecf20Sopenharmony_ci .ndo_set_rx_mode = octeon_mgmt_set_rx_filtering, 13758c2ecf20Sopenharmony_ci .ndo_set_mac_address = octeon_mgmt_set_mac_address, 13768c2ecf20Sopenharmony_ci .ndo_do_ioctl = octeon_mgmt_ioctl, 13778c2ecf20Sopenharmony_ci .ndo_change_mtu = octeon_mgmt_change_mtu, 13788c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 13798c2ecf20Sopenharmony_ci .ndo_poll_controller = octeon_mgmt_poll_controller, 13808c2ecf20Sopenharmony_ci#endif 13818c2ecf20Sopenharmony_ci}; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic int octeon_mgmt_probe(struct platform_device *pdev) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct net_device *netdev; 13868c2ecf20Sopenharmony_ci struct octeon_mgmt *p; 13878c2ecf20Sopenharmony_ci const __be32 *data; 13888c2ecf20Sopenharmony_ci const u8 *mac; 13898c2ecf20Sopenharmony_ci struct resource *res_mix; 13908c2ecf20Sopenharmony_ci struct resource *res_agl; 13918c2ecf20Sopenharmony_ci struct resource *res_agl_prt_ctl; 13928c2ecf20Sopenharmony_ci int len; 13938c2ecf20Sopenharmony_ci int result; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct octeon_mgmt)); 13968c2ecf20Sopenharmony_ci if (netdev == NULL) 13978c2ecf20Sopenharmony_ci return -ENOMEM; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, netdev); 14028c2ecf20Sopenharmony_ci p = netdev_priv(netdev); 14038c2ecf20Sopenharmony_ci netif_napi_add(netdev, &p->napi, octeon_mgmt_napi_poll, 14048c2ecf20Sopenharmony_ci OCTEON_MGMT_NAPI_WEIGHT); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci p->netdev = netdev; 14078c2ecf20Sopenharmony_ci p->dev = &pdev->dev; 14088c2ecf20Sopenharmony_ci p->has_rx_tstamp = false; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci data = of_get_property(pdev->dev.of_node, "cell-index", &len); 14118c2ecf20Sopenharmony_ci if (data && len == sizeof(*data)) { 14128c2ecf20Sopenharmony_ci p->port = be32_to_cpup(data); 14138c2ecf20Sopenharmony_ci } else { 14148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no 'cell-index' property\n"); 14158c2ecf20Sopenharmony_ci result = -ENXIO; 14168c2ecf20Sopenharmony_ci goto err; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci snprintf(netdev->name, IFNAMSIZ, "mgmt%d", p->port); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci result = platform_get_irq(pdev, 0); 14228c2ecf20Sopenharmony_ci if (result < 0) 14238c2ecf20Sopenharmony_ci goto err; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci p->irq = result; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci res_mix = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14288c2ecf20Sopenharmony_ci if (res_mix == NULL) { 14298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no 'reg' resource\n"); 14308c2ecf20Sopenharmony_ci result = -ENXIO; 14318c2ecf20Sopenharmony_ci goto err; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci res_agl = platform_get_resource(pdev, IORESOURCE_MEM, 1); 14358c2ecf20Sopenharmony_ci if (res_agl == NULL) { 14368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no 'reg' resource\n"); 14378c2ecf20Sopenharmony_ci result = -ENXIO; 14388c2ecf20Sopenharmony_ci goto err; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci res_agl_prt_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 3); 14428c2ecf20Sopenharmony_ci if (res_agl_prt_ctl == NULL) { 14438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no 'reg' resource\n"); 14448c2ecf20Sopenharmony_ci result = -ENXIO; 14458c2ecf20Sopenharmony_ci goto err; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci p->mix_phys = res_mix->start; 14498c2ecf20Sopenharmony_ci p->mix_size = resource_size(res_mix); 14508c2ecf20Sopenharmony_ci p->agl_phys = res_agl->start; 14518c2ecf20Sopenharmony_ci p->agl_size = resource_size(res_agl); 14528c2ecf20Sopenharmony_ci p->agl_prt_ctl_phys = res_agl_prt_ctl->start; 14538c2ecf20Sopenharmony_ci p->agl_prt_ctl_size = resource_size(res_agl_prt_ctl); 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, p->mix_phys, p->mix_size, 14578c2ecf20Sopenharmony_ci res_mix->name)) { 14588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_mem_region (%s) failed\n", 14598c2ecf20Sopenharmony_ci res_mix->name); 14608c2ecf20Sopenharmony_ci result = -ENXIO; 14618c2ecf20Sopenharmony_ci goto err; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, p->agl_phys, p->agl_size, 14658c2ecf20Sopenharmony_ci res_agl->name)) { 14668c2ecf20Sopenharmony_ci result = -ENXIO; 14678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_mem_region (%s) failed\n", 14688c2ecf20Sopenharmony_ci res_agl->name); 14698c2ecf20Sopenharmony_ci goto err; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, p->agl_prt_ctl_phys, 14738c2ecf20Sopenharmony_ci p->agl_prt_ctl_size, res_agl_prt_ctl->name)) { 14748c2ecf20Sopenharmony_ci result = -ENXIO; 14758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_mem_region (%s) failed\n", 14768c2ecf20Sopenharmony_ci res_agl_prt_ctl->name); 14778c2ecf20Sopenharmony_ci goto err; 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci p->mix = (u64)devm_ioremap(&pdev->dev, p->mix_phys, p->mix_size); 14818c2ecf20Sopenharmony_ci p->agl = (u64)devm_ioremap(&pdev->dev, p->agl_phys, p->agl_size); 14828c2ecf20Sopenharmony_ci p->agl_prt_ctl = (u64)devm_ioremap(&pdev->dev, p->agl_prt_ctl_phys, 14838c2ecf20Sopenharmony_ci p->agl_prt_ctl_size); 14848c2ecf20Sopenharmony_ci if (!p->mix || !p->agl || !p->agl_prt_ctl) { 14858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to map I/O memory\n"); 14868c2ecf20Sopenharmony_ci result = -ENOMEM; 14878c2ecf20Sopenharmony_ci goto err; 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci spin_lock_init(&p->lock); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci skb_queue_head_init(&p->tx_list); 14938c2ecf20Sopenharmony_ci skb_queue_head_init(&p->rx_list); 14948c2ecf20Sopenharmony_ci tasklet_setup(&p->tx_clean_tasklet, 14958c2ecf20Sopenharmony_ci octeon_mgmt_clean_tx_tasklet); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci netdev->priv_flags |= IFF_UNICAST_FLT; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci netdev->netdev_ops = &octeon_mgmt_ops; 15008c2ecf20Sopenharmony_ci netdev->ethtool_ops = &octeon_mgmt_ethtool_ops; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci netdev->min_mtu = 64 - OCTEON_MGMT_RX_HEADROOM; 15038c2ecf20Sopenharmony_ci netdev->max_mtu = 16383 - OCTEON_MGMT_RX_HEADROOM - VLAN_HLEN; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci mac = of_get_mac_address(pdev->dev.of_node); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci if (!IS_ERR(mac)) 15088c2ecf20Sopenharmony_ci ether_addr_copy(netdev->dev_addr, mac); 15098c2ecf20Sopenharmony_ci else 15108c2ecf20Sopenharmony_ci eth_hw_addr_random(netdev); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci p->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci result = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 15158c2ecf20Sopenharmony_ci if (result) 15168c2ecf20Sopenharmony_ci goto err; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 15198c2ecf20Sopenharmony_ci result = register_netdev(netdev); 15208c2ecf20Sopenharmony_ci if (result) 15218c2ecf20Sopenharmony_ci goto err; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci return 0; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_cierr: 15268c2ecf20Sopenharmony_ci of_node_put(p->phy_np); 15278c2ecf20Sopenharmony_ci free_netdev(netdev); 15288c2ecf20Sopenharmony_ci return result; 15298c2ecf20Sopenharmony_ci} 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_cistatic int octeon_mgmt_remove(struct platform_device *pdev) 15328c2ecf20Sopenharmony_ci{ 15338c2ecf20Sopenharmony_ci struct net_device *netdev = platform_get_drvdata(pdev); 15348c2ecf20Sopenharmony_ci struct octeon_mgmt *p = netdev_priv(netdev); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci unregister_netdev(netdev); 15378c2ecf20Sopenharmony_ci of_node_put(p->phy_np); 15388c2ecf20Sopenharmony_ci free_netdev(netdev); 15398c2ecf20Sopenharmony_ci return 0; 15408c2ecf20Sopenharmony_ci} 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic const struct of_device_id octeon_mgmt_match[] = { 15438c2ecf20Sopenharmony_ci { 15448c2ecf20Sopenharmony_ci .compatible = "cavium,octeon-5750-mix", 15458c2ecf20Sopenharmony_ci }, 15468c2ecf20Sopenharmony_ci {}, 15478c2ecf20Sopenharmony_ci}; 15488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, octeon_mgmt_match); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_cistatic struct platform_driver octeon_mgmt_driver = { 15518c2ecf20Sopenharmony_ci .driver = { 15528c2ecf20Sopenharmony_ci .name = "octeon_mgmt", 15538c2ecf20Sopenharmony_ci .of_match_table = octeon_mgmt_match, 15548c2ecf20Sopenharmony_ci }, 15558c2ecf20Sopenharmony_ci .probe = octeon_mgmt_probe, 15568c2ecf20Sopenharmony_ci .remove = octeon_mgmt_remove, 15578c2ecf20Sopenharmony_ci}; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistatic int __init octeon_mgmt_mod_init(void) 15608c2ecf20Sopenharmony_ci{ 15618c2ecf20Sopenharmony_ci return platform_driver_register(&octeon_mgmt_driver); 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic void __exit octeon_mgmt_mod_exit(void) 15658c2ecf20Sopenharmony_ci{ 15668c2ecf20Sopenharmony_ci platform_driver_unregister(&octeon_mgmt_driver); 15678c2ecf20Sopenharmony_ci} 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_cimodule_init(octeon_mgmt_mod_init); 15708c2ecf20Sopenharmony_cimodule_exit(octeon_mgmt_mod_exit); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ciMODULE_SOFTDEP("pre: mdio-cavium"); 15738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION); 15748c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Daney"); 15758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1576