18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel IXP4xx Ethernet driver for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007 Krzysztof Halasa <khc@pm.waw.pl> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Ethernet port config (0x00 is not present on IXP42X): 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * logical port 0x00 0x10 0x20 108c2ecf20Sopenharmony_ci * NPE 0 (NPE-A) 1 (NPE-B) 2 (NPE-C) 118c2ecf20Sopenharmony_ci * physical PortId 2 0 1 128c2ecf20Sopenharmony_ci * TX queue 23 24 25 138c2ecf20Sopenharmony_ci * RX-free queue 26 27 28 148c2ecf20Sopenharmony_ci * TX-done queue is always 31, per-port RX and TX-ready queues are configurable 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Queue entries: 178c2ecf20Sopenharmony_ci * bits 0 -> 1 - NPE ID (RX and TX-done) 188c2ecf20Sopenharmony_ci * bits 0 -> 2 - priority (TX, per 802.1D) 198c2ecf20Sopenharmony_ci * bits 3 -> 4 - port ID (user-set?) 208c2ecf20Sopenharmony_ci * bits 5 -> 31 - physical descriptor address 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 258c2ecf20Sopenharmony_ci#include <linux/dmapool.h> 268c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 278c2ecf20Sopenharmony_ci#include <linux/io.h> 288c2ecf20Sopenharmony_ci#include <linux/kernel.h> 298c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h> 308c2ecf20Sopenharmony_ci#include <linux/of.h> 318c2ecf20Sopenharmony_ci#include <linux/phy.h> 328c2ecf20Sopenharmony_ci#include <linux/platform_data/eth_ixp4xx.h> 338c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 348c2ecf20Sopenharmony_ci#include <linux/ptp_classify.h> 358c2ecf20Sopenharmony_ci#include <linux/slab.h> 368c2ecf20Sopenharmony_ci#include <linux/module.h> 378c2ecf20Sopenharmony_ci#include <linux/soc/ixp4xx/npe.h> 388c2ecf20Sopenharmony_ci#include <linux/soc/ixp4xx/qmgr.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include "ixp46x_ts.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define DEBUG_DESC 0 438c2ecf20Sopenharmony_ci#define DEBUG_RX 0 448c2ecf20Sopenharmony_ci#define DEBUG_TX 0 458c2ecf20Sopenharmony_ci#define DEBUG_PKT_BYTES 0 468c2ecf20Sopenharmony_ci#define DEBUG_MDIO 0 478c2ecf20Sopenharmony_ci#define DEBUG_CLOSE 0 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define DRV_NAME "ixp4xx_eth" 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define MAX_NPES 3 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define RX_DESCS 64 /* also length of all RX queues */ 548c2ecf20Sopenharmony_ci#define TX_DESCS 16 /* also length of all TX queues */ 558c2ecf20Sopenharmony_ci#define TXDONE_QUEUE_LEN 64 /* dwords */ 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define POOL_ALLOC_SIZE (sizeof(struct desc) * (RX_DESCS + TX_DESCS)) 588c2ecf20Sopenharmony_ci#define REGS_SIZE 0x1000 598c2ecf20Sopenharmony_ci#define MAX_MRU 1536 /* 0x600 */ 608c2ecf20Sopenharmony_ci#define RX_BUFF_SIZE ALIGN((NET_IP_ALIGN) + MAX_MRU, 4) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define NAPI_WEIGHT 16 638c2ecf20Sopenharmony_ci#define MDIO_INTERVAL (3 * HZ) 648c2ecf20Sopenharmony_ci#define MAX_MDIO_RETRIES 100 /* microseconds, typically 30 cycles */ 658c2ecf20Sopenharmony_ci#define MAX_CLOSE_WAIT 1000 /* microseconds, typically 2-3 cycles */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define NPE_ID(port_id) ((port_id) >> 4) 688c2ecf20Sopenharmony_ci#define PHYSICAL_ID(port_id) ((NPE_ID(port_id) + 2) % 3) 698c2ecf20Sopenharmony_ci#define TX_QUEUE(port_id) (NPE_ID(port_id) + 23) 708c2ecf20Sopenharmony_ci#define RXFREE_QUEUE(port_id) (NPE_ID(port_id) + 26) 718c2ecf20Sopenharmony_ci#define TXDONE_QUEUE 31 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define PTP_SLAVE_MODE 1 748c2ecf20Sopenharmony_ci#define PTP_MASTER_MODE 2 758c2ecf20Sopenharmony_ci#define PORT2CHANNEL(p) NPE_ID(p->id) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* TX Control Registers */ 788c2ecf20Sopenharmony_ci#define TX_CNTRL0_TX_EN 0x01 798c2ecf20Sopenharmony_ci#define TX_CNTRL0_HALFDUPLEX 0x02 808c2ecf20Sopenharmony_ci#define TX_CNTRL0_RETRY 0x04 818c2ecf20Sopenharmony_ci#define TX_CNTRL0_PAD_EN 0x08 828c2ecf20Sopenharmony_ci#define TX_CNTRL0_APPEND_FCS 0x10 838c2ecf20Sopenharmony_ci#define TX_CNTRL0_2DEFER 0x20 848c2ecf20Sopenharmony_ci#define TX_CNTRL0_RMII 0x40 /* reduced MII */ 858c2ecf20Sopenharmony_ci#define TX_CNTRL1_RETRIES 0x0F /* 4 bits */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* RX Control Registers */ 888c2ecf20Sopenharmony_ci#define RX_CNTRL0_RX_EN 0x01 898c2ecf20Sopenharmony_ci#define RX_CNTRL0_PADSTRIP_EN 0x02 908c2ecf20Sopenharmony_ci#define RX_CNTRL0_SEND_FCS 0x04 918c2ecf20Sopenharmony_ci#define RX_CNTRL0_PAUSE_EN 0x08 928c2ecf20Sopenharmony_ci#define RX_CNTRL0_LOOP_EN 0x10 938c2ecf20Sopenharmony_ci#define RX_CNTRL0_ADDR_FLTR_EN 0x20 948c2ecf20Sopenharmony_ci#define RX_CNTRL0_RX_RUNT_EN 0x40 958c2ecf20Sopenharmony_ci#define RX_CNTRL0_BCAST_DIS 0x80 968c2ecf20Sopenharmony_ci#define RX_CNTRL1_DEFER_EN 0x01 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Core Control Register */ 998c2ecf20Sopenharmony_ci#define CORE_RESET 0x01 1008c2ecf20Sopenharmony_ci#define CORE_RX_FIFO_FLUSH 0x02 1018c2ecf20Sopenharmony_ci#define CORE_TX_FIFO_FLUSH 0x04 1028c2ecf20Sopenharmony_ci#define CORE_SEND_JAM 0x08 1038c2ecf20Sopenharmony_ci#define CORE_MDC_EN 0x10 /* MDIO using NPE-B ETH-0 only */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define DEFAULT_TX_CNTRL0 (TX_CNTRL0_TX_EN | TX_CNTRL0_RETRY | \ 1068c2ecf20Sopenharmony_ci TX_CNTRL0_PAD_EN | TX_CNTRL0_APPEND_FCS | \ 1078c2ecf20Sopenharmony_ci TX_CNTRL0_2DEFER) 1088c2ecf20Sopenharmony_ci#define DEFAULT_RX_CNTRL0 RX_CNTRL0_RX_EN 1098c2ecf20Sopenharmony_ci#define DEFAULT_CORE_CNTRL CORE_MDC_EN 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* NPE message codes */ 1138c2ecf20Sopenharmony_ci#define NPE_GETSTATUS 0x00 1148c2ecf20Sopenharmony_ci#define NPE_EDB_SETPORTADDRESS 0x01 1158c2ecf20Sopenharmony_ci#define NPE_EDB_GETMACADDRESSDATABASE 0x02 1168c2ecf20Sopenharmony_ci#define NPE_EDB_SETMACADDRESSSDATABASE 0x03 1178c2ecf20Sopenharmony_ci#define NPE_GETSTATS 0x04 1188c2ecf20Sopenharmony_ci#define NPE_RESETSTATS 0x05 1198c2ecf20Sopenharmony_ci#define NPE_SETMAXFRAMELENGTHS 0x06 1208c2ecf20Sopenharmony_ci#define NPE_VLAN_SETRXTAGMODE 0x07 1218c2ecf20Sopenharmony_ci#define NPE_VLAN_SETDEFAULTRXVID 0x08 1228c2ecf20Sopenharmony_ci#define NPE_VLAN_SETPORTVLANTABLEENTRY 0x09 1238c2ecf20Sopenharmony_ci#define NPE_VLAN_SETPORTVLANTABLERANGE 0x0A 1248c2ecf20Sopenharmony_ci#define NPE_VLAN_SETRXQOSENTRY 0x0B 1258c2ecf20Sopenharmony_ci#define NPE_VLAN_SETPORTIDEXTRACTIONMODE 0x0C 1268c2ecf20Sopenharmony_ci#define NPE_STP_SETBLOCKINGSTATE 0x0D 1278c2ecf20Sopenharmony_ci#define NPE_FW_SETFIREWALLMODE 0x0E 1288c2ecf20Sopenharmony_ci#define NPE_PC_SETFRAMECONTROLDURATIONID 0x0F 1298c2ecf20Sopenharmony_ci#define NPE_PC_SETAPMACTABLE 0x11 1308c2ecf20Sopenharmony_ci#define NPE_SETLOOPBACK_MODE 0x12 1318c2ecf20Sopenharmony_ci#define NPE_PC_SETBSSIDTABLE 0x13 1328c2ecf20Sopenharmony_ci#define NPE_ADDRESS_FILTER_CONFIG 0x14 1338c2ecf20Sopenharmony_ci#define NPE_APPENDFCSCONFIG 0x15 1348c2ecf20Sopenharmony_ci#define NPE_NOTIFY_MAC_RECOVERY_DONE 0x16 1358c2ecf20Sopenharmony_ci#define NPE_MAC_RECOVERY_START 0x17 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 1398c2ecf20Sopenharmony_citypedef struct sk_buff buffer_t; 1408c2ecf20Sopenharmony_ci#define free_buffer dev_kfree_skb 1418c2ecf20Sopenharmony_ci#define free_buffer_irq dev_consume_skb_irq 1428c2ecf20Sopenharmony_ci#else 1438c2ecf20Sopenharmony_citypedef void buffer_t; 1448c2ecf20Sopenharmony_ci#define free_buffer kfree 1458c2ecf20Sopenharmony_ci#define free_buffer_irq kfree 1468c2ecf20Sopenharmony_ci#endif 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistruct eth_regs { 1498c2ecf20Sopenharmony_ci u32 tx_control[2], __res1[2]; /* 000 */ 1508c2ecf20Sopenharmony_ci u32 rx_control[2], __res2[2]; /* 010 */ 1518c2ecf20Sopenharmony_ci u32 random_seed, __res3[3]; /* 020 */ 1528c2ecf20Sopenharmony_ci u32 partial_empty_threshold, __res4; /* 030 */ 1538c2ecf20Sopenharmony_ci u32 partial_full_threshold, __res5; /* 038 */ 1548c2ecf20Sopenharmony_ci u32 tx_start_bytes, __res6[3]; /* 040 */ 1558c2ecf20Sopenharmony_ci u32 tx_deferral, rx_deferral, __res7[2];/* 050 */ 1568c2ecf20Sopenharmony_ci u32 tx_2part_deferral[2], __res8[2]; /* 060 */ 1578c2ecf20Sopenharmony_ci u32 slot_time, __res9[3]; /* 070 */ 1588c2ecf20Sopenharmony_ci u32 mdio_command[4]; /* 080 */ 1598c2ecf20Sopenharmony_ci u32 mdio_status[4]; /* 090 */ 1608c2ecf20Sopenharmony_ci u32 mcast_mask[6], __res10[2]; /* 0A0 */ 1618c2ecf20Sopenharmony_ci u32 mcast_addr[6], __res11[2]; /* 0C0 */ 1628c2ecf20Sopenharmony_ci u32 int_clock_threshold, __res12[3]; /* 0E0 */ 1638c2ecf20Sopenharmony_ci u32 hw_addr[6], __res13[61]; /* 0F0 */ 1648c2ecf20Sopenharmony_ci u32 core_control; /* 1FC */ 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistruct port { 1688c2ecf20Sopenharmony_ci struct resource *mem_res; 1698c2ecf20Sopenharmony_ci struct eth_regs __iomem *regs; 1708c2ecf20Sopenharmony_ci struct npe *npe; 1718c2ecf20Sopenharmony_ci struct net_device *netdev; 1728c2ecf20Sopenharmony_ci struct napi_struct napi; 1738c2ecf20Sopenharmony_ci struct eth_plat_info *plat; 1748c2ecf20Sopenharmony_ci buffer_t *rx_buff_tab[RX_DESCS], *tx_buff_tab[TX_DESCS]; 1758c2ecf20Sopenharmony_ci struct desc *desc_tab; /* coherent */ 1768c2ecf20Sopenharmony_ci u32 desc_tab_phys; 1778c2ecf20Sopenharmony_ci int id; /* logical port ID */ 1788c2ecf20Sopenharmony_ci int speed, duplex; 1798c2ecf20Sopenharmony_ci u8 firmware[4]; 1808c2ecf20Sopenharmony_ci int hwts_tx_en; 1818c2ecf20Sopenharmony_ci int hwts_rx_en; 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* NPE message structure */ 1858c2ecf20Sopenharmony_cistruct msg { 1868c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 1878c2ecf20Sopenharmony_ci u8 cmd, eth_id, byte2, byte3; 1888c2ecf20Sopenharmony_ci u8 byte4, byte5, byte6, byte7; 1898c2ecf20Sopenharmony_ci#else 1908c2ecf20Sopenharmony_ci u8 byte3, byte2, eth_id, cmd; 1918c2ecf20Sopenharmony_ci u8 byte7, byte6, byte5, byte4; 1928c2ecf20Sopenharmony_ci#endif 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* Ethernet packet descriptor */ 1968c2ecf20Sopenharmony_cistruct desc { 1978c2ecf20Sopenharmony_ci u32 next; /* pointer to next buffer, unused */ 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 2008c2ecf20Sopenharmony_ci u16 buf_len; /* buffer length */ 2018c2ecf20Sopenharmony_ci u16 pkt_len; /* packet length */ 2028c2ecf20Sopenharmony_ci u32 data; /* pointer to data buffer in RAM */ 2038c2ecf20Sopenharmony_ci u8 dest_id; 2048c2ecf20Sopenharmony_ci u8 src_id; 2058c2ecf20Sopenharmony_ci u16 flags; 2068c2ecf20Sopenharmony_ci u8 qos; 2078c2ecf20Sopenharmony_ci u8 padlen; 2088c2ecf20Sopenharmony_ci u16 vlan_tci; 2098c2ecf20Sopenharmony_ci#else 2108c2ecf20Sopenharmony_ci u16 pkt_len; /* packet length */ 2118c2ecf20Sopenharmony_ci u16 buf_len; /* buffer length */ 2128c2ecf20Sopenharmony_ci u32 data; /* pointer to data buffer in RAM */ 2138c2ecf20Sopenharmony_ci u16 flags; 2148c2ecf20Sopenharmony_ci u8 src_id; 2158c2ecf20Sopenharmony_ci u8 dest_id; 2168c2ecf20Sopenharmony_ci u16 vlan_tci; 2178c2ecf20Sopenharmony_ci u8 padlen; 2188c2ecf20Sopenharmony_ci u8 qos; 2198c2ecf20Sopenharmony_ci#endif 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 2228c2ecf20Sopenharmony_ci u8 dst_mac_0, dst_mac_1, dst_mac_2, dst_mac_3; 2238c2ecf20Sopenharmony_ci u8 dst_mac_4, dst_mac_5, src_mac_0, src_mac_1; 2248c2ecf20Sopenharmony_ci u8 src_mac_2, src_mac_3, src_mac_4, src_mac_5; 2258c2ecf20Sopenharmony_ci#else 2268c2ecf20Sopenharmony_ci u8 dst_mac_3, dst_mac_2, dst_mac_1, dst_mac_0; 2278c2ecf20Sopenharmony_ci u8 src_mac_1, src_mac_0, dst_mac_5, dst_mac_4; 2288c2ecf20Sopenharmony_ci u8 src_mac_5, src_mac_4, src_mac_3, src_mac_2; 2298c2ecf20Sopenharmony_ci#endif 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci#define rx_desc_phys(port, n) ((port)->desc_tab_phys + \ 2348c2ecf20Sopenharmony_ci (n) * sizeof(struct desc)) 2358c2ecf20Sopenharmony_ci#define rx_desc_ptr(port, n) (&(port)->desc_tab[n]) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#define tx_desc_phys(port, n) ((port)->desc_tab_phys + \ 2388c2ecf20Sopenharmony_ci ((n) + RX_DESCS) * sizeof(struct desc)) 2398c2ecf20Sopenharmony_ci#define tx_desc_ptr(port, n) (&(port)->desc_tab[(n) + RX_DESCS]) 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci#ifndef __ARMEB__ 2428c2ecf20Sopenharmony_cistatic inline void memcpy_swab32(u32 *dest, u32 *src, int cnt) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int i; 2458c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) 2468c2ecf20Sopenharmony_ci dest[i] = swab32(src[i]); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci#endif 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic spinlock_t mdio_lock; 2518c2ecf20Sopenharmony_cistatic struct eth_regs __iomem *mdio_regs; /* mdio command and status only */ 2528c2ecf20Sopenharmony_cistatic struct mii_bus *mdio_bus; 2538c2ecf20Sopenharmony_cistatic int ports_open; 2548c2ecf20Sopenharmony_cistatic struct port *npe_port_tab[MAX_NPES]; 2558c2ecf20Sopenharmony_cistatic struct dma_pool *dma_pool; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int ixp_ptp_match(struct sk_buff *skb, u16 uid_hi, u32 uid_lo, u16 seqid) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci u8 *data = skb->data; 2608c2ecf20Sopenharmony_ci unsigned int offset; 2618c2ecf20Sopenharmony_ci u16 *hi, *id; 2628c2ecf20Sopenharmony_ci u32 lo; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (ptp_classify_raw(skb) != PTP_CLASS_V1_IPV4) 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci offset = ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (skb->len < offset + OFF_PTP_SEQUENCE_ID + sizeof(seqid)) 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci hi = (u16 *)(data + offset + OFF_PTP_SOURCE_UUID); 2738c2ecf20Sopenharmony_ci id = (u16 *)(data + offset + OFF_PTP_SEQUENCE_ID); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci memcpy(&lo, &hi[1], sizeof(lo)); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return (uid_hi == ntohs(*hi) && 2788c2ecf20Sopenharmony_ci uid_lo == ntohl(lo) && 2798c2ecf20Sopenharmony_ci seqid == ntohs(*id)); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic void ixp_rx_timestamp(struct port *port, struct sk_buff *skb) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 2858c2ecf20Sopenharmony_ci struct ixp46x_ts_regs *regs; 2868c2ecf20Sopenharmony_ci u64 ns; 2878c2ecf20Sopenharmony_ci u32 ch, hi, lo, val; 2888c2ecf20Sopenharmony_ci u16 uid, seq; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!port->hwts_rx_en) 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ch = PORT2CHANNEL(port); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci val = __raw_readl(®s->channel[ch].ch_event); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (!(val & RX_SNAPSHOT_LOCKED)) 3008c2ecf20Sopenharmony_ci return; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci lo = __raw_readl(®s->channel[ch].src_uuid_lo); 3038c2ecf20Sopenharmony_ci hi = __raw_readl(®s->channel[ch].src_uuid_hi); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci uid = hi & 0xffff; 3068c2ecf20Sopenharmony_ci seq = (hi >> 16) & 0xffff; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!ixp_ptp_match(skb, htons(uid), htonl(lo), htons(seq))) 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci lo = __raw_readl(®s->channel[ch].rx_snap_lo); 3128c2ecf20Sopenharmony_ci hi = __raw_readl(®s->channel[ch].rx_snap_hi); 3138c2ecf20Sopenharmony_ci ns = ((u64) hi) << 32; 3148c2ecf20Sopenharmony_ci ns |= lo; 3158c2ecf20Sopenharmony_ci ns <<= TICKS_NS_SHIFT; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 3188c2ecf20Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 3198c2ecf20Sopenharmony_ci shhwtstamps->hwtstamp = ns_to_ktime(ns); 3208c2ecf20Sopenharmony_ciout: 3218c2ecf20Sopenharmony_ci __raw_writel(RX_SNAPSHOT_LOCKED, ®s->channel[ch].ch_event); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void ixp_tx_timestamp(struct port *port, struct sk_buff *skb) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 3278c2ecf20Sopenharmony_ci struct ixp46x_ts_regs *regs; 3288c2ecf20Sopenharmony_ci struct skb_shared_info *shtx; 3298c2ecf20Sopenharmony_ci u64 ns; 3308c2ecf20Sopenharmony_ci u32 ch, cnt, hi, lo, val; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci shtx = skb_shinfo(skb); 3338c2ecf20Sopenharmony_ci if (unlikely(shtx->tx_flags & SKBTX_HW_TSTAMP && port->hwts_tx_en)) 3348c2ecf20Sopenharmony_ci shtx->tx_flags |= SKBTX_IN_PROGRESS; 3358c2ecf20Sopenharmony_ci else 3368c2ecf20Sopenharmony_ci return; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ch = PORT2CHANNEL(port); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * This really stinks, but we have to poll for the Tx time stamp. 3448c2ecf20Sopenharmony_ci * Usually, the time stamp is ready after 4 to 6 microseconds. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci for (cnt = 0; cnt < 100; cnt++) { 3478c2ecf20Sopenharmony_ci val = __raw_readl(®s->channel[ch].ch_event); 3488c2ecf20Sopenharmony_ci if (val & TX_SNAPSHOT_LOCKED) 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci udelay(1); 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci if (!(val & TX_SNAPSHOT_LOCKED)) { 3538c2ecf20Sopenharmony_ci shtx->tx_flags &= ~SKBTX_IN_PROGRESS; 3548c2ecf20Sopenharmony_ci return; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci lo = __raw_readl(®s->channel[ch].tx_snap_lo); 3588c2ecf20Sopenharmony_ci hi = __raw_readl(®s->channel[ch].tx_snap_hi); 3598c2ecf20Sopenharmony_ci ns = ((u64) hi) << 32; 3608c2ecf20Sopenharmony_ci ns |= lo; 3618c2ecf20Sopenharmony_ci ns <<= TICKS_NS_SHIFT; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 3648c2ecf20Sopenharmony_ci shhwtstamps.hwtstamp = ns_to_ktime(ns); 3658c2ecf20Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci __raw_writel(TX_SNAPSHOT_LOCKED, ®s->channel[ch].ch_event); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int hwtstamp_set(struct net_device *netdev, struct ifreq *ifr) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct hwtstamp_config cfg; 3738c2ecf20Sopenharmony_ci struct ixp46x_ts_regs *regs; 3748c2ecf20Sopenharmony_ci struct port *port = netdev_priv(netdev); 3758c2ecf20Sopenharmony_ci int ch; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) 3788c2ecf20Sopenharmony_ci return -EFAULT; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (cfg.flags) /* reserved for future extensions */ 3818c2ecf20Sopenharmony_ci return -EINVAL; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci ch = PORT2CHANNEL(port); 3848c2ecf20Sopenharmony_ci regs = (struct ixp46x_ts_regs __iomem *) IXP4XX_TIMESYNC_BASE_VIRT; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON) 3878c2ecf20Sopenharmony_ci return -ERANGE; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci switch (cfg.rx_filter) { 3908c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_NONE: 3918c2ecf20Sopenharmony_ci port->hwts_rx_en = 0; 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: 3948c2ecf20Sopenharmony_ci port->hwts_rx_en = PTP_SLAVE_MODE; 3958c2ecf20Sopenharmony_ci __raw_writel(0, ®s->channel[ch].ch_control); 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: 3988c2ecf20Sopenharmony_ci port->hwts_rx_en = PTP_MASTER_MODE; 3998c2ecf20Sopenharmony_ci __raw_writel(MASTER_MODE, ®s->channel[ch].ch_control); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci default: 4028c2ecf20Sopenharmony_ci return -ERANGE; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci port->hwts_tx_en = cfg.tx_type == HWTSTAMP_TX_ON; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* Clear out any old time stamps. */ 4088c2ecf20Sopenharmony_ci __raw_writel(TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED, 4098c2ecf20Sopenharmony_ci ®s->channel[ch].ch_event); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int hwtstamp_get(struct net_device *netdev, struct ifreq *ifr) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct hwtstamp_config cfg; 4178c2ecf20Sopenharmony_ci struct port *port = netdev_priv(netdev); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci cfg.flags = 0; 4208c2ecf20Sopenharmony_ci cfg.tx_type = port->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci switch (port->hwts_rx_en) { 4238c2ecf20Sopenharmony_ci case 0: 4248c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_NONE; 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci case PTP_SLAVE_MODE: 4278c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_SYNC; 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci case PTP_MASTER_MODE: 4308c2ecf20Sopenharmony_ci cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci default: 4338c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 4348c2ecf20Sopenharmony_ci return -ERANGE; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int ixp4xx_mdio_cmd(struct mii_bus *bus, int phy_id, int location, 4418c2ecf20Sopenharmony_ci int write, u16 cmd) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci int cycles = 0; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80) { 4468c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: MII not ready to transmit\n", bus->name); 4478c2ecf20Sopenharmony_ci return -1; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (write) { 4518c2ecf20Sopenharmony_ci __raw_writel(cmd & 0xFF, &mdio_regs->mdio_command[0]); 4528c2ecf20Sopenharmony_ci __raw_writel(cmd >> 8, &mdio_regs->mdio_command[1]); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci __raw_writel(((phy_id << 5) | location) & 0xFF, 4558c2ecf20Sopenharmony_ci &mdio_regs->mdio_command[2]); 4568c2ecf20Sopenharmony_ci __raw_writel((phy_id >> 3) | (write << 2) | 0x80 /* GO */, 4578c2ecf20Sopenharmony_ci &mdio_regs->mdio_command[3]); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci while ((cycles < MAX_MDIO_RETRIES) && 4608c2ecf20Sopenharmony_ci (__raw_readl(&mdio_regs->mdio_command[3]) & 0x80)) { 4618c2ecf20Sopenharmony_ci udelay(1); 4628c2ecf20Sopenharmony_ci cycles++; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (cycles == MAX_MDIO_RETRIES) { 4668c2ecf20Sopenharmony_ci printk(KERN_ERR "%s #%i: MII write failed\n", bus->name, 4678c2ecf20Sopenharmony_ci phy_id); 4688c2ecf20Sopenharmony_ci return -1; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci#if DEBUG_MDIO 4728c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s #%i: mdio_%s() took %i cycles\n", bus->name, 4738c2ecf20Sopenharmony_ci phy_id, write ? "write" : "read", cycles); 4748c2ecf20Sopenharmony_ci#endif 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (write) 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (__raw_readl(&mdio_regs->mdio_status[3]) & 0x80) { 4808c2ecf20Sopenharmony_ci#if DEBUG_MDIO 4818c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s #%i: MII read failed\n", bus->name, 4828c2ecf20Sopenharmony_ci phy_id); 4838c2ecf20Sopenharmony_ci#endif 4848c2ecf20Sopenharmony_ci return 0xFFFF; /* don't return error */ 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return (__raw_readl(&mdio_regs->mdio_status[0]) & 0xFF) | 4888c2ecf20Sopenharmony_ci ((__raw_readl(&mdio_regs->mdio_status[1]) & 0xFF) << 8); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int ixp4xx_mdio_read(struct mii_bus *bus, int phy_id, int location) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci unsigned long flags; 4948c2ecf20Sopenharmony_ci int ret; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci spin_lock_irqsave(&mdio_lock, flags); 4978c2ecf20Sopenharmony_ci ret = ixp4xx_mdio_cmd(bus, phy_id, location, 0, 0); 4988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mdio_lock, flags); 4998c2ecf20Sopenharmony_ci#if DEBUG_MDIO 5008c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s #%i: MII read [%i] -> 0x%X\n", bus->name, 5018c2ecf20Sopenharmony_ci phy_id, location, ret); 5028c2ecf20Sopenharmony_ci#endif 5038c2ecf20Sopenharmony_ci return ret; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic int ixp4xx_mdio_write(struct mii_bus *bus, int phy_id, int location, 5078c2ecf20Sopenharmony_ci u16 val) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci unsigned long flags; 5108c2ecf20Sopenharmony_ci int ret; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci spin_lock_irqsave(&mdio_lock, flags); 5138c2ecf20Sopenharmony_ci ret = ixp4xx_mdio_cmd(bus, phy_id, location, 1, val); 5148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mdio_lock, flags); 5158c2ecf20Sopenharmony_ci#if DEBUG_MDIO 5168c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s #%i: MII write [%i] <- 0x%X, err = %i\n", 5178c2ecf20Sopenharmony_ci bus->name, phy_id, location, val, ret); 5188c2ecf20Sopenharmony_ci#endif 5198c2ecf20Sopenharmony_ci return ret; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int ixp4xx_mdio_register(struct eth_regs __iomem *regs) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci int err; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (!(mdio_bus = mdiobus_alloc())) 5278c2ecf20Sopenharmony_ci return -ENOMEM; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci mdio_regs = regs; 5308c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control); 5318c2ecf20Sopenharmony_ci spin_lock_init(&mdio_lock); 5328c2ecf20Sopenharmony_ci mdio_bus->name = "IXP4xx MII Bus"; 5338c2ecf20Sopenharmony_ci mdio_bus->read = &ixp4xx_mdio_read; 5348c2ecf20Sopenharmony_ci mdio_bus->write = &ixp4xx_mdio_write; 5358c2ecf20Sopenharmony_ci snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "ixp4xx-eth-0"); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if ((err = mdiobus_register(mdio_bus))) 5388c2ecf20Sopenharmony_ci mdiobus_free(mdio_bus); 5398c2ecf20Sopenharmony_ci return err; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void ixp4xx_mdio_remove(void) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci mdiobus_unregister(mdio_bus); 5458c2ecf20Sopenharmony_ci mdiobus_free(mdio_bus); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic void ixp4xx_adjust_link(struct net_device *dev) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct port *port = netdev_priv(dev); 5528c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (!phydev->link) { 5558c2ecf20Sopenharmony_ci if (port->speed) { 5568c2ecf20Sopenharmony_ci port->speed = 0; 5578c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: link down\n", dev->name); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci return; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (port->speed == phydev->speed && port->duplex == phydev->duplex) 5638c2ecf20Sopenharmony_ci return; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci port->speed = phydev->speed; 5668c2ecf20Sopenharmony_ci port->duplex = phydev->duplex; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (port->duplex) 5698c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_TX_CNTRL0 & ~TX_CNTRL0_HALFDUPLEX, 5708c2ecf20Sopenharmony_ci &port->regs->tx_control[0]); 5718c2ecf20Sopenharmony_ci else 5728c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_TX_CNTRL0 | TX_CNTRL0_HALFDUPLEX, 5738c2ecf20Sopenharmony_ci &port->regs->tx_control[0]); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci netdev_info(dev, "%s: link up, speed %u Mb/s, %s duplex\n", 5768c2ecf20Sopenharmony_ci dev->name, port->speed, port->duplex ? "full" : "half"); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic inline void debug_pkt(struct net_device *dev, const char *func, 5818c2ecf20Sopenharmony_ci u8 *data, int len) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci#if DEBUG_PKT_BYTES 5848c2ecf20Sopenharmony_ci int i; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci netdev_debug(dev, "%s(%i) ", func, len); 5878c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 5888c2ecf20Sopenharmony_ci if (i >= DEBUG_PKT_BYTES) 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci printk("%s%02X", 5918c2ecf20Sopenharmony_ci ((i == 6) || (i == 12) || (i >= 14)) ? " " : "", 5928c2ecf20Sopenharmony_ci data[i]); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci printk("\n"); 5958c2ecf20Sopenharmony_ci#endif 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic inline void debug_desc(u32 phys, struct desc *desc) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci#if DEBUG_DESC 6028c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%X: %X %3X %3X %08X %2X < %2X %4X %X" 6038c2ecf20Sopenharmony_ci " %X %X %02X%02X%02X%02X%02X%02X < %02X%02X%02X%02X%02X%02X\n", 6048c2ecf20Sopenharmony_ci phys, desc->next, desc->buf_len, desc->pkt_len, 6058c2ecf20Sopenharmony_ci desc->data, desc->dest_id, desc->src_id, desc->flags, 6068c2ecf20Sopenharmony_ci desc->qos, desc->padlen, desc->vlan_tci, 6078c2ecf20Sopenharmony_ci desc->dst_mac_0, desc->dst_mac_1, desc->dst_mac_2, 6088c2ecf20Sopenharmony_ci desc->dst_mac_3, desc->dst_mac_4, desc->dst_mac_5, 6098c2ecf20Sopenharmony_ci desc->src_mac_0, desc->src_mac_1, desc->src_mac_2, 6108c2ecf20Sopenharmony_ci desc->src_mac_3, desc->src_mac_4, desc->src_mac_5); 6118c2ecf20Sopenharmony_ci#endif 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic inline int queue_get_desc(unsigned int queue, struct port *port, 6158c2ecf20Sopenharmony_ci int is_tx) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci u32 phys, tab_phys, n_desc; 6188c2ecf20Sopenharmony_ci struct desc *tab; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (!(phys = qmgr_get_entry(queue))) 6218c2ecf20Sopenharmony_ci return -1; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci phys &= ~0x1F; /* mask out non-address bits */ 6248c2ecf20Sopenharmony_ci tab_phys = is_tx ? tx_desc_phys(port, 0) : rx_desc_phys(port, 0); 6258c2ecf20Sopenharmony_ci tab = is_tx ? tx_desc_ptr(port, 0) : rx_desc_ptr(port, 0); 6268c2ecf20Sopenharmony_ci n_desc = (phys - tab_phys) / sizeof(struct desc); 6278c2ecf20Sopenharmony_ci BUG_ON(n_desc >= (is_tx ? TX_DESCS : RX_DESCS)); 6288c2ecf20Sopenharmony_ci debug_desc(phys, &tab[n_desc]); 6298c2ecf20Sopenharmony_ci BUG_ON(tab[n_desc].next); 6308c2ecf20Sopenharmony_ci return n_desc; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic inline void queue_put_desc(unsigned int queue, u32 phys, 6348c2ecf20Sopenharmony_ci struct desc *desc) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci debug_desc(phys, desc); 6378c2ecf20Sopenharmony_ci BUG_ON(phys & 0x1F); 6388c2ecf20Sopenharmony_ci qmgr_put_entry(queue, phys); 6398c2ecf20Sopenharmony_ci /* Don't check for queue overflow here, we've allocated sufficient 6408c2ecf20Sopenharmony_ci length and queues >= 32 don't support this check anyway. */ 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic inline void dma_unmap_tx(struct port *port, struct desc *desc) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 6478c2ecf20Sopenharmony_ci dma_unmap_single(&port->netdev->dev, desc->data, 6488c2ecf20Sopenharmony_ci desc->buf_len, DMA_TO_DEVICE); 6498c2ecf20Sopenharmony_ci#else 6508c2ecf20Sopenharmony_ci dma_unmap_single(&port->netdev->dev, desc->data & ~3, 6518c2ecf20Sopenharmony_ci ALIGN((desc->data & 3) + desc->buf_len, 4), 6528c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6538c2ecf20Sopenharmony_ci#endif 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic void eth_rx_irq(void *pdev) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct net_device *dev = pdev; 6608c2ecf20Sopenharmony_ci struct port *port = netdev_priv(dev); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci#if DEBUG_RX 6638c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: eth_rx_irq\n", dev->name); 6648c2ecf20Sopenharmony_ci#endif 6658c2ecf20Sopenharmony_ci qmgr_disable_irq(port->plat->rxq); 6668c2ecf20Sopenharmony_ci napi_schedule(&port->napi); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic int eth_poll(struct napi_struct *napi, int budget) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct port *port = container_of(napi, struct port, napi); 6728c2ecf20Sopenharmony_ci struct net_device *dev = port->netdev; 6738c2ecf20Sopenharmony_ci unsigned int rxq = port->plat->rxq, rxfreeq = RXFREE_QUEUE(port->id); 6748c2ecf20Sopenharmony_ci int received = 0; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci#if DEBUG_RX 6778c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_poll\n"); 6788c2ecf20Sopenharmony_ci#endif 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci while (received < budget) { 6818c2ecf20Sopenharmony_ci struct sk_buff *skb; 6828c2ecf20Sopenharmony_ci struct desc *desc; 6838c2ecf20Sopenharmony_ci int n; 6848c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 6858c2ecf20Sopenharmony_ci struct sk_buff *temp; 6868c2ecf20Sopenharmony_ci u32 phys; 6878c2ecf20Sopenharmony_ci#endif 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if ((n = queue_get_desc(rxq, port, 0)) < 0) { 6908c2ecf20Sopenharmony_ci#if DEBUG_RX 6918c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_poll napi_complete\n"); 6928c2ecf20Sopenharmony_ci#endif 6938c2ecf20Sopenharmony_ci napi_complete(napi); 6948c2ecf20Sopenharmony_ci qmgr_enable_irq(rxq); 6958c2ecf20Sopenharmony_ci if (!qmgr_stat_below_low_watermark(rxq) && 6968c2ecf20Sopenharmony_ci napi_reschedule(napi)) { /* not empty again */ 6978c2ecf20Sopenharmony_ci#if DEBUG_RX 6988c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_poll napi_reschedule succeeded\n"); 6998c2ecf20Sopenharmony_ci#endif 7008c2ecf20Sopenharmony_ci qmgr_disable_irq(rxq); 7018c2ecf20Sopenharmony_ci continue; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci#if DEBUG_RX 7048c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_poll all done\n"); 7058c2ecf20Sopenharmony_ci#endif 7068c2ecf20Sopenharmony_ci return received; /* all work done */ 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci desc = rx_desc_ptr(port, n); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 7128c2ecf20Sopenharmony_ci if ((skb = netdev_alloc_skb(dev, RX_BUFF_SIZE))) { 7138c2ecf20Sopenharmony_ci phys = dma_map_single(&dev->dev, skb->data, 7148c2ecf20Sopenharmony_ci RX_BUFF_SIZE, DMA_FROM_DEVICE); 7158c2ecf20Sopenharmony_ci if (dma_mapping_error(&dev->dev, phys)) { 7168c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 7178c2ecf20Sopenharmony_ci skb = NULL; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci#else 7218c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, 7228c2ecf20Sopenharmony_ci ALIGN(NET_IP_ALIGN + desc->pkt_len, 4)); 7238c2ecf20Sopenharmony_ci#endif 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (!skb) { 7268c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 7278c2ecf20Sopenharmony_ci /* put the desc back on RX-ready queue */ 7288c2ecf20Sopenharmony_ci desc->buf_len = MAX_MRU; 7298c2ecf20Sopenharmony_ci desc->pkt_len = 0; 7308c2ecf20Sopenharmony_ci queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc); 7318c2ecf20Sopenharmony_ci continue; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* process received frame */ 7358c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 7368c2ecf20Sopenharmony_ci temp = skb; 7378c2ecf20Sopenharmony_ci skb = port->rx_buff_tab[n]; 7388c2ecf20Sopenharmony_ci dma_unmap_single(&dev->dev, desc->data - NET_IP_ALIGN, 7398c2ecf20Sopenharmony_ci RX_BUFF_SIZE, DMA_FROM_DEVICE); 7408c2ecf20Sopenharmony_ci#else 7418c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&dev->dev, desc->data - NET_IP_ALIGN, 7428c2ecf20Sopenharmony_ci RX_BUFF_SIZE, DMA_FROM_DEVICE); 7438c2ecf20Sopenharmony_ci memcpy_swab32((u32 *)skb->data, (u32 *)port->rx_buff_tab[n], 7448c2ecf20Sopenharmony_ci ALIGN(NET_IP_ALIGN + desc->pkt_len, 4) / 4); 7458c2ecf20Sopenharmony_ci#endif 7468c2ecf20Sopenharmony_ci skb_reserve(skb, NET_IP_ALIGN); 7478c2ecf20Sopenharmony_ci skb_put(skb, desc->pkt_len); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci debug_pkt(dev, "eth_poll", skb->data, skb->len); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ixp_rx_timestamp(port, skb); 7528c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 7538c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 7548c2ecf20Sopenharmony_ci dev->stats.rx_bytes += skb->len; 7558c2ecf20Sopenharmony_ci netif_receive_skb(skb); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* put the new buffer on RX-free queue */ 7588c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 7598c2ecf20Sopenharmony_ci port->rx_buff_tab[n] = temp; 7608c2ecf20Sopenharmony_ci desc->data = phys + NET_IP_ALIGN; 7618c2ecf20Sopenharmony_ci#endif 7628c2ecf20Sopenharmony_ci desc->buf_len = MAX_MRU; 7638c2ecf20Sopenharmony_ci desc->pkt_len = 0; 7648c2ecf20Sopenharmony_ci queue_put_desc(rxfreeq, rx_desc_phys(port, n), desc); 7658c2ecf20Sopenharmony_ci received++; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci#if DEBUG_RX 7698c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_poll(): end, not all work done\n"); 7708c2ecf20Sopenharmony_ci#endif 7718c2ecf20Sopenharmony_ci return received; /* not all work done */ 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic void eth_txdone_irq(void *unused) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci u32 phys; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci#if DEBUG_TX 7808c2ecf20Sopenharmony_ci printk(KERN_DEBUG DRV_NAME ": eth_txdone_irq\n"); 7818c2ecf20Sopenharmony_ci#endif 7828c2ecf20Sopenharmony_ci while ((phys = qmgr_get_entry(TXDONE_QUEUE)) != 0) { 7838c2ecf20Sopenharmony_ci u32 npe_id, n_desc; 7848c2ecf20Sopenharmony_ci struct port *port; 7858c2ecf20Sopenharmony_ci struct desc *desc; 7868c2ecf20Sopenharmony_ci int start; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci npe_id = phys & 3; 7898c2ecf20Sopenharmony_ci BUG_ON(npe_id >= MAX_NPES); 7908c2ecf20Sopenharmony_ci port = npe_port_tab[npe_id]; 7918c2ecf20Sopenharmony_ci BUG_ON(!port); 7928c2ecf20Sopenharmony_ci phys &= ~0x1F; /* mask out non-address bits */ 7938c2ecf20Sopenharmony_ci n_desc = (phys - tx_desc_phys(port, 0)) / sizeof(struct desc); 7948c2ecf20Sopenharmony_ci BUG_ON(n_desc >= TX_DESCS); 7958c2ecf20Sopenharmony_ci desc = tx_desc_ptr(port, n_desc); 7968c2ecf20Sopenharmony_ci debug_desc(phys, desc); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (port->tx_buff_tab[n_desc]) { /* not the draining packet */ 7998c2ecf20Sopenharmony_ci port->netdev->stats.tx_packets++; 8008c2ecf20Sopenharmony_ci port->netdev->stats.tx_bytes += desc->pkt_len; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci dma_unmap_tx(port, desc); 8038c2ecf20Sopenharmony_ci#if DEBUG_TX 8048c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: eth_txdone_irq free %p\n", 8058c2ecf20Sopenharmony_ci port->netdev->name, port->tx_buff_tab[n_desc]); 8068c2ecf20Sopenharmony_ci#endif 8078c2ecf20Sopenharmony_ci free_buffer_irq(port->tx_buff_tab[n_desc]); 8088c2ecf20Sopenharmony_ci port->tx_buff_tab[n_desc] = NULL; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci start = qmgr_stat_below_low_watermark(port->plat->txreadyq); 8128c2ecf20Sopenharmony_ci queue_put_desc(port->plat->txreadyq, phys, desc); 8138c2ecf20Sopenharmony_ci if (start) { /* TX-ready queue was empty */ 8148c2ecf20Sopenharmony_ci#if DEBUG_TX 8158c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: eth_txdone_irq xmit ready\n", 8168c2ecf20Sopenharmony_ci port->netdev->name); 8178c2ecf20Sopenharmony_ci#endif 8188c2ecf20Sopenharmony_ci netif_wake_queue(port->netdev); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic int eth_xmit(struct sk_buff *skb, struct net_device *dev) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct port *port = netdev_priv(dev); 8268c2ecf20Sopenharmony_ci unsigned int txreadyq = port->plat->txreadyq; 8278c2ecf20Sopenharmony_ci int len, offset, bytes, n; 8288c2ecf20Sopenharmony_ci void *mem; 8298c2ecf20Sopenharmony_ci u32 phys; 8308c2ecf20Sopenharmony_ci struct desc *desc; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci#if DEBUG_TX 8338c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_xmit\n"); 8348c2ecf20Sopenharmony_ci#endif 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (unlikely(skb->len > MAX_MRU)) { 8378c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8388c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8398c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci debug_pkt(dev, "eth_xmit", skb->data, skb->len); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci len = skb->len; 8458c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 8468c2ecf20Sopenharmony_ci offset = 0; /* no need to keep alignment */ 8478c2ecf20Sopenharmony_ci bytes = len; 8488c2ecf20Sopenharmony_ci mem = skb->data; 8498c2ecf20Sopenharmony_ci#else 8508c2ecf20Sopenharmony_ci offset = (int)skb->data & 3; /* keep 32-bit alignment */ 8518c2ecf20Sopenharmony_ci bytes = ALIGN(offset + len, 4); 8528c2ecf20Sopenharmony_ci if (!(mem = kmalloc(bytes, GFP_ATOMIC))) { 8538c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8548c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 8558c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci memcpy_swab32(mem, (u32 *)((int)skb->data & ~3), bytes / 4); 8588c2ecf20Sopenharmony_ci#endif 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci phys = dma_map_single(&dev->dev, mem, bytes, DMA_TO_DEVICE); 8618c2ecf20Sopenharmony_ci if (dma_mapping_error(&dev->dev, phys)) { 8628c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 8638c2ecf20Sopenharmony_ci#ifndef __ARMEB__ 8648c2ecf20Sopenharmony_ci kfree(mem); 8658c2ecf20Sopenharmony_ci#endif 8668c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 8678c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci n = queue_get_desc(txreadyq, port, 1); 8718c2ecf20Sopenharmony_ci BUG_ON(n < 0); 8728c2ecf20Sopenharmony_ci desc = tx_desc_ptr(port, n); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 8758c2ecf20Sopenharmony_ci port->tx_buff_tab[n] = skb; 8768c2ecf20Sopenharmony_ci#else 8778c2ecf20Sopenharmony_ci port->tx_buff_tab[n] = mem; 8788c2ecf20Sopenharmony_ci#endif 8798c2ecf20Sopenharmony_ci desc->data = phys + offset; 8808c2ecf20Sopenharmony_ci desc->buf_len = desc->pkt_len = len; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* NPE firmware pads short frames with zeros internally */ 8838c2ecf20Sopenharmony_ci wmb(); 8848c2ecf20Sopenharmony_ci queue_put_desc(TX_QUEUE(port->id), tx_desc_phys(port, n), desc); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (qmgr_stat_below_low_watermark(txreadyq)) { /* empty */ 8878c2ecf20Sopenharmony_ci#if DEBUG_TX 8888c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_xmit queue full\n"); 8898c2ecf20Sopenharmony_ci#endif 8908c2ecf20Sopenharmony_ci netif_stop_queue(dev); 8918c2ecf20Sopenharmony_ci /* we could miss TX ready interrupt */ 8928c2ecf20Sopenharmony_ci /* really empty in fact */ 8938c2ecf20Sopenharmony_ci if (!qmgr_stat_below_low_watermark(txreadyq)) { 8948c2ecf20Sopenharmony_ci#if DEBUG_TX 8958c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_xmit ready again\n"); 8968c2ecf20Sopenharmony_ci#endif 8978c2ecf20Sopenharmony_ci netif_wake_queue(dev); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci#if DEBUG_TX 9028c2ecf20Sopenharmony_ci netdev_debug(dev, "eth_xmit end\n"); 9038c2ecf20Sopenharmony_ci#endif 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci ixp_tx_timestamp(port, skb); 9068c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci#ifndef __ARMEB__ 9098c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 9108c2ecf20Sopenharmony_ci#endif 9118c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic void eth_set_mcast_list(struct net_device *dev) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci struct port *port = netdev_priv(dev); 9188c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 9198c2ecf20Sopenharmony_ci u8 diffs[ETH_ALEN], *addr; 9208c2ecf20Sopenharmony_ci int i; 9218c2ecf20Sopenharmony_ci static const u8 allmulti[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if ((dev->flags & IFF_ALLMULTI) && !(dev->flags & IFF_PROMISC)) { 9248c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) { 9258c2ecf20Sopenharmony_ci __raw_writel(allmulti[i], &port->regs->mcast_addr[i]); 9268c2ecf20Sopenharmony_ci __raw_writel(allmulti[i], &port->regs->mcast_mask[i]); 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_RX_CNTRL0 | RX_CNTRL0_ADDR_FLTR_EN, 9298c2ecf20Sopenharmony_ci &port->regs->rx_control[0]); 9308c2ecf20Sopenharmony_ci return; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if ((dev->flags & IFF_PROMISC) || netdev_mc_empty(dev)) { 9348c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_RX_CNTRL0 & ~RX_CNTRL0_ADDR_FLTR_EN, 9358c2ecf20Sopenharmony_ci &port->regs->rx_control[0]); 9368c2ecf20Sopenharmony_ci return; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci eth_zero_addr(diffs); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci addr = NULL; 9428c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 9438c2ecf20Sopenharmony_ci if (!addr) 9448c2ecf20Sopenharmony_ci addr = ha->addr; /* first MAC address */ 9458c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 9468c2ecf20Sopenharmony_ci diffs[i] |= addr[i] ^ ha->addr[i]; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) { 9508c2ecf20Sopenharmony_ci __raw_writel(addr[i], &port->regs->mcast_addr[i]); 9518c2ecf20Sopenharmony_ci __raw_writel(~diffs[i], &port->regs->mcast_mask[i]); 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_RX_CNTRL0 | RX_CNTRL0_ADDR_FLTR_EN, 9558c2ecf20Sopenharmony_ci &port->regs->rx_control[0]); 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic int eth_ioctl(struct net_device *dev, struct ifreq *req, int cmd) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci if (!netif_running(dev)) 9628c2ecf20Sopenharmony_ci return -EINVAL; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (cpu_is_ixp46x()) { 9658c2ecf20Sopenharmony_ci if (cmd == SIOCSHWTSTAMP) 9668c2ecf20Sopenharmony_ci return hwtstamp_set(dev, req); 9678c2ecf20Sopenharmony_ci if (cmd == SIOCGHWTSTAMP) 9688c2ecf20Sopenharmony_ci return hwtstamp_get(dev, req); 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci return phy_mii_ioctl(dev->phydev, req, cmd); 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci/* ethtool support */ 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic void ixp4xx_get_drvinfo(struct net_device *dev, 9778c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci struct port *port = netdev_priv(dev); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 9828c2ecf20Sopenharmony_ci snprintf(info->fw_version, sizeof(info->fw_version), "%u:%u:%u:%u", 9838c2ecf20Sopenharmony_ci port->firmware[0], port->firmware[1], 9848c2ecf20Sopenharmony_ci port->firmware[2], port->firmware[3]); 9858c2ecf20Sopenharmony_ci strlcpy(info->bus_info, "internal", sizeof(info->bus_info)); 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ciint ixp46x_phc_index = -1; 9898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ixp46x_phc_index); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic int ixp4xx_get_ts_info(struct net_device *dev, 9928c2ecf20Sopenharmony_ci struct ethtool_ts_info *info) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci if (!cpu_is_ixp46x()) { 9958c2ecf20Sopenharmony_ci info->so_timestamping = 9968c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_TX_SOFTWARE | 9978c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_SOFTWARE | 9988c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_SOFTWARE; 9998c2ecf20Sopenharmony_ci info->phc_index = -1; 10008c2ecf20Sopenharmony_ci return 0; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci info->so_timestamping = 10038c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_TX_HARDWARE | 10048c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RX_HARDWARE | 10058c2ecf20Sopenharmony_ci SOF_TIMESTAMPING_RAW_HARDWARE; 10068c2ecf20Sopenharmony_ci info->phc_index = ixp46x_phc_index; 10078c2ecf20Sopenharmony_ci info->tx_types = 10088c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_OFF) | 10098c2ecf20Sopenharmony_ci (1 << HWTSTAMP_TX_ON); 10108c2ecf20Sopenharmony_ci info->rx_filters = 10118c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_NONE) | 10128c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | 10138c2ecf20Sopenharmony_ci (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ); 10148c2ecf20Sopenharmony_ci return 0; 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic const struct ethtool_ops ixp4xx_ethtool_ops = { 10188c2ecf20Sopenharmony_ci .get_drvinfo = ixp4xx_get_drvinfo, 10198c2ecf20Sopenharmony_ci .nway_reset = phy_ethtool_nway_reset, 10208c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 10218c2ecf20Sopenharmony_ci .get_ts_info = ixp4xx_get_ts_info, 10228c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 10238c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 10248c2ecf20Sopenharmony_ci}; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic int request_queues(struct port *port) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci int err; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci err = qmgr_request_queue(RXFREE_QUEUE(port->id), RX_DESCS, 0, 0, 10328c2ecf20Sopenharmony_ci "%s:RX-free", port->netdev->name); 10338c2ecf20Sopenharmony_ci if (err) 10348c2ecf20Sopenharmony_ci return err; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci err = qmgr_request_queue(port->plat->rxq, RX_DESCS, 0, 0, 10378c2ecf20Sopenharmony_ci "%s:RX", port->netdev->name); 10388c2ecf20Sopenharmony_ci if (err) 10398c2ecf20Sopenharmony_ci goto rel_rxfree; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci err = qmgr_request_queue(TX_QUEUE(port->id), TX_DESCS, 0, 0, 10428c2ecf20Sopenharmony_ci "%s:TX", port->netdev->name); 10438c2ecf20Sopenharmony_ci if (err) 10448c2ecf20Sopenharmony_ci goto rel_rx; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci err = qmgr_request_queue(port->plat->txreadyq, TX_DESCS, 0, 0, 10478c2ecf20Sopenharmony_ci "%s:TX-ready", port->netdev->name); 10488c2ecf20Sopenharmony_ci if (err) 10498c2ecf20Sopenharmony_ci goto rel_tx; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* TX-done queue handles skbs sent out by the NPEs */ 10528c2ecf20Sopenharmony_ci if (!ports_open) { 10538c2ecf20Sopenharmony_ci err = qmgr_request_queue(TXDONE_QUEUE, TXDONE_QUEUE_LEN, 0, 0, 10548c2ecf20Sopenharmony_ci "%s:TX-done", DRV_NAME); 10558c2ecf20Sopenharmony_ci if (err) 10568c2ecf20Sopenharmony_ci goto rel_txready; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci return 0; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cirel_txready: 10618c2ecf20Sopenharmony_ci qmgr_release_queue(port->plat->txreadyq); 10628c2ecf20Sopenharmony_cirel_tx: 10638c2ecf20Sopenharmony_ci qmgr_release_queue(TX_QUEUE(port->id)); 10648c2ecf20Sopenharmony_cirel_rx: 10658c2ecf20Sopenharmony_ci qmgr_release_queue(port->plat->rxq); 10668c2ecf20Sopenharmony_cirel_rxfree: 10678c2ecf20Sopenharmony_ci qmgr_release_queue(RXFREE_QUEUE(port->id)); 10688c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: unable to request hardware queues\n", 10698c2ecf20Sopenharmony_ci port->netdev->name); 10708c2ecf20Sopenharmony_ci return err; 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void release_queues(struct port *port) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci qmgr_release_queue(RXFREE_QUEUE(port->id)); 10768c2ecf20Sopenharmony_ci qmgr_release_queue(port->plat->rxq); 10778c2ecf20Sopenharmony_ci qmgr_release_queue(TX_QUEUE(port->id)); 10788c2ecf20Sopenharmony_ci qmgr_release_queue(port->plat->txreadyq); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci if (!ports_open) 10818c2ecf20Sopenharmony_ci qmgr_release_queue(TXDONE_QUEUE); 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic int init_queues(struct port *port) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci int i; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (!ports_open) { 10898c2ecf20Sopenharmony_ci dma_pool = dma_pool_create(DRV_NAME, &port->netdev->dev, 10908c2ecf20Sopenharmony_ci POOL_ALLOC_SIZE, 32, 0); 10918c2ecf20Sopenharmony_ci if (!dma_pool) 10928c2ecf20Sopenharmony_ci return -ENOMEM; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (!(port->desc_tab = dma_pool_alloc(dma_pool, GFP_KERNEL, 10968c2ecf20Sopenharmony_ci &port->desc_tab_phys))) 10978c2ecf20Sopenharmony_ci return -ENOMEM; 10988c2ecf20Sopenharmony_ci memset(port->desc_tab, 0, POOL_ALLOC_SIZE); 10998c2ecf20Sopenharmony_ci memset(port->rx_buff_tab, 0, sizeof(port->rx_buff_tab)); /* tables */ 11008c2ecf20Sopenharmony_ci memset(port->tx_buff_tab, 0, sizeof(port->tx_buff_tab)); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci /* Setup RX buffers */ 11038c2ecf20Sopenharmony_ci for (i = 0; i < RX_DESCS; i++) { 11048c2ecf20Sopenharmony_ci struct desc *desc = rx_desc_ptr(port, i); 11058c2ecf20Sopenharmony_ci buffer_t *buff; /* skb or kmalloc()ated memory */ 11068c2ecf20Sopenharmony_ci void *data; 11078c2ecf20Sopenharmony_ci#ifdef __ARMEB__ 11088c2ecf20Sopenharmony_ci if (!(buff = netdev_alloc_skb(port->netdev, RX_BUFF_SIZE))) 11098c2ecf20Sopenharmony_ci return -ENOMEM; 11108c2ecf20Sopenharmony_ci data = buff->data; 11118c2ecf20Sopenharmony_ci#else 11128c2ecf20Sopenharmony_ci if (!(buff = kmalloc(RX_BUFF_SIZE, GFP_KERNEL))) 11138c2ecf20Sopenharmony_ci return -ENOMEM; 11148c2ecf20Sopenharmony_ci data = buff; 11158c2ecf20Sopenharmony_ci#endif 11168c2ecf20Sopenharmony_ci desc->buf_len = MAX_MRU; 11178c2ecf20Sopenharmony_ci desc->data = dma_map_single(&port->netdev->dev, data, 11188c2ecf20Sopenharmony_ci RX_BUFF_SIZE, DMA_FROM_DEVICE); 11198c2ecf20Sopenharmony_ci if (dma_mapping_error(&port->netdev->dev, desc->data)) { 11208c2ecf20Sopenharmony_ci free_buffer(buff); 11218c2ecf20Sopenharmony_ci return -EIO; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci desc->data += NET_IP_ALIGN; 11248c2ecf20Sopenharmony_ci port->rx_buff_tab[i] = buff; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci return 0; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cistatic void destroy_queues(struct port *port) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci int i; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (port->desc_tab) { 11358c2ecf20Sopenharmony_ci for (i = 0; i < RX_DESCS; i++) { 11368c2ecf20Sopenharmony_ci struct desc *desc = rx_desc_ptr(port, i); 11378c2ecf20Sopenharmony_ci buffer_t *buff = port->rx_buff_tab[i]; 11388c2ecf20Sopenharmony_ci if (buff) { 11398c2ecf20Sopenharmony_ci dma_unmap_single(&port->netdev->dev, 11408c2ecf20Sopenharmony_ci desc->data - NET_IP_ALIGN, 11418c2ecf20Sopenharmony_ci RX_BUFF_SIZE, DMA_FROM_DEVICE); 11428c2ecf20Sopenharmony_ci free_buffer(buff); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci for (i = 0; i < TX_DESCS; i++) { 11468c2ecf20Sopenharmony_ci struct desc *desc = tx_desc_ptr(port, i); 11478c2ecf20Sopenharmony_ci buffer_t *buff = port->tx_buff_tab[i]; 11488c2ecf20Sopenharmony_ci if (buff) { 11498c2ecf20Sopenharmony_ci dma_unmap_tx(port, desc); 11508c2ecf20Sopenharmony_ci free_buffer(buff); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci dma_pool_free(dma_pool, port->desc_tab, port->desc_tab_phys); 11548c2ecf20Sopenharmony_ci port->desc_tab = NULL; 11558c2ecf20Sopenharmony_ci } 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (!ports_open && dma_pool) { 11588c2ecf20Sopenharmony_ci dma_pool_destroy(dma_pool); 11598c2ecf20Sopenharmony_ci dma_pool = NULL; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic int eth_open(struct net_device *dev) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct port *port = netdev_priv(dev); 11668c2ecf20Sopenharmony_ci struct npe *npe = port->npe; 11678c2ecf20Sopenharmony_ci struct msg msg; 11688c2ecf20Sopenharmony_ci int i, err; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (!npe_running(npe)) { 11718c2ecf20Sopenharmony_ci err = npe_load_firmware(npe, npe_name(npe), &dev->dev); 11728c2ecf20Sopenharmony_ci if (err) 11738c2ecf20Sopenharmony_ci return err; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (npe_recv_message(npe, &msg, "ETH_GET_STATUS")) { 11768c2ecf20Sopenharmony_ci netdev_err(dev, "%s not responding\n", npe_name(npe)); 11778c2ecf20Sopenharmony_ci return -EIO; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci port->firmware[0] = msg.byte4; 11808c2ecf20Sopenharmony_ci port->firmware[1] = msg.byte5; 11818c2ecf20Sopenharmony_ci port->firmware[2] = msg.byte6; 11828c2ecf20Sopenharmony_ci port->firmware[3] = msg.byte7; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 11868c2ecf20Sopenharmony_ci msg.cmd = NPE_VLAN_SETRXQOSENTRY; 11878c2ecf20Sopenharmony_ci msg.eth_id = port->id; 11888c2ecf20Sopenharmony_ci msg.byte5 = port->plat->rxq | 0x80; 11898c2ecf20Sopenharmony_ci msg.byte7 = port->plat->rxq << 4; 11908c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 11918c2ecf20Sopenharmony_ci msg.byte3 = i; 11928c2ecf20Sopenharmony_ci if (npe_send_recv_message(port->npe, &msg, "ETH_SET_RXQ")) 11938c2ecf20Sopenharmony_ci return -EIO; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci msg.cmd = NPE_EDB_SETPORTADDRESS; 11978c2ecf20Sopenharmony_ci msg.eth_id = PHYSICAL_ID(port->id); 11988c2ecf20Sopenharmony_ci msg.byte2 = dev->dev_addr[0]; 11998c2ecf20Sopenharmony_ci msg.byte3 = dev->dev_addr[1]; 12008c2ecf20Sopenharmony_ci msg.byte4 = dev->dev_addr[2]; 12018c2ecf20Sopenharmony_ci msg.byte5 = dev->dev_addr[3]; 12028c2ecf20Sopenharmony_ci msg.byte6 = dev->dev_addr[4]; 12038c2ecf20Sopenharmony_ci msg.byte7 = dev->dev_addr[5]; 12048c2ecf20Sopenharmony_ci if (npe_send_recv_message(port->npe, &msg, "ETH_SET_MAC")) 12058c2ecf20Sopenharmony_ci return -EIO; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 12088c2ecf20Sopenharmony_ci msg.cmd = NPE_FW_SETFIREWALLMODE; 12098c2ecf20Sopenharmony_ci msg.eth_id = port->id; 12108c2ecf20Sopenharmony_ci if (npe_send_recv_message(port->npe, &msg, "ETH_SET_FIREWALL_MODE")) 12118c2ecf20Sopenharmony_ci return -EIO; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if ((err = request_queues(port)) != 0) 12148c2ecf20Sopenharmony_ci return err; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if ((err = init_queues(port)) != 0) { 12178c2ecf20Sopenharmony_ci destroy_queues(port); 12188c2ecf20Sopenharmony_ci release_queues(port); 12198c2ecf20Sopenharmony_ci return err; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci port->speed = 0; /* force "link up" message */ 12238c2ecf20Sopenharmony_ci phy_start(dev->phydev); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 12268c2ecf20Sopenharmony_ci __raw_writel(dev->dev_addr[i], &port->regs->hw_addr[i]); 12278c2ecf20Sopenharmony_ci __raw_writel(0x08, &port->regs->random_seed); 12288c2ecf20Sopenharmony_ci __raw_writel(0x12, &port->regs->partial_empty_threshold); 12298c2ecf20Sopenharmony_ci __raw_writel(0x30, &port->regs->partial_full_threshold); 12308c2ecf20Sopenharmony_ci __raw_writel(0x08, &port->regs->tx_start_bytes); 12318c2ecf20Sopenharmony_ci __raw_writel(0x15, &port->regs->tx_deferral); 12328c2ecf20Sopenharmony_ci __raw_writel(0x08, &port->regs->tx_2part_deferral[0]); 12338c2ecf20Sopenharmony_ci __raw_writel(0x07, &port->regs->tx_2part_deferral[1]); 12348c2ecf20Sopenharmony_ci __raw_writel(0x80, &port->regs->slot_time); 12358c2ecf20Sopenharmony_ci __raw_writel(0x01, &port->regs->int_clock_threshold); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* Populate queues with buffers, no failure after this point */ 12388c2ecf20Sopenharmony_ci for (i = 0; i < TX_DESCS; i++) 12398c2ecf20Sopenharmony_ci queue_put_desc(port->plat->txreadyq, 12408c2ecf20Sopenharmony_ci tx_desc_phys(port, i), tx_desc_ptr(port, i)); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci for (i = 0; i < RX_DESCS; i++) 12438c2ecf20Sopenharmony_ci queue_put_desc(RXFREE_QUEUE(port->id), 12448c2ecf20Sopenharmony_ci rx_desc_phys(port, i), rx_desc_ptr(port, i)); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci __raw_writel(TX_CNTRL1_RETRIES, &port->regs->tx_control[1]); 12478c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_TX_CNTRL0, &port->regs->tx_control[0]); 12488c2ecf20Sopenharmony_ci __raw_writel(0, &port->regs->rx_control[1]); 12498c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_RX_CNTRL0, &port->regs->rx_control[0]); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci napi_enable(&port->napi); 12528c2ecf20Sopenharmony_ci eth_set_mcast_list(dev); 12538c2ecf20Sopenharmony_ci netif_start_queue(dev); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci qmgr_set_irq(port->plat->rxq, QUEUE_IRQ_SRC_NOT_EMPTY, 12568c2ecf20Sopenharmony_ci eth_rx_irq, dev); 12578c2ecf20Sopenharmony_ci if (!ports_open) { 12588c2ecf20Sopenharmony_ci qmgr_set_irq(TXDONE_QUEUE, QUEUE_IRQ_SRC_NOT_EMPTY, 12598c2ecf20Sopenharmony_ci eth_txdone_irq, NULL); 12608c2ecf20Sopenharmony_ci qmgr_enable_irq(TXDONE_QUEUE); 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci ports_open++; 12638c2ecf20Sopenharmony_ci /* we may already have RX data, enables IRQ */ 12648c2ecf20Sopenharmony_ci napi_schedule(&port->napi); 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic int eth_close(struct net_device *dev) 12698c2ecf20Sopenharmony_ci{ 12708c2ecf20Sopenharmony_ci struct port *port = netdev_priv(dev); 12718c2ecf20Sopenharmony_ci struct msg msg; 12728c2ecf20Sopenharmony_ci int buffs = RX_DESCS; /* allocated RX buffers */ 12738c2ecf20Sopenharmony_ci int i; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci ports_open--; 12768c2ecf20Sopenharmony_ci qmgr_disable_irq(port->plat->rxq); 12778c2ecf20Sopenharmony_ci napi_disable(&port->napi); 12788c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci while (queue_get_desc(RXFREE_QUEUE(port->id), port, 0) >= 0) 12818c2ecf20Sopenharmony_ci buffs--; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 12848c2ecf20Sopenharmony_ci msg.cmd = NPE_SETLOOPBACK_MODE; 12858c2ecf20Sopenharmony_ci msg.eth_id = port->id; 12868c2ecf20Sopenharmony_ci msg.byte3 = 1; 12878c2ecf20Sopenharmony_ci if (npe_send_recv_message(port->npe, &msg, "ETH_ENABLE_LOOPBACK")) 12888c2ecf20Sopenharmony_ci netdev_crit(dev, "unable to enable loopback\n"); 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci i = 0; 12918c2ecf20Sopenharmony_ci do { /* drain RX buffers */ 12928c2ecf20Sopenharmony_ci while (queue_get_desc(port->plat->rxq, port, 0) >= 0) 12938c2ecf20Sopenharmony_ci buffs--; 12948c2ecf20Sopenharmony_ci if (!buffs) 12958c2ecf20Sopenharmony_ci break; 12968c2ecf20Sopenharmony_ci if (qmgr_stat_empty(TX_QUEUE(port->id))) { 12978c2ecf20Sopenharmony_ci /* we have to inject some packet */ 12988c2ecf20Sopenharmony_ci struct desc *desc; 12998c2ecf20Sopenharmony_ci u32 phys; 13008c2ecf20Sopenharmony_ci int n = queue_get_desc(port->plat->txreadyq, port, 1); 13018c2ecf20Sopenharmony_ci BUG_ON(n < 0); 13028c2ecf20Sopenharmony_ci desc = tx_desc_ptr(port, n); 13038c2ecf20Sopenharmony_ci phys = tx_desc_phys(port, n); 13048c2ecf20Sopenharmony_ci desc->buf_len = desc->pkt_len = 1; 13058c2ecf20Sopenharmony_ci wmb(); 13068c2ecf20Sopenharmony_ci queue_put_desc(TX_QUEUE(port->id), phys, desc); 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci udelay(1); 13098c2ecf20Sopenharmony_ci } while (++i < MAX_CLOSE_WAIT); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (buffs) 13128c2ecf20Sopenharmony_ci netdev_crit(dev, "unable to drain RX queue, %i buffer(s)" 13138c2ecf20Sopenharmony_ci " left in NPE\n", buffs); 13148c2ecf20Sopenharmony_ci#if DEBUG_CLOSE 13158c2ecf20Sopenharmony_ci if (!buffs) 13168c2ecf20Sopenharmony_ci netdev_debug(dev, "draining RX queue took %i cycles\n", i); 13178c2ecf20Sopenharmony_ci#endif 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci buffs = TX_DESCS; 13208c2ecf20Sopenharmony_ci while (queue_get_desc(TX_QUEUE(port->id), port, 1) >= 0) 13218c2ecf20Sopenharmony_ci buffs--; /* cancel TX */ 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci i = 0; 13248c2ecf20Sopenharmony_ci do { 13258c2ecf20Sopenharmony_ci while (queue_get_desc(port->plat->txreadyq, port, 1) >= 0) 13268c2ecf20Sopenharmony_ci buffs--; 13278c2ecf20Sopenharmony_ci if (!buffs) 13288c2ecf20Sopenharmony_ci break; 13298c2ecf20Sopenharmony_ci } while (++i < MAX_CLOSE_WAIT); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci if (buffs) 13328c2ecf20Sopenharmony_ci netdev_crit(dev, "unable to drain TX queue, %i buffer(s) " 13338c2ecf20Sopenharmony_ci "left in NPE\n", buffs); 13348c2ecf20Sopenharmony_ci#if DEBUG_CLOSE 13358c2ecf20Sopenharmony_ci if (!buffs) 13368c2ecf20Sopenharmony_ci netdev_debug(dev, "draining TX queues took %i cycles\n", i); 13378c2ecf20Sopenharmony_ci#endif 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci msg.byte3 = 0; 13408c2ecf20Sopenharmony_ci if (npe_send_recv_message(port->npe, &msg, "ETH_DISABLE_LOOPBACK")) 13418c2ecf20Sopenharmony_ci netdev_crit(dev, "unable to disable loopback\n"); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci phy_stop(dev->phydev); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (!ports_open) 13468c2ecf20Sopenharmony_ci qmgr_disable_irq(TXDONE_QUEUE); 13478c2ecf20Sopenharmony_ci destroy_queues(port); 13488c2ecf20Sopenharmony_ci release_queues(port); 13498c2ecf20Sopenharmony_ci return 0; 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_cistatic const struct net_device_ops ixp4xx_netdev_ops = { 13538c2ecf20Sopenharmony_ci .ndo_open = eth_open, 13548c2ecf20Sopenharmony_ci .ndo_stop = eth_close, 13558c2ecf20Sopenharmony_ci .ndo_start_xmit = eth_xmit, 13568c2ecf20Sopenharmony_ci .ndo_set_rx_mode = eth_set_mcast_list, 13578c2ecf20Sopenharmony_ci .ndo_do_ioctl = eth_ioctl, 13588c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 13598c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 13608c2ecf20Sopenharmony_ci}; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_cistatic int ixp4xx_eth_probe(struct platform_device *pdev) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci char phy_id[MII_BUS_ID_SIZE + 3]; 13658c2ecf20Sopenharmony_ci struct phy_device *phydev = NULL; 13668c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 13678c2ecf20Sopenharmony_ci struct eth_plat_info *plat; 13688c2ecf20Sopenharmony_ci resource_size_t regs_phys; 13698c2ecf20Sopenharmony_ci struct net_device *ndev; 13708c2ecf20Sopenharmony_ci struct resource *res; 13718c2ecf20Sopenharmony_ci struct port *port; 13728c2ecf20Sopenharmony_ci int err; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci plat = dev_get_platdata(dev); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci if (!(ndev = devm_alloc_etherdev(dev, sizeof(struct port)))) 13778c2ecf20Sopenharmony_ci return -ENOMEM; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, dev); 13808c2ecf20Sopenharmony_ci port = netdev_priv(ndev); 13818c2ecf20Sopenharmony_ci port->netdev = ndev; 13828c2ecf20Sopenharmony_ci port->id = pdev->id; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci /* Get the port resource and remap */ 13858c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 13868c2ecf20Sopenharmony_ci if (!res) 13878c2ecf20Sopenharmony_ci return -ENODEV; 13888c2ecf20Sopenharmony_ci regs_phys = res->start; 13898c2ecf20Sopenharmony_ci port->regs = devm_ioremap_resource(dev, res); 13908c2ecf20Sopenharmony_ci if (IS_ERR(port->regs)) 13918c2ecf20Sopenharmony_ci return PTR_ERR(port->regs); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci switch (port->id) { 13948c2ecf20Sopenharmony_ci case IXP4XX_ETH_NPEA: 13958c2ecf20Sopenharmony_ci /* If the MDIO bus is not up yet, defer probe */ 13968c2ecf20Sopenharmony_ci if (!mdio_bus) 13978c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 13988c2ecf20Sopenharmony_ci break; 13998c2ecf20Sopenharmony_ci case IXP4XX_ETH_NPEB: 14008c2ecf20Sopenharmony_ci /* 14018c2ecf20Sopenharmony_ci * On all except IXP43x, NPE-B is used for the MDIO bus. 14028c2ecf20Sopenharmony_ci * If there is no NPE-B in the feature set, bail out, else 14038c2ecf20Sopenharmony_ci * register the MDIO bus. 14048c2ecf20Sopenharmony_ci */ 14058c2ecf20Sopenharmony_ci if (!cpu_is_ixp43x()) { 14068c2ecf20Sopenharmony_ci if (!(ixp4xx_read_feature_bits() & 14078c2ecf20Sopenharmony_ci IXP4XX_FEATURE_NPEB_ETH0)) 14088c2ecf20Sopenharmony_ci return -ENODEV; 14098c2ecf20Sopenharmony_ci /* Else register the MDIO bus on NPE-B */ 14108c2ecf20Sopenharmony_ci if ((err = ixp4xx_mdio_register(port->regs))) 14118c2ecf20Sopenharmony_ci return err; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci if (!mdio_bus) 14148c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 14158c2ecf20Sopenharmony_ci break; 14168c2ecf20Sopenharmony_ci case IXP4XX_ETH_NPEC: 14178c2ecf20Sopenharmony_ci /* 14188c2ecf20Sopenharmony_ci * IXP43x lacks NPE-B and uses NPE-C for the MDIO bus access, 14198c2ecf20Sopenharmony_ci * of there is no NPE-C, no bus, nothing works, so bail out. 14208c2ecf20Sopenharmony_ci */ 14218c2ecf20Sopenharmony_ci if (cpu_is_ixp43x()) { 14228c2ecf20Sopenharmony_ci if (!(ixp4xx_read_feature_bits() & 14238c2ecf20Sopenharmony_ci IXP4XX_FEATURE_NPEC_ETH)) 14248c2ecf20Sopenharmony_ci return -ENODEV; 14258c2ecf20Sopenharmony_ci /* Else register the MDIO bus on NPE-C */ 14268c2ecf20Sopenharmony_ci if ((err = ixp4xx_mdio_register(port->regs))) 14278c2ecf20Sopenharmony_ci return err; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci if (!mdio_bus) 14308c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 14318c2ecf20Sopenharmony_ci break; 14328c2ecf20Sopenharmony_ci default: 14338c2ecf20Sopenharmony_ci return -ENODEV; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci ndev->netdev_ops = &ixp4xx_netdev_ops; 14378c2ecf20Sopenharmony_ci ndev->ethtool_ops = &ixp4xx_ethtool_ops; 14388c2ecf20Sopenharmony_ci ndev->tx_queue_len = 100; 14398c2ecf20Sopenharmony_ci /* Inherit the DMA masks from the platform device */ 14408c2ecf20Sopenharmony_ci ndev->dev.dma_mask = dev->dma_mask; 14418c2ecf20Sopenharmony_ci ndev->dev.coherent_dma_mask = dev->coherent_dma_mask; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci netif_napi_add(ndev, &port->napi, eth_poll, NAPI_WEIGHT); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci if (!(port->npe = npe_request(NPE_ID(port->id)))) 14468c2ecf20Sopenharmony_ci return -EIO; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci port->mem_res = request_mem_region(regs_phys, REGS_SIZE, ndev->name); 14498c2ecf20Sopenharmony_ci if (!port->mem_res) { 14508c2ecf20Sopenharmony_ci err = -EBUSY; 14518c2ecf20Sopenharmony_ci goto err_npe_rel; 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci port->plat = plat; 14558c2ecf20Sopenharmony_ci npe_port_tab[NPE_ID(port->id)] = port; 14568c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, plat->hwaddr, ETH_ALEN); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ndev); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_CORE_CNTRL | CORE_RESET, 14618c2ecf20Sopenharmony_ci &port->regs->core_control); 14628c2ecf20Sopenharmony_ci udelay(50); 14638c2ecf20Sopenharmony_ci __raw_writel(DEFAULT_CORE_CNTRL, &port->regs->core_control); 14648c2ecf20Sopenharmony_ci udelay(50); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, 14678c2ecf20Sopenharmony_ci mdio_bus->id, plat->phy); 14688c2ecf20Sopenharmony_ci phydev = phy_connect(ndev, phy_id, &ixp4xx_adjust_link, 14698c2ecf20Sopenharmony_ci PHY_INTERFACE_MODE_MII); 14708c2ecf20Sopenharmony_ci if (IS_ERR(phydev)) { 14718c2ecf20Sopenharmony_ci err = PTR_ERR(phydev); 14728c2ecf20Sopenharmony_ci goto err_free_mem; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci phydev->irq = PHY_POLL; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if ((err = register_netdev(ndev))) 14788c2ecf20Sopenharmony_ci goto err_phy_dis; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci netdev_info(ndev, "%s: MII PHY %i on %s\n", ndev->name, plat->phy, 14818c2ecf20Sopenharmony_ci npe_name(port->npe)); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci return 0; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cierr_phy_dis: 14868c2ecf20Sopenharmony_ci phy_disconnect(phydev); 14878c2ecf20Sopenharmony_cierr_free_mem: 14888c2ecf20Sopenharmony_ci npe_port_tab[NPE_ID(port->id)] = NULL; 14898c2ecf20Sopenharmony_ci release_resource(port->mem_res); 14908c2ecf20Sopenharmony_cierr_npe_rel: 14918c2ecf20Sopenharmony_ci npe_release(port->npe); 14928c2ecf20Sopenharmony_ci return err; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic int ixp4xx_eth_remove(struct platform_device *pdev) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 14988c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 14998c2ecf20Sopenharmony_ci struct port *port = netdev_priv(ndev); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci unregister_netdev(ndev); 15028c2ecf20Sopenharmony_ci phy_disconnect(phydev); 15038c2ecf20Sopenharmony_ci ixp4xx_mdio_remove(); 15048c2ecf20Sopenharmony_ci npe_port_tab[NPE_ID(port->id)] = NULL; 15058c2ecf20Sopenharmony_ci npe_release(port->npe); 15068c2ecf20Sopenharmony_ci release_resource(port->mem_res); 15078c2ecf20Sopenharmony_ci return 0; 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic struct platform_driver ixp4xx_eth_driver = { 15118c2ecf20Sopenharmony_ci .driver.name = DRV_NAME, 15128c2ecf20Sopenharmony_ci .probe = ixp4xx_eth_probe, 15138c2ecf20Sopenharmony_ci .remove = ixp4xx_eth_remove, 15148c2ecf20Sopenharmony_ci}; 15158c2ecf20Sopenharmony_cimodule_platform_driver(ixp4xx_eth_driver); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Halasa"); 15188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel IXP4xx Ethernet driver"); 15198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 15208c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ixp4xx_eth"); 1521