18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/ethernet/nxp/lpc_eth.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Kevin Wells <kevin.wells@nxp.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2010 NXP Semiconductors 88c2ecf20Sopenharmony_ci * Copyright (C) 2012 Roland Stigge <stigge@antcom.de> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/crc32.h> 158c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 198c2ecf20Sopenharmony_ci#include <linux/of_net.h> 208c2ecf20Sopenharmony_ci#include <linux/phy.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/soc/nxp/lpc32xx-misc.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MODNAME "lpc-eth" 268c2ecf20Sopenharmony_ci#define DRV_VERSION "1.00" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define ENET_MAXF_SIZE 1536 298c2ecf20Sopenharmony_ci#define ENET_RX_DESC 48 308c2ecf20Sopenharmony_ci#define ENET_TX_DESC 16 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define NAPI_WEIGHT 16 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * Ethernet MAC controller Register offsets 368c2ecf20Sopenharmony_ci */ 378c2ecf20Sopenharmony_ci#define LPC_ENET_MAC1(x) (x + 0x000) 388c2ecf20Sopenharmony_ci#define LPC_ENET_MAC2(x) (x + 0x004) 398c2ecf20Sopenharmony_ci#define LPC_ENET_IPGT(x) (x + 0x008) 408c2ecf20Sopenharmony_ci#define LPC_ENET_IPGR(x) (x + 0x00C) 418c2ecf20Sopenharmony_ci#define LPC_ENET_CLRT(x) (x + 0x010) 428c2ecf20Sopenharmony_ci#define LPC_ENET_MAXF(x) (x + 0x014) 438c2ecf20Sopenharmony_ci#define LPC_ENET_SUPP(x) (x + 0x018) 448c2ecf20Sopenharmony_ci#define LPC_ENET_TEST(x) (x + 0x01C) 458c2ecf20Sopenharmony_ci#define LPC_ENET_MCFG(x) (x + 0x020) 468c2ecf20Sopenharmony_ci#define LPC_ENET_MCMD(x) (x + 0x024) 478c2ecf20Sopenharmony_ci#define LPC_ENET_MADR(x) (x + 0x028) 488c2ecf20Sopenharmony_ci#define LPC_ENET_MWTD(x) (x + 0x02C) 498c2ecf20Sopenharmony_ci#define LPC_ENET_MRDD(x) (x + 0x030) 508c2ecf20Sopenharmony_ci#define LPC_ENET_MIND(x) (x + 0x034) 518c2ecf20Sopenharmony_ci#define LPC_ENET_SA0(x) (x + 0x040) 528c2ecf20Sopenharmony_ci#define LPC_ENET_SA1(x) (x + 0x044) 538c2ecf20Sopenharmony_ci#define LPC_ENET_SA2(x) (x + 0x048) 548c2ecf20Sopenharmony_ci#define LPC_ENET_COMMAND(x) (x + 0x100) 558c2ecf20Sopenharmony_ci#define LPC_ENET_STATUS(x) (x + 0x104) 568c2ecf20Sopenharmony_ci#define LPC_ENET_RXDESCRIPTOR(x) (x + 0x108) 578c2ecf20Sopenharmony_ci#define LPC_ENET_RXSTATUS(x) (x + 0x10C) 588c2ecf20Sopenharmony_ci#define LPC_ENET_RXDESCRIPTORNUMBER(x) (x + 0x110) 598c2ecf20Sopenharmony_ci#define LPC_ENET_RXPRODUCEINDEX(x) (x + 0x114) 608c2ecf20Sopenharmony_ci#define LPC_ENET_RXCONSUMEINDEX(x) (x + 0x118) 618c2ecf20Sopenharmony_ci#define LPC_ENET_TXDESCRIPTOR(x) (x + 0x11C) 628c2ecf20Sopenharmony_ci#define LPC_ENET_TXSTATUS(x) (x + 0x120) 638c2ecf20Sopenharmony_ci#define LPC_ENET_TXDESCRIPTORNUMBER(x) (x + 0x124) 648c2ecf20Sopenharmony_ci#define LPC_ENET_TXPRODUCEINDEX(x) (x + 0x128) 658c2ecf20Sopenharmony_ci#define LPC_ENET_TXCONSUMEINDEX(x) (x + 0x12C) 668c2ecf20Sopenharmony_ci#define LPC_ENET_TSV0(x) (x + 0x158) 678c2ecf20Sopenharmony_ci#define LPC_ENET_TSV1(x) (x + 0x15C) 688c2ecf20Sopenharmony_ci#define LPC_ENET_RSV(x) (x + 0x160) 698c2ecf20Sopenharmony_ci#define LPC_ENET_FLOWCONTROLCOUNTER(x) (x + 0x170) 708c2ecf20Sopenharmony_ci#define LPC_ENET_FLOWCONTROLSTATUS(x) (x + 0x174) 718c2ecf20Sopenharmony_ci#define LPC_ENET_RXFILTER_CTRL(x) (x + 0x200) 728c2ecf20Sopenharmony_ci#define LPC_ENET_RXFILTERWOLSTATUS(x) (x + 0x204) 738c2ecf20Sopenharmony_ci#define LPC_ENET_RXFILTERWOLCLEAR(x) (x + 0x208) 748c2ecf20Sopenharmony_ci#define LPC_ENET_HASHFILTERL(x) (x + 0x210) 758c2ecf20Sopenharmony_ci#define LPC_ENET_HASHFILTERH(x) (x + 0x214) 768c2ecf20Sopenharmony_ci#define LPC_ENET_INTSTATUS(x) (x + 0xFE0) 778c2ecf20Sopenharmony_ci#define LPC_ENET_INTENABLE(x) (x + 0xFE4) 788c2ecf20Sopenharmony_ci#define LPC_ENET_INTCLEAR(x) (x + 0xFE8) 798c2ecf20Sopenharmony_ci#define LPC_ENET_INTSET(x) (x + 0xFEC) 808c2ecf20Sopenharmony_ci#define LPC_ENET_POWERDOWN(x) (x + 0xFF4) 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* 838c2ecf20Sopenharmony_ci * mac1 register definitions 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci#define LPC_MAC1_RECV_ENABLE (1 << 0) 868c2ecf20Sopenharmony_ci#define LPC_MAC1_PASS_ALL_RX_FRAMES (1 << 1) 878c2ecf20Sopenharmony_ci#define LPC_MAC1_RX_FLOW_CONTROL (1 << 2) 888c2ecf20Sopenharmony_ci#define LPC_MAC1_TX_FLOW_CONTROL (1 << 3) 898c2ecf20Sopenharmony_ci#define LPC_MAC1_LOOPBACK (1 << 4) 908c2ecf20Sopenharmony_ci#define LPC_MAC1_RESET_TX (1 << 8) 918c2ecf20Sopenharmony_ci#define LPC_MAC1_RESET_MCS_TX (1 << 9) 928c2ecf20Sopenharmony_ci#define LPC_MAC1_RESET_RX (1 << 10) 938c2ecf20Sopenharmony_ci#define LPC_MAC1_RESET_MCS_RX (1 << 11) 948c2ecf20Sopenharmony_ci#define LPC_MAC1_SIMULATION_RESET (1 << 14) 958c2ecf20Sopenharmony_ci#define LPC_MAC1_SOFT_RESET (1 << 15) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * mac2 register definitions 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci#define LPC_MAC2_FULL_DUPLEX (1 << 0) 1018c2ecf20Sopenharmony_ci#define LPC_MAC2_FRAME_LENGTH_CHECKING (1 << 1) 1028c2ecf20Sopenharmony_ci#define LPC_MAC2_HUGH_LENGTH_CHECKING (1 << 2) 1038c2ecf20Sopenharmony_ci#define LPC_MAC2_DELAYED_CRC (1 << 3) 1048c2ecf20Sopenharmony_ci#define LPC_MAC2_CRC_ENABLE (1 << 4) 1058c2ecf20Sopenharmony_ci#define LPC_MAC2_PAD_CRC_ENABLE (1 << 5) 1068c2ecf20Sopenharmony_ci#define LPC_MAC2_VLAN_PAD_ENABLE (1 << 6) 1078c2ecf20Sopenharmony_ci#define LPC_MAC2_AUTO_DETECT_PAD_ENABLE (1 << 7) 1088c2ecf20Sopenharmony_ci#define LPC_MAC2_PURE_PREAMBLE_ENFORCEMENT (1 << 8) 1098c2ecf20Sopenharmony_ci#define LPC_MAC2_LONG_PREAMBLE_ENFORCEMENT (1 << 9) 1108c2ecf20Sopenharmony_ci#define LPC_MAC2_NO_BACKOFF (1 << 12) 1118c2ecf20Sopenharmony_ci#define LPC_MAC2_BACK_PRESSURE (1 << 13) 1128c2ecf20Sopenharmony_ci#define LPC_MAC2_EXCESS_DEFER (1 << 14) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * ipgt register definitions 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci#define LPC_IPGT_LOAD(n) ((n) & 0x7F) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/* 1208c2ecf20Sopenharmony_ci * ipgr register definitions 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci#define LPC_IPGR_LOAD_PART2(n) ((n) & 0x7F) 1238c2ecf20Sopenharmony_ci#define LPC_IPGR_LOAD_PART1(n) (((n) & 0x7F) << 8) 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * clrt register definitions 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci#define LPC_CLRT_LOAD_RETRY_MAX(n) ((n) & 0xF) 1298c2ecf20Sopenharmony_ci#define LPC_CLRT_LOAD_COLLISION_WINDOW(n) (((n) & 0x3F) << 8) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * maxf register definitions 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci#define LPC_MAXF_LOAD_MAX_FRAME_LEN(n) ((n) & 0xFFFF) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* 1378c2ecf20Sopenharmony_ci * supp register definitions 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci#define LPC_SUPP_SPEED (1 << 8) 1408c2ecf20Sopenharmony_ci#define LPC_SUPP_RESET_RMII (1 << 11) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* 1438c2ecf20Sopenharmony_ci * test register definitions 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci#define LPC_TEST_SHORTCUT_PAUSE_QUANTA (1 << 0) 1468c2ecf20Sopenharmony_ci#define LPC_TEST_PAUSE (1 << 1) 1478c2ecf20Sopenharmony_ci#define LPC_TEST_BACKPRESSURE (1 << 2) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * mcfg register definitions 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci#define LPC_MCFG_SCAN_INCREMENT (1 << 0) 1538c2ecf20Sopenharmony_ci#define LPC_MCFG_SUPPRESS_PREAMBLE (1 << 1) 1548c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_SELECT(n) (((n) & 0x7) << 2) 1558c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_4 0 1568c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_6 2 1578c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_8 3 1588c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_10 4 1598c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_14 5 1608c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_20 6 1618c2ecf20Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_28 7 1628c2ecf20Sopenharmony_ci#define LPC_MCFG_RESET_MII_MGMT (1 << 15) 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/* 1658c2ecf20Sopenharmony_ci * mcmd register definitions 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci#define LPC_MCMD_READ (1 << 0) 1688c2ecf20Sopenharmony_ci#define LPC_MCMD_SCAN (1 << 1) 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * madr register definitions 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci#define LPC_MADR_REGISTER_ADDRESS(n) ((n) & 0x1F) 1748c2ecf20Sopenharmony_ci#define LPC_MADR_PHY_0ADDRESS(n) (((n) & 0x1F) << 8) 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/* 1778c2ecf20Sopenharmony_ci * mwtd register definitions 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci#define LPC_MWDT_WRITE(n) ((n) & 0xFFFF) 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* 1828c2ecf20Sopenharmony_ci * mrdd register definitions 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci#define LPC_MRDD_READ_MASK 0xFFFF 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * mind register definitions 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci#define LPC_MIND_BUSY (1 << 0) 1908c2ecf20Sopenharmony_ci#define LPC_MIND_SCANNING (1 << 1) 1918c2ecf20Sopenharmony_ci#define LPC_MIND_NOT_VALID (1 << 2) 1928c2ecf20Sopenharmony_ci#define LPC_MIND_MII_LINK_FAIL (1 << 3) 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* 1958c2ecf20Sopenharmony_ci * command register definitions 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_ci#define LPC_COMMAND_RXENABLE (1 << 0) 1988c2ecf20Sopenharmony_ci#define LPC_COMMAND_TXENABLE (1 << 1) 1998c2ecf20Sopenharmony_ci#define LPC_COMMAND_REG_RESET (1 << 3) 2008c2ecf20Sopenharmony_ci#define LPC_COMMAND_TXRESET (1 << 4) 2018c2ecf20Sopenharmony_ci#define LPC_COMMAND_RXRESET (1 << 5) 2028c2ecf20Sopenharmony_ci#define LPC_COMMAND_PASSRUNTFRAME (1 << 6) 2038c2ecf20Sopenharmony_ci#define LPC_COMMAND_PASSRXFILTER (1 << 7) 2048c2ecf20Sopenharmony_ci#define LPC_COMMAND_TXFLOWCONTROL (1 << 8) 2058c2ecf20Sopenharmony_ci#define LPC_COMMAND_RMII (1 << 9) 2068c2ecf20Sopenharmony_ci#define LPC_COMMAND_FULLDUPLEX (1 << 10) 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* 2098c2ecf20Sopenharmony_ci * status register definitions 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci#define LPC_STATUS_RXACTIVE (1 << 0) 2128c2ecf20Sopenharmony_ci#define LPC_STATUS_TXACTIVE (1 << 1) 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * tsv0 register definitions 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci#define LPC_TSV0_CRC_ERROR (1 << 0) 2188c2ecf20Sopenharmony_ci#define LPC_TSV0_LENGTH_CHECK_ERROR (1 << 1) 2198c2ecf20Sopenharmony_ci#define LPC_TSV0_LENGTH_OUT_OF_RANGE (1 << 2) 2208c2ecf20Sopenharmony_ci#define LPC_TSV0_DONE (1 << 3) 2218c2ecf20Sopenharmony_ci#define LPC_TSV0_MULTICAST (1 << 4) 2228c2ecf20Sopenharmony_ci#define LPC_TSV0_BROADCAST (1 << 5) 2238c2ecf20Sopenharmony_ci#define LPC_TSV0_PACKET_DEFER (1 << 6) 2248c2ecf20Sopenharmony_ci#define LPC_TSV0_ESCESSIVE_DEFER (1 << 7) 2258c2ecf20Sopenharmony_ci#define LPC_TSV0_ESCESSIVE_COLLISION (1 << 8) 2268c2ecf20Sopenharmony_ci#define LPC_TSV0_LATE_COLLISION (1 << 9) 2278c2ecf20Sopenharmony_ci#define LPC_TSV0_GIANT (1 << 10) 2288c2ecf20Sopenharmony_ci#define LPC_TSV0_UNDERRUN (1 << 11) 2298c2ecf20Sopenharmony_ci#define LPC_TSV0_TOTAL_BYTES(n) (((n) >> 12) & 0xFFFF) 2308c2ecf20Sopenharmony_ci#define LPC_TSV0_CONTROL_FRAME (1 << 28) 2318c2ecf20Sopenharmony_ci#define LPC_TSV0_PAUSE (1 << 29) 2328c2ecf20Sopenharmony_ci#define LPC_TSV0_BACKPRESSURE (1 << 30) 2338c2ecf20Sopenharmony_ci#define LPC_TSV0_VLAN (1 << 31) 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* 2368c2ecf20Sopenharmony_ci * tsv1 register definitions 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci#define LPC_TSV1_TRANSMIT_BYTE_COUNT(n) ((n) & 0xFFFF) 2398c2ecf20Sopenharmony_ci#define LPC_TSV1_COLLISION_COUNT(n) (((n) >> 16) & 0xF) 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * rsv register definitions 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci#define LPC_RSV_RECEIVED_BYTE_COUNT(n) ((n) & 0xFFFF) 2458c2ecf20Sopenharmony_ci#define LPC_RSV_RXDV_EVENT_IGNORED (1 << 16) 2468c2ecf20Sopenharmony_ci#define LPC_RSV_RXDV_EVENT_PREVIOUSLY_SEEN (1 << 17) 2478c2ecf20Sopenharmony_ci#define LPC_RSV_CARRIER_EVNT_PREVIOUS_SEEN (1 << 18) 2488c2ecf20Sopenharmony_ci#define LPC_RSV_RECEIVE_CODE_VIOLATION (1 << 19) 2498c2ecf20Sopenharmony_ci#define LPC_RSV_CRC_ERROR (1 << 20) 2508c2ecf20Sopenharmony_ci#define LPC_RSV_LENGTH_CHECK_ERROR (1 << 21) 2518c2ecf20Sopenharmony_ci#define LPC_RSV_LENGTH_OUT_OF_RANGE (1 << 22) 2528c2ecf20Sopenharmony_ci#define LPC_RSV_RECEIVE_OK (1 << 23) 2538c2ecf20Sopenharmony_ci#define LPC_RSV_MULTICAST (1 << 24) 2548c2ecf20Sopenharmony_ci#define LPC_RSV_BROADCAST (1 << 25) 2558c2ecf20Sopenharmony_ci#define LPC_RSV_DRIBBLE_NIBBLE (1 << 26) 2568c2ecf20Sopenharmony_ci#define LPC_RSV_CONTROL_FRAME (1 << 27) 2578c2ecf20Sopenharmony_ci#define LPC_RSV_PAUSE (1 << 28) 2588c2ecf20Sopenharmony_ci#define LPC_RSV_UNSUPPORTED_OPCODE (1 << 29) 2598c2ecf20Sopenharmony_ci#define LPC_RSV_VLAN (1 << 30) 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci/* 2628c2ecf20Sopenharmony_ci * flowcontrolcounter register definitions 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci#define LPC_FCCR_MIRRORCOUNTER(n) ((n) & 0xFFFF) 2658c2ecf20Sopenharmony_ci#define LPC_FCCR_PAUSETIMER(n) (((n) >> 16) & 0xFFFF) 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/* 2688c2ecf20Sopenharmony_ci * flowcontrolstatus register definitions 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci#define LPC_FCCR_MIRRORCOUNTERCURRENT(n) ((n) & 0xFFFF) 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * rxfilterctrl, rxfilterwolstatus, and rxfilterwolclear shared 2748c2ecf20Sopenharmony_ci * register definitions 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUNICAST (1 << 0) 2778c2ecf20Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUBROADCAST (1 << 1) 2788c2ecf20Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUMULTICAST (1 << 2) 2798c2ecf20Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUNICASTHASH (1 << 3) 2808c2ecf20Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUMULTICASTHASH (1 << 4) 2818c2ecf20Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTPERFECT (1 << 5) 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * rxfilterctrl register definitions 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci#define LPC_RXFLTRWSTS_MAGICPACKETENWOL (1 << 12) 2878c2ecf20Sopenharmony_ci#define LPC_RXFLTRWSTS_RXFILTERENWOL (1 << 13) 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * rxfilterwolstatus/rxfilterwolclear register definitions 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci#define LPC_RXFLTRWSTS_RXFILTERWOL (1 << 7) 2938c2ecf20Sopenharmony_ci#define LPC_RXFLTRWSTS_MAGICPACKETWOL (1 << 8) 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* 2968c2ecf20Sopenharmony_ci * intstatus, intenable, intclear, and Intset shared register 2978c2ecf20Sopenharmony_ci * definitions 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci#define LPC_MACINT_RXOVERRUNINTEN (1 << 0) 3008c2ecf20Sopenharmony_ci#define LPC_MACINT_RXERRORONINT (1 << 1) 3018c2ecf20Sopenharmony_ci#define LPC_MACINT_RXFINISHEDINTEN (1 << 2) 3028c2ecf20Sopenharmony_ci#define LPC_MACINT_RXDONEINTEN (1 << 3) 3038c2ecf20Sopenharmony_ci#define LPC_MACINT_TXUNDERRUNINTEN (1 << 4) 3048c2ecf20Sopenharmony_ci#define LPC_MACINT_TXERRORINTEN (1 << 5) 3058c2ecf20Sopenharmony_ci#define LPC_MACINT_TXFINISHEDINTEN (1 << 6) 3068c2ecf20Sopenharmony_ci#define LPC_MACINT_TXDONEINTEN (1 << 7) 3078c2ecf20Sopenharmony_ci#define LPC_MACINT_SOFTINTEN (1 << 12) 3088c2ecf20Sopenharmony_ci#define LPC_MACINT_WAKEUPINTEN (1 << 13) 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* 3118c2ecf20Sopenharmony_ci * powerdown register definitions 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci#define LPC_POWERDOWN_MACAHB (1 << 31) 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic phy_interface_t lpc_phy_interface_mode(struct device *dev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci if (dev && dev->of_node) { 3188c2ecf20Sopenharmony_ci const char *mode = of_get_property(dev->of_node, 3198c2ecf20Sopenharmony_ci "phy-mode", NULL); 3208c2ecf20Sopenharmony_ci if (mode && !strcmp(mode, "mii")) 3218c2ecf20Sopenharmony_ci return PHY_INTERFACE_MODE_MII; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci return PHY_INTERFACE_MODE_RMII; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic bool use_iram_for_net(struct device *dev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci if (dev && dev->of_node) 3298c2ecf20Sopenharmony_ci return of_property_read_bool(dev->of_node, "use-iram"); 3308c2ecf20Sopenharmony_ci return false; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* Receive Status information word */ 3348c2ecf20Sopenharmony_ci#define RXSTATUS_SIZE 0x000007FF 3358c2ecf20Sopenharmony_ci#define RXSTATUS_CONTROL (1 << 18) 3368c2ecf20Sopenharmony_ci#define RXSTATUS_VLAN (1 << 19) 3378c2ecf20Sopenharmony_ci#define RXSTATUS_FILTER (1 << 20) 3388c2ecf20Sopenharmony_ci#define RXSTATUS_MULTICAST (1 << 21) 3398c2ecf20Sopenharmony_ci#define RXSTATUS_BROADCAST (1 << 22) 3408c2ecf20Sopenharmony_ci#define RXSTATUS_CRC (1 << 23) 3418c2ecf20Sopenharmony_ci#define RXSTATUS_SYMBOL (1 << 24) 3428c2ecf20Sopenharmony_ci#define RXSTATUS_LENGTH (1 << 25) 3438c2ecf20Sopenharmony_ci#define RXSTATUS_RANGE (1 << 26) 3448c2ecf20Sopenharmony_ci#define RXSTATUS_ALIGN (1 << 27) 3458c2ecf20Sopenharmony_ci#define RXSTATUS_OVERRUN (1 << 28) 3468c2ecf20Sopenharmony_ci#define RXSTATUS_NODESC (1 << 29) 3478c2ecf20Sopenharmony_ci#define RXSTATUS_LAST (1 << 30) 3488c2ecf20Sopenharmony_ci#define RXSTATUS_ERROR (1 << 31) 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci#define RXSTATUS_STATUS_ERROR \ 3518c2ecf20Sopenharmony_ci (RXSTATUS_NODESC | RXSTATUS_OVERRUN | RXSTATUS_ALIGN | \ 3528c2ecf20Sopenharmony_ci RXSTATUS_RANGE | RXSTATUS_LENGTH | RXSTATUS_SYMBOL | RXSTATUS_CRC) 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* Receive Descriptor control word */ 3558c2ecf20Sopenharmony_ci#define RXDESC_CONTROL_SIZE 0x000007FF 3568c2ecf20Sopenharmony_ci#define RXDESC_CONTROL_INT (1 << 31) 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci/* Transmit Status information word */ 3598c2ecf20Sopenharmony_ci#define TXSTATUS_COLLISIONS_GET(x) (((x) >> 21) & 0xF) 3608c2ecf20Sopenharmony_ci#define TXSTATUS_DEFER (1 << 25) 3618c2ecf20Sopenharmony_ci#define TXSTATUS_EXCESSDEFER (1 << 26) 3628c2ecf20Sopenharmony_ci#define TXSTATUS_EXCESSCOLL (1 << 27) 3638c2ecf20Sopenharmony_ci#define TXSTATUS_LATECOLL (1 << 28) 3648c2ecf20Sopenharmony_ci#define TXSTATUS_UNDERRUN (1 << 29) 3658c2ecf20Sopenharmony_ci#define TXSTATUS_NODESC (1 << 30) 3668c2ecf20Sopenharmony_ci#define TXSTATUS_ERROR (1 << 31) 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* Transmit Descriptor control word */ 3698c2ecf20Sopenharmony_ci#define TXDESC_CONTROL_SIZE 0x000007FF 3708c2ecf20Sopenharmony_ci#define TXDESC_CONTROL_OVERRIDE (1 << 26) 3718c2ecf20Sopenharmony_ci#define TXDESC_CONTROL_HUGE (1 << 27) 3728c2ecf20Sopenharmony_ci#define TXDESC_CONTROL_PAD (1 << 28) 3738c2ecf20Sopenharmony_ci#define TXDESC_CONTROL_CRC (1 << 29) 3748c2ecf20Sopenharmony_ci#define TXDESC_CONTROL_LAST (1 << 30) 3758c2ecf20Sopenharmony_ci#define TXDESC_CONTROL_INT (1 << 31) 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* 3788c2ecf20Sopenharmony_ci * Structure of a TX/RX descriptors and RX status 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistruct txrx_desc_t { 3818c2ecf20Sopenharmony_ci __le32 packet; 3828c2ecf20Sopenharmony_ci __le32 control; 3838c2ecf20Sopenharmony_ci}; 3848c2ecf20Sopenharmony_cistruct rx_status_t { 3858c2ecf20Sopenharmony_ci __le32 statusinfo; 3868c2ecf20Sopenharmony_ci __le32 statushashcrc; 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* 3908c2ecf20Sopenharmony_ci * Device driver data structure 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_cistruct netdata_local { 3938c2ecf20Sopenharmony_ci struct platform_device *pdev; 3948c2ecf20Sopenharmony_ci struct net_device *ndev; 3958c2ecf20Sopenharmony_ci struct device_node *phy_node; 3968c2ecf20Sopenharmony_ci spinlock_t lock; 3978c2ecf20Sopenharmony_ci void __iomem *net_base; 3988c2ecf20Sopenharmony_ci u32 msg_enable; 3998c2ecf20Sopenharmony_ci unsigned int skblen[ENET_TX_DESC]; 4008c2ecf20Sopenharmony_ci unsigned int last_tx_idx; 4018c2ecf20Sopenharmony_ci unsigned int num_used_tx_buffs; 4028c2ecf20Sopenharmony_ci struct mii_bus *mii_bus; 4038c2ecf20Sopenharmony_ci struct clk *clk; 4048c2ecf20Sopenharmony_ci dma_addr_t dma_buff_base_p; 4058c2ecf20Sopenharmony_ci void *dma_buff_base_v; 4068c2ecf20Sopenharmony_ci size_t dma_buff_size; 4078c2ecf20Sopenharmony_ci struct txrx_desc_t *tx_desc_v; 4088c2ecf20Sopenharmony_ci u32 *tx_stat_v; 4098c2ecf20Sopenharmony_ci void *tx_buff_v; 4108c2ecf20Sopenharmony_ci struct txrx_desc_t *rx_desc_v; 4118c2ecf20Sopenharmony_ci struct rx_status_t *rx_stat_v; 4128c2ecf20Sopenharmony_ci void *rx_buff_v; 4138c2ecf20Sopenharmony_ci int link; 4148c2ecf20Sopenharmony_ci int speed; 4158c2ecf20Sopenharmony_ci int duplex; 4168c2ecf20Sopenharmony_ci struct napi_struct napi; 4178c2ecf20Sopenharmony_ci}; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/* 4208c2ecf20Sopenharmony_ci * MAC support functions 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_cistatic void __lpc_set_mac(struct netdata_local *pldat, u8 *mac) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci u32 tmp; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* Set station address */ 4278c2ecf20Sopenharmony_ci tmp = mac[0] | ((u32)mac[1] << 8); 4288c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_SA2(pldat->net_base)); 4298c2ecf20Sopenharmony_ci tmp = mac[2] | ((u32)mac[3] << 8); 4308c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_SA1(pldat->net_base)); 4318c2ecf20Sopenharmony_ci tmp = mac[4] | ((u32)mac[5] << 8); 4328c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_SA0(pldat->net_base)); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci netdev_dbg(pldat->ndev, "Ethernet MAC address %pM\n", mac); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic void __lpc_get_mac(struct netdata_local *pldat, u8 *mac) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci u32 tmp; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Get station address */ 4428c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_SA2(pldat->net_base)); 4438c2ecf20Sopenharmony_ci mac[0] = tmp & 0xFF; 4448c2ecf20Sopenharmony_ci mac[1] = tmp >> 8; 4458c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_SA1(pldat->net_base)); 4468c2ecf20Sopenharmony_ci mac[2] = tmp & 0xFF; 4478c2ecf20Sopenharmony_ci mac[3] = tmp >> 8; 4488c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_SA0(pldat->net_base)); 4498c2ecf20Sopenharmony_ci mac[4] = tmp & 0xFF; 4508c2ecf20Sopenharmony_ci mac[5] = tmp >> 8; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void __lpc_params_setup(struct netdata_local *pldat) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci u32 tmp; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (pldat->duplex == DUPLEX_FULL) { 4588c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_MAC2(pldat->net_base)); 4598c2ecf20Sopenharmony_ci tmp |= LPC_MAC2_FULL_DUPLEX; 4608c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_MAC2(pldat->net_base)); 4618c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 4628c2ecf20Sopenharmony_ci tmp |= LPC_COMMAND_FULLDUPLEX; 4638c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 4648c2ecf20Sopenharmony_ci writel(LPC_IPGT_LOAD(0x15), LPC_ENET_IPGT(pldat->net_base)); 4658c2ecf20Sopenharmony_ci } else { 4668c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_MAC2(pldat->net_base)); 4678c2ecf20Sopenharmony_ci tmp &= ~LPC_MAC2_FULL_DUPLEX; 4688c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_MAC2(pldat->net_base)); 4698c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 4708c2ecf20Sopenharmony_ci tmp &= ~LPC_COMMAND_FULLDUPLEX; 4718c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 4728c2ecf20Sopenharmony_ci writel(LPC_IPGT_LOAD(0x12), LPC_ENET_IPGT(pldat->net_base)); 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (pldat->speed == SPEED_100) 4768c2ecf20Sopenharmony_ci writel(LPC_SUPP_SPEED, LPC_ENET_SUPP(pldat->net_base)); 4778c2ecf20Sopenharmony_ci else 4788c2ecf20Sopenharmony_ci writel(0, LPC_ENET_SUPP(pldat->net_base)); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_cistatic void __lpc_eth_reset(struct netdata_local *pldat) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci /* Reset all MAC logic */ 4848c2ecf20Sopenharmony_ci writel((LPC_MAC1_RESET_TX | LPC_MAC1_RESET_MCS_TX | LPC_MAC1_RESET_RX | 4858c2ecf20Sopenharmony_ci LPC_MAC1_RESET_MCS_RX | LPC_MAC1_SIMULATION_RESET | 4868c2ecf20Sopenharmony_ci LPC_MAC1_SOFT_RESET), LPC_ENET_MAC1(pldat->net_base)); 4878c2ecf20Sopenharmony_ci writel((LPC_COMMAND_REG_RESET | LPC_COMMAND_TXRESET | 4888c2ecf20Sopenharmony_ci LPC_COMMAND_RXRESET), LPC_ENET_COMMAND(pldat->net_base)); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int __lpc_mii_mngt_reset(struct netdata_local *pldat) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci /* Reset MII management hardware */ 4948c2ecf20Sopenharmony_ci writel(LPC_MCFG_RESET_MII_MGMT, LPC_ENET_MCFG(pldat->net_base)); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Setup MII clock to slowest rate with a /28 divider */ 4978c2ecf20Sopenharmony_ci writel(LPC_MCFG_CLOCK_SELECT(LPC_MCFG_CLOCK_HOST_DIV_28), 4988c2ecf20Sopenharmony_ci LPC_ENET_MCFG(pldat->net_base)); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic inline phys_addr_t __va_to_pa(void *addr, struct netdata_local *pldat) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci phys_addr_t phaddr; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci phaddr = addr - pldat->dma_buff_base_v; 5088c2ecf20Sopenharmony_ci phaddr += pldat->dma_buff_base_p; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return phaddr; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void lpc_eth_enable_int(void __iomem *regbase) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci writel((LPC_MACINT_RXDONEINTEN | LPC_MACINT_TXDONEINTEN), 5168c2ecf20Sopenharmony_ci LPC_ENET_INTENABLE(regbase)); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void lpc_eth_disable_int(void __iomem *regbase) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci writel(0, LPC_ENET_INTENABLE(regbase)); 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/* Setup TX/RX descriptors */ 5258c2ecf20Sopenharmony_cistatic void __lpc_txrx_desc_setup(struct netdata_local *pldat) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci u32 *ptxstat; 5288c2ecf20Sopenharmony_ci void *tbuff; 5298c2ecf20Sopenharmony_ci int i; 5308c2ecf20Sopenharmony_ci struct txrx_desc_t *ptxrxdesc; 5318c2ecf20Sopenharmony_ci struct rx_status_t *prxstat; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci tbuff = PTR_ALIGN(pldat->dma_buff_base_v, 16); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Setup TX descriptors, status, and buffers */ 5368c2ecf20Sopenharmony_ci pldat->tx_desc_v = tbuff; 5378c2ecf20Sopenharmony_ci tbuff += sizeof(struct txrx_desc_t) * ENET_TX_DESC; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci pldat->tx_stat_v = tbuff; 5408c2ecf20Sopenharmony_ci tbuff += sizeof(u32) * ENET_TX_DESC; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci tbuff = PTR_ALIGN(tbuff, 16); 5438c2ecf20Sopenharmony_ci pldat->tx_buff_v = tbuff; 5448c2ecf20Sopenharmony_ci tbuff += ENET_MAXF_SIZE * ENET_TX_DESC; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci /* Setup RX descriptors, status, and buffers */ 5478c2ecf20Sopenharmony_ci pldat->rx_desc_v = tbuff; 5488c2ecf20Sopenharmony_ci tbuff += sizeof(struct txrx_desc_t) * ENET_RX_DESC; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci tbuff = PTR_ALIGN(tbuff, 16); 5518c2ecf20Sopenharmony_ci pldat->rx_stat_v = tbuff; 5528c2ecf20Sopenharmony_ci tbuff += sizeof(struct rx_status_t) * ENET_RX_DESC; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci tbuff = PTR_ALIGN(tbuff, 16); 5558c2ecf20Sopenharmony_ci pldat->rx_buff_v = tbuff; 5568c2ecf20Sopenharmony_ci tbuff += ENET_MAXF_SIZE * ENET_RX_DESC; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* Map the TX descriptors to the TX buffers in hardware */ 5598c2ecf20Sopenharmony_ci for (i = 0; i < ENET_TX_DESC; i++) { 5608c2ecf20Sopenharmony_ci ptxstat = &pldat->tx_stat_v[i]; 5618c2ecf20Sopenharmony_ci ptxrxdesc = &pldat->tx_desc_v[i]; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ptxrxdesc->packet = __va_to_pa( 5648c2ecf20Sopenharmony_ci pldat->tx_buff_v + i * ENET_MAXF_SIZE, pldat); 5658c2ecf20Sopenharmony_ci ptxrxdesc->control = 0; 5668c2ecf20Sopenharmony_ci *ptxstat = 0; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Map the RX descriptors to the RX buffers in hardware */ 5708c2ecf20Sopenharmony_ci for (i = 0; i < ENET_RX_DESC; i++) { 5718c2ecf20Sopenharmony_ci prxstat = &pldat->rx_stat_v[i]; 5728c2ecf20Sopenharmony_ci ptxrxdesc = &pldat->rx_desc_v[i]; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ptxrxdesc->packet = __va_to_pa( 5758c2ecf20Sopenharmony_ci pldat->rx_buff_v + i * ENET_MAXF_SIZE, pldat); 5768c2ecf20Sopenharmony_ci ptxrxdesc->control = RXDESC_CONTROL_INT | (ENET_MAXF_SIZE - 1); 5778c2ecf20Sopenharmony_ci prxstat->statusinfo = 0; 5788c2ecf20Sopenharmony_ci prxstat->statushashcrc = 0; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Setup base addresses in hardware to point to buffers and 5828c2ecf20Sopenharmony_ci * descriptors 5838c2ecf20Sopenharmony_ci */ 5848c2ecf20Sopenharmony_ci writel((ENET_TX_DESC - 1), 5858c2ecf20Sopenharmony_ci LPC_ENET_TXDESCRIPTORNUMBER(pldat->net_base)); 5868c2ecf20Sopenharmony_ci writel(__va_to_pa(pldat->tx_desc_v, pldat), 5878c2ecf20Sopenharmony_ci LPC_ENET_TXDESCRIPTOR(pldat->net_base)); 5888c2ecf20Sopenharmony_ci writel(__va_to_pa(pldat->tx_stat_v, pldat), 5898c2ecf20Sopenharmony_ci LPC_ENET_TXSTATUS(pldat->net_base)); 5908c2ecf20Sopenharmony_ci writel((ENET_RX_DESC - 1), 5918c2ecf20Sopenharmony_ci LPC_ENET_RXDESCRIPTORNUMBER(pldat->net_base)); 5928c2ecf20Sopenharmony_ci writel(__va_to_pa(pldat->rx_desc_v, pldat), 5938c2ecf20Sopenharmony_ci LPC_ENET_RXDESCRIPTOR(pldat->net_base)); 5948c2ecf20Sopenharmony_ci writel(__va_to_pa(pldat->rx_stat_v, pldat), 5958c2ecf20Sopenharmony_ci LPC_ENET_RXSTATUS(pldat->net_base)); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic void __lpc_eth_init(struct netdata_local *pldat) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci u32 tmp; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Disable controller and reset */ 6038c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 6048c2ecf20Sopenharmony_ci tmp &= ~LPC_COMMAND_RXENABLE | LPC_COMMAND_TXENABLE; 6058c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 6068c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_MAC1(pldat->net_base)); 6078c2ecf20Sopenharmony_ci tmp &= ~LPC_MAC1_RECV_ENABLE; 6088c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_MAC1(pldat->net_base)); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci /* Initial MAC setup */ 6118c2ecf20Sopenharmony_ci writel(LPC_MAC1_PASS_ALL_RX_FRAMES, LPC_ENET_MAC1(pldat->net_base)); 6128c2ecf20Sopenharmony_ci writel((LPC_MAC2_PAD_CRC_ENABLE | LPC_MAC2_CRC_ENABLE), 6138c2ecf20Sopenharmony_ci LPC_ENET_MAC2(pldat->net_base)); 6148c2ecf20Sopenharmony_ci writel(ENET_MAXF_SIZE, LPC_ENET_MAXF(pldat->net_base)); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Collision window, gap */ 6178c2ecf20Sopenharmony_ci writel((LPC_CLRT_LOAD_RETRY_MAX(0xF) | 6188c2ecf20Sopenharmony_ci LPC_CLRT_LOAD_COLLISION_WINDOW(0x37)), 6198c2ecf20Sopenharmony_ci LPC_ENET_CLRT(pldat->net_base)); 6208c2ecf20Sopenharmony_ci writel(LPC_IPGR_LOAD_PART2(0x12), LPC_ENET_IPGR(pldat->net_base)); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII) 6238c2ecf20Sopenharmony_ci writel(LPC_COMMAND_PASSRUNTFRAME, 6248c2ecf20Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 6258c2ecf20Sopenharmony_ci else { 6268c2ecf20Sopenharmony_ci writel((LPC_COMMAND_PASSRUNTFRAME | LPC_COMMAND_RMII), 6278c2ecf20Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 6288c2ecf20Sopenharmony_ci writel(LPC_SUPP_RESET_RMII, LPC_ENET_SUPP(pldat->net_base)); 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci __lpc_params_setup(pldat); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* Setup TX and RX descriptors */ 6348c2ecf20Sopenharmony_ci __lpc_txrx_desc_setup(pldat); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Setup packet filtering */ 6378c2ecf20Sopenharmony_ci writel((LPC_RXFLTRW_ACCEPTUBROADCAST | LPC_RXFLTRW_ACCEPTPERFECT), 6388c2ecf20Sopenharmony_ci LPC_ENET_RXFILTER_CTRL(pldat->net_base)); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* Get the next TX buffer output index */ 6418c2ecf20Sopenharmony_ci pldat->num_used_tx_buffs = 0; 6428c2ecf20Sopenharmony_ci pldat->last_tx_idx = 6438c2ecf20Sopenharmony_ci readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* Clear and enable interrupts */ 6468c2ecf20Sopenharmony_ci writel(0xFFFF, LPC_ENET_INTCLEAR(pldat->net_base)); 6478c2ecf20Sopenharmony_ci smp_wmb(); 6488c2ecf20Sopenharmony_ci lpc_eth_enable_int(pldat->net_base); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Enable controller */ 6518c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 6528c2ecf20Sopenharmony_ci tmp |= LPC_COMMAND_RXENABLE | LPC_COMMAND_TXENABLE; 6538c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 6548c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_MAC1(pldat->net_base)); 6558c2ecf20Sopenharmony_ci tmp |= LPC_MAC1_RECV_ENABLE; 6568c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_MAC1(pldat->net_base)); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic void __lpc_eth_shutdown(struct netdata_local *pldat) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci /* Reset ethernet and power down PHY */ 6628c2ecf20Sopenharmony_ci __lpc_eth_reset(pldat); 6638c2ecf20Sopenharmony_ci writel(0, LPC_ENET_MAC1(pldat->net_base)); 6648c2ecf20Sopenharmony_ci writel(0, LPC_ENET_MAC2(pldat->net_base)); 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/* 6688c2ecf20Sopenharmony_ci * MAC<--->PHY support functions 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_cistatic int lpc_mdio_read(struct mii_bus *bus, int phy_id, int phyreg) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct netdata_local *pldat = bus->priv; 6738c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(100); 6748c2ecf20Sopenharmony_ci int lps; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci writel(((phy_id << 8) | phyreg), LPC_ENET_MADR(pldat->net_base)); 6778c2ecf20Sopenharmony_ci writel(LPC_MCMD_READ, LPC_ENET_MCMD(pldat->net_base)); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* Wait for unbusy status */ 6808c2ecf20Sopenharmony_ci while (readl(LPC_ENET_MIND(pldat->net_base)) & LPC_MIND_BUSY) { 6818c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 6828c2ecf20Sopenharmony_ci return -EIO; 6838c2ecf20Sopenharmony_ci cpu_relax(); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci lps = readl(LPC_ENET_MRDD(pldat->net_base)); 6878c2ecf20Sopenharmony_ci writel(0, LPC_ENET_MCMD(pldat->net_base)); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return lps; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int lpc_mdio_write(struct mii_bus *bus, int phy_id, int phyreg, 6938c2ecf20Sopenharmony_ci u16 phydata) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct netdata_local *pldat = bus->priv; 6968c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(100); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci writel(((phy_id << 8) | phyreg), LPC_ENET_MADR(pldat->net_base)); 6998c2ecf20Sopenharmony_ci writel(phydata, LPC_ENET_MWTD(pldat->net_base)); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* Wait for completion */ 7028c2ecf20Sopenharmony_ci while (readl(LPC_ENET_MIND(pldat->net_base)) & LPC_MIND_BUSY) { 7038c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 7048c2ecf20Sopenharmony_ci return -EIO; 7058c2ecf20Sopenharmony_ci cpu_relax(); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int lpc_mdio_reset(struct mii_bus *bus) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci return __lpc_mii_mngt_reset((struct netdata_local *)bus->priv); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic void lpc_handle_link_change(struct net_device *ndev) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 7198c2ecf20Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 7208c2ecf20Sopenharmony_ci unsigned long flags; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci bool status_change = false; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (phydev->link) { 7278c2ecf20Sopenharmony_ci if ((pldat->speed != phydev->speed) || 7288c2ecf20Sopenharmony_ci (pldat->duplex != phydev->duplex)) { 7298c2ecf20Sopenharmony_ci pldat->speed = phydev->speed; 7308c2ecf20Sopenharmony_ci pldat->duplex = phydev->duplex; 7318c2ecf20Sopenharmony_ci status_change = true; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (phydev->link != pldat->link) { 7368c2ecf20Sopenharmony_ci if (!phydev->link) { 7378c2ecf20Sopenharmony_ci pldat->speed = 0; 7388c2ecf20Sopenharmony_ci pldat->duplex = -1; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci pldat->link = phydev->link; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci status_change = true; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (status_change) 7488c2ecf20Sopenharmony_ci __lpc_params_setup(pldat); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic int lpc_mii_probe(struct net_device *ndev) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 7548c2ecf20Sopenharmony_ci struct phy_device *phydev; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Attach to the PHY */ 7578c2ecf20Sopenharmony_ci if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII) 7588c2ecf20Sopenharmony_ci netdev_info(ndev, "using MII interface\n"); 7598c2ecf20Sopenharmony_ci else 7608c2ecf20Sopenharmony_ci netdev_info(ndev, "using RMII interface\n"); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (pldat->phy_node) 7638c2ecf20Sopenharmony_ci phydev = of_phy_find_device(pldat->phy_node); 7648c2ecf20Sopenharmony_ci else 7658c2ecf20Sopenharmony_ci phydev = phy_find_first(pldat->mii_bus); 7668c2ecf20Sopenharmony_ci if (!phydev) { 7678c2ecf20Sopenharmony_ci netdev_err(ndev, "no PHY found\n"); 7688c2ecf20Sopenharmony_ci return -ENODEV; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci phydev = phy_connect(ndev, phydev_name(phydev), 7728c2ecf20Sopenharmony_ci &lpc_handle_link_change, 7738c2ecf20Sopenharmony_ci lpc_phy_interface_mode(&pldat->pdev->dev)); 7748c2ecf20Sopenharmony_ci if (IS_ERR(phydev)) { 7758c2ecf20Sopenharmony_ci netdev_err(ndev, "Could not attach to PHY\n"); 7768c2ecf20Sopenharmony_ci return PTR_ERR(phydev); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci phy_set_max_speed(phydev, SPEED_100); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci pldat->link = 0; 7828c2ecf20Sopenharmony_ci pldat->speed = 0; 7838c2ecf20Sopenharmony_ci pldat->duplex = -1; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci phy_attached_info(phydev); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci return 0; 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic int lpc_mii_init(struct netdata_local *pldat) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct device_node *node; 7938c2ecf20Sopenharmony_ci int err = -ENXIO; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci pldat->mii_bus = mdiobus_alloc(); 7968c2ecf20Sopenharmony_ci if (!pldat->mii_bus) { 7978c2ecf20Sopenharmony_ci err = -ENOMEM; 7988c2ecf20Sopenharmony_ci goto err_out; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci /* Setup MII mode */ 8028c2ecf20Sopenharmony_ci if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII) 8038c2ecf20Sopenharmony_ci writel(LPC_COMMAND_PASSRUNTFRAME, 8048c2ecf20Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 8058c2ecf20Sopenharmony_ci else { 8068c2ecf20Sopenharmony_ci writel((LPC_COMMAND_PASSRUNTFRAME | LPC_COMMAND_RMII), 8078c2ecf20Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 8088c2ecf20Sopenharmony_ci writel(LPC_SUPP_RESET_RMII, LPC_ENET_SUPP(pldat->net_base)); 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci pldat->mii_bus->name = "lpc_mii_bus"; 8128c2ecf20Sopenharmony_ci pldat->mii_bus->read = &lpc_mdio_read; 8138c2ecf20Sopenharmony_ci pldat->mii_bus->write = &lpc_mdio_write; 8148c2ecf20Sopenharmony_ci pldat->mii_bus->reset = &lpc_mdio_reset; 8158c2ecf20Sopenharmony_ci snprintf(pldat->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 8168c2ecf20Sopenharmony_ci pldat->pdev->name, pldat->pdev->id); 8178c2ecf20Sopenharmony_ci pldat->mii_bus->priv = pldat; 8188c2ecf20Sopenharmony_ci pldat->mii_bus->parent = &pldat->pdev->dev; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci node = of_get_child_by_name(pldat->pdev->dev.of_node, "mdio"); 8218c2ecf20Sopenharmony_ci err = of_mdiobus_register(pldat->mii_bus, node); 8228c2ecf20Sopenharmony_ci of_node_put(node); 8238c2ecf20Sopenharmony_ci if (err) 8248c2ecf20Sopenharmony_ci goto err_out_unregister_bus; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci err = lpc_mii_probe(pldat->ndev); 8278c2ecf20Sopenharmony_ci if (err) 8288c2ecf20Sopenharmony_ci goto err_out_unregister_bus; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return 0; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cierr_out_unregister_bus: 8338c2ecf20Sopenharmony_ci mdiobus_unregister(pldat->mii_bus); 8348c2ecf20Sopenharmony_ci mdiobus_free(pldat->mii_bus); 8358c2ecf20Sopenharmony_cierr_out: 8368c2ecf20Sopenharmony_ci return err; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic void __lpc_handle_xmit(struct net_device *ndev) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 8428c2ecf20Sopenharmony_ci u32 txcidx, *ptxstat, txstat; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci txcidx = readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); 8458c2ecf20Sopenharmony_ci while (pldat->last_tx_idx != txcidx) { 8468c2ecf20Sopenharmony_ci unsigned int skblen = pldat->skblen[pldat->last_tx_idx]; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* A buffer is available, get buffer status */ 8498c2ecf20Sopenharmony_ci ptxstat = &pldat->tx_stat_v[pldat->last_tx_idx]; 8508c2ecf20Sopenharmony_ci txstat = *ptxstat; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci /* Next buffer and decrement used buffer counter */ 8538c2ecf20Sopenharmony_ci pldat->num_used_tx_buffs--; 8548c2ecf20Sopenharmony_ci pldat->last_tx_idx++; 8558c2ecf20Sopenharmony_ci if (pldat->last_tx_idx >= ENET_TX_DESC) 8568c2ecf20Sopenharmony_ci pldat->last_tx_idx = 0; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* Update collision counter */ 8598c2ecf20Sopenharmony_ci ndev->stats.collisions += TXSTATUS_COLLISIONS_GET(txstat); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci /* Any errors occurred? */ 8628c2ecf20Sopenharmony_ci if (txstat & TXSTATUS_ERROR) { 8638c2ecf20Sopenharmony_ci if (txstat & TXSTATUS_UNDERRUN) { 8648c2ecf20Sopenharmony_ci /* FIFO underrun */ 8658c2ecf20Sopenharmony_ci ndev->stats.tx_fifo_errors++; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci if (txstat & TXSTATUS_LATECOLL) { 8688c2ecf20Sopenharmony_ci /* Late collision */ 8698c2ecf20Sopenharmony_ci ndev->stats.tx_aborted_errors++; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci if (txstat & TXSTATUS_EXCESSCOLL) { 8728c2ecf20Sopenharmony_ci /* Excessive collision */ 8738c2ecf20Sopenharmony_ci ndev->stats.tx_aborted_errors++; 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci if (txstat & TXSTATUS_EXCESSDEFER) { 8768c2ecf20Sopenharmony_ci /* Defer limit */ 8778c2ecf20Sopenharmony_ci ndev->stats.tx_aborted_errors++; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci ndev->stats.tx_errors++; 8808c2ecf20Sopenharmony_ci } else { 8818c2ecf20Sopenharmony_ci /* Update stats */ 8828c2ecf20Sopenharmony_ci ndev->stats.tx_packets++; 8838c2ecf20Sopenharmony_ci ndev->stats.tx_bytes += skblen; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci txcidx = readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (pldat->num_used_tx_buffs <= ENET_TX_DESC/2) { 8908c2ecf20Sopenharmony_ci if (netif_queue_stopped(ndev)) 8918c2ecf20Sopenharmony_ci netif_wake_queue(ndev); 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic int __lpc_handle_recv(struct net_device *ndev, int budget) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 8988c2ecf20Sopenharmony_ci struct sk_buff *skb; 8998c2ecf20Sopenharmony_ci u32 rxconsidx, len, ethst; 9008c2ecf20Sopenharmony_ci struct rx_status_t *prxstat; 9018c2ecf20Sopenharmony_ci int rx_done = 0; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* Get the current RX buffer indexes */ 9048c2ecf20Sopenharmony_ci rxconsidx = readl(LPC_ENET_RXCONSUMEINDEX(pldat->net_base)); 9058c2ecf20Sopenharmony_ci while (rx_done < budget && rxconsidx != 9068c2ecf20Sopenharmony_ci readl(LPC_ENET_RXPRODUCEINDEX(pldat->net_base))) { 9078c2ecf20Sopenharmony_ci /* Get pointer to receive status */ 9088c2ecf20Sopenharmony_ci prxstat = &pldat->rx_stat_v[rxconsidx]; 9098c2ecf20Sopenharmony_ci len = (prxstat->statusinfo & RXSTATUS_SIZE) + 1; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* Status error? */ 9128c2ecf20Sopenharmony_ci ethst = prxstat->statusinfo; 9138c2ecf20Sopenharmony_ci if ((ethst & (RXSTATUS_ERROR | RXSTATUS_STATUS_ERROR)) == 9148c2ecf20Sopenharmony_ci (RXSTATUS_ERROR | RXSTATUS_RANGE)) 9158c2ecf20Sopenharmony_ci ethst &= ~RXSTATUS_ERROR; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (ethst & RXSTATUS_ERROR) { 9188c2ecf20Sopenharmony_ci int si = prxstat->statusinfo; 9198c2ecf20Sopenharmony_ci /* Check statuses */ 9208c2ecf20Sopenharmony_ci if (si & RXSTATUS_OVERRUN) { 9218c2ecf20Sopenharmony_ci /* Overrun error */ 9228c2ecf20Sopenharmony_ci ndev->stats.rx_fifo_errors++; 9238c2ecf20Sopenharmony_ci } else if (si & RXSTATUS_CRC) { 9248c2ecf20Sopenharmony_ci /* CRC error */ 9258c2ecf20Sopenharmony_ci ndev->stats.rx_crc_errors++; 9268c2ecf20Sopenharmony_ci } else if (si & RXSTATUS_LENGTH) { 9278c2ecf20Sopenharmony_ci /* Length error */ 9288c2ecf20Sopenharmony_ci ndev->stats.rx_length_errors++; 9298c2ecf20Sopenharmony_ci } else if (si & RXSTATUS_ERROR) { 9308c2ecf20Sopenharmony_ci /* Other error */ 9318c2ecf20Sopenharmony_ci ndev->stats.rx_length_errors++; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci ndev->stats.rx_errors++; 9348c2ecf20Sopenharmony_ci } else { 9358c2ecf20Sopenharmony_ci /* Packet is good */ 9368c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len); 9378c2ecf20Sopenharmony_ci if (!skb) { 9388c2ecf20Sopenharmony_ci ndev->stats.rx_dropped++; 9398c2ecf20Sopenharmony_ci } else { 9408c2ecf20Sopenharmony_ci /* Copy packet from buffer */ 9418c2ecf20Sopenharmony_ci skb_put_data(skb, 9428c2ecf20Sopenharmony_ci pldat->rx_buff_v + rxconsidx * ENET_MAXF_SIZE, 9438c2ecf20Sopenharmony_ci len); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Pass to upper layer */ 9468c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 9478c2ecf20Sopenharmony_ci netif_receive_skb(skb); 9488c2ecf20Sopenharmony_ci ndev->stats.rx_packets++; 9498c2ecf20Sopenharmony_ci ndev->stats.rx_bytes += len; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* Increment consume index */ 9548c2ecf20Sopenharmony_ci rxconsidx = rxconsidx + 1; 9558c2ecf20Sopenharmony_ci if (rxconsidx >= ENET_RX_DESC) 9568c2ecf20Sopenharmony_ci rxconsidx = 0; 9578c2ecf20Sopenharmony_ci writel(rxconsidx, 9588c2ecf20Sopenharmony_ci LPC_ENET_RXCONSUMEINDEX(pldat->net_base)); 9598c2ecf20Sopenharmony_ci rx_done++; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci return rx_done; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic int lpc_eth_poll(struct napi_struct *napi, int budget) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci struct netdata_local *pldat = container_of(napi, 9688c2ecf20Sopenharmony_ci struct netdata_local, napi); 9698c2ecf20Sopenharmony_ci struct net_device *ndev = pldat->ndev; 9708c2ecf20Sopenharmony_ci int rx_done = 0; 9718c2ecf20Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(ndev, 0); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci __netif_tx_lock(txq, smp_processor_id()); 9748c2ecf20Sopenharmony_ci __lpc_handle_xmit(ndev); 9758c2ecf20Sopenharmony_ci __netif_tx_unlock(txq); 9768c2ecf20Sopenharmony_ci rx_done = __lpc_handle_recv(ndev, budget); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (rx_done < budget) { 9798c2ecf20Sopenharmony_ci napi_complete_done(napi, rx_done); 9808c2ecf20Sopenharmony_ci lpc_eth_enable_int(pldat->net_base); 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci return rx_done; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic irqreturn_t __lpc_eth_interrupt(int irq, void *dev_id) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct net_device *ndev = dev_id; 9898c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 9908c2ecf20Sopenharmony_ci u32 tmp; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci spin_lock(&pldat->lock); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci tmp = readl(LPC_ENET_INTSTATUS(pldat->net_base)); 9958c2ecf20Sopenharmony_ci /* Clear interrupts */ 9968c2ecf20Sopenharmony_ci writel(tmp, LPC_ENET_INTCLEAR(pldat->net_base)); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci lpc_eth_disable_int(pldat->net_base); 9998c2ecf20Sopenharmony_ci if (likely(napi_schedule_prep(&pldat->napi))) 10008c2ecf20Sopenharmony_ci __napi_schedule(&pldat->napi); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci spin_unlock(&pldat->lock); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic int lpc_eth_close(struct net_device *ndev) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci unsigned long flags; 10108c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (netif_msg_ifdown(pldat)) 10138c2ecf20Sopenharmony_ci dev_dbg(&pldat->pdev->dev, "shutting down %s\n", ndev->name); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci napi_disable(&pldat->napi); 10168c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 10198c2ecf20Sopenharmony_ci __lpc_eth_reset(pldat); 10208c2ecf20Sopenharmony_ci netif_carrier_off(ndev); 10218c2ecf20Sopenharmony_ci writel(0, LPC_ENET_MAC1(pldat->net_base)); 10228c2ecf20Sopenharmony_ci writel(0, LPC_ENET_MAC2(pldat->net_base)); 10238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (ndev->phydev) 10268c2ecf20Sopenharmony_ci phy_stop(ndev->phydev); 10278c2ecf20Sopenharmony_ci clk_disable_unprepare(pldat->clk); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci return 0; 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic netdev_tx_t lpc_eth_hard_start_xmit(struct sk_buff *skb, 10338c2ecf20Sopenharmony_ci struct net_device *ndev) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 10368c2ecf20Sopenharmony_ci u32 len, txidx; 10378c2ecf20Sopenharmony_ci u32 *ptxstat; 10388c2ecf20Sopenharmony_ci struct txrx_desc_t *ptxrxdesc; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci len = skb->len; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci spin_lock_irq(&pldat->lock); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1)) { 10458c2ecf20Sopenharmony_ci /* This function should never be called when there are no 10468c2ecf20Sopenharmony_ci buffers */ 10478c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 10488c2ecf20Sopenharmony_ci spin_unlock_irq(&pldat->lock); 10498c2ecf20Sopenharmony_ci WARN(1, "BUG! TX request when no free TX buffers!\n"); 10508c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* Get the next TX descriptor index */ 10548c2ecf20Sopenharmony_ci txidx = readl(LPC_ENET_TXPRODUCEINDEX(pldat->net_base)); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci /* Setup control for the transfer */ 10578c2ecf20Sopenharmony_ci ptxstat = &pldat->tx_stat_v[txidx]; 10588c2ecf20Sopenharmony_ci *ptxstat = 0; 10598c2ecf20Sopenharmony_ci ptxrxdesc = &pldat->tx_desc_v[txidx]; 10608c2ecf20Sopenharmony_ci ptxrxdesc->control = 10618c2ecf20Sopenharmony_ci (len - 1) | TXDESC_CONTROL_LAST | TXDESC_CONTROL_INT; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci /* Copy data to the DMA buffer */ 10648c2ecf20Sopenharmony_ci memcpy(pldat->tx_buff_v + txidx * ENET_MAXF_SIZE, skb->data, len); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* Save the buffer and increment the buffer counter */ 10678c2ecf20Sopenharmony_ci pldat->skblen[txidx] = len; 10688c2ecf20Sopenharmony_ci pldat->num_used_tx_buffs++; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* Start transmit */ 10718c2ecf20Sopenharmony_ci txidx++; 10728c2ecf20Sopenharmony_ci if (txidx >= ENET_TX_DESC) 10738c2ecf20Sopenharmony_ci txidx = 0; 10748c2ecf20Sopenharmony_ci writel(txidx, LPC_ENET_TXPRODUCEINDEX(pldat->net_base)); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Stop queue if no more TX buffers */ 10778c2ecf20Sopenharmony_ci if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1)) 10788c2ecf20Sopenharmony_ci netif_stop_queue(ndev); 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci spin_unlock_irq(&pldat->lock); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 10838c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_cistatic int lpc_set_mac_address(struct net_device *ndev, void *p) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 10898c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 10908c2ecf20Sopenharmony_ci unsigned long flags; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 10938c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 10948c2ecf20Sopenharmony_ci memcpy(ndev->dev_addr, addr->sa_data, ETH_ALEN); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* Set station address */ 10998c2ecf20Sopenharmony_ci __lpc_set_mac(pldat, ndev->dev_addr); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void lpc_eth_set_multicast_list(struct net_device *ndev) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 11098c2ecf20Sopenharmony_ci struct netdev_hw_addr_list *mcptr = &ndev->mc; 11108c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 11118c2ecf20Sopenharmony_ci u32 tmp32, hash_val, hashlo, hashhi; 11128c2ecf20Sopenharmony_ci unsigned long flags; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* Set station address */ 11178c2ecf20Sopenharmony_ci __lpc_set_mac(pldat, ndev->dev_addr); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci tmp32 = LPC_RXFLTRW_ACCEPTUBROADCAST | LPC_RXFLTRW_ACCEPTPERFECT; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (ndev->flags & IFF_PROMISC) 11228c2ecf20Sopenharmony_ci tmp32 |= LPC_RXFLTRW_ACCEPTUNICAST | 11238c2ecf20Sopenharmony_ci LPC_RXFLTRW_ACCEPTUMULTICAST; 11248c2ecf20Sopenharmony_ci if (ndev->flags & IFF_ALLMULTI) 11258c2ecf20Sopenharmony_ci tmp32 |= LPC_RXFLTRW_ACCEPTUMULTICAST; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (netdev_hw_addr_list_count(mcptr)) 11288c2ecf20Sopenharmony_ci tmp32 |= LPC_RXFLTRW_ACCEPTUMULTICASTHASH; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci writel(tmp32, LPC_ENET_RXFILTER_CTRL(pldat->net_base)); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* Set initial hash table */ 11348c2ecf20Sopenharmony_ci hashlo = 0x0; 11358c2ecf20Sopenharmony_ci hashhi = 0x0; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci /* 64 bits : multicast address in hash table */ 11388c2ecf20Sopenharmony_ci netdev_hw_addr_list_for_each(ha, mcptr) { 11398c2ecf20Sopenharmony_ci hash_val = (ether_crc(6, ha->addr) >> 23) & 0x3F; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (hash_val >= 32) 11428c2ecf20Sopenharmony_ci hashhi |= 1 << (hash_val - 32); 11438c2ecf20Sopenharmony_ci else 11448c2ecf20Sopenharmony_ci hashlo |= 1 << hash_val; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci writel(hashlo, LPC_ENET_HASHFILTERL(pldat->net_base)); 11488c2ecf20Sopenharmony_ci writel(hashhi, LPC_ENET_HASHFILTERH(pldat->net_base)); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 11518c2ecf20Sopenharmony_ci} 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic int lpc_eth_open(struct net_device *ndev) 11548c2ecf20Sopenharmony_ci{ 11558c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 11568c2ecf20Sopenharmony_ci int ret; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (netif_msg_ifup(pldat)) 11598c2ecf20Sopenharmony_ci dev_dbg(&pldat->pdev->dev, "enabling %s\n", ndev->name); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pldat->clk); 11628c2ecf20Sopenharmony_ci if (ret) 11638c2ecf20Sopenharmony_ci return ret; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* Suspended PHY makes LPC ethernet core block, so resume now */ 11668c2ecf20Sopenharmony_ci phy_resume(ndev->phydev); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci /* Reset and initialize */ 11698c2ecf20Sopenharmony_ci __lpc_eth_reset(pldat); 11708c2ecf20Sopenharmony_ci __lpc_eth_init(pldat); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci /* schedule a link state check */ 11738c2ecf20Sopenharmony_ci phy_start(ndev->phydev); 11748c2ecf20Sopenharmony_ci netif_start_queue(ndev); 11758c2ecf20Sopenharmony_ci napi_enable(&pldat->napi); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return 0; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * Ethtool ops 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_cistatic void lpc_eth_ethtool_getdrvinfo(struct net_device *ndev, 11848c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci strlcpy(info->driver, MODNAME, sizeof(info->driver)); 11878c2ecf20Sopenharmony_ci strlcpy(info->version, DRV_VERSION, sizeof(info->version)); 11888c2ecf20Sopenharmony_ci strlcpy(info->bus_info, dev_name(ndev->dev.parent), 11898c2ecf20Sopenharmony_ci sizeof(info->bus_info)); 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic u32 lpc_eth_ethtool_getmsglevel(struct net_device *ndev) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return pldat->msg_enable; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic void lpc_eth_ethtool_setmsglevel(struct net_device *ndev, u32 level) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci pldat->msg_enable = level; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic const struct ethtool_ops lpc_eth_ethtool_ops = { 12078c2ecf20Sopenharmony_ci .get_drvinfo = lpc_eth_ethtool_getdrvinfo, 12088c2ecf20Sopenharmony_ci .get_msglevel = lpc_eth_ethtool_getmsglevel, 12098c2ecf20Sopenharmony_ci .set_msglevel = lpc_eth_ethtool_setmsglevel, 12108c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 12118c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 12128c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 12138c2ecf20Sopenharmony_ci}; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic const struct net_device_ops lpc_netdev_ops = { 12168c2ecf20Sopenharmony_ci .ndo_open = lpc_eth_open, 12178c2ecf20Sopenharmony_ci .ndo_stop = lpc_eth_close, 12188c2ecf20Sopenharmony_ci .ndo_start_xmit = lpc_eth_hard_start_xmit, 12198c2ecf20Sopenharmony_ci .ndo_set_rx_mode = lpc_eth_set_multicast_list, 12208c2ecf20Sopenharmony_ci .ndo_do_ioctl = phy_do_ioctl_running, 12218c2ecf20Sopenharmony_ci .ndo_set_mac_address = lpc_set_mac_address, 12228c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 12238c2ecf20Sopenharmony_ci}; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic int lpc_eth_drv_probe(struct platform_device *pdev) 12268c2ecf20Sopenharmony_ci{ 12278c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 12288c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 12298c2ecf20Sopenharmony_ci struct netdata_local *pldat; 12308c2ecf20Sopenharmony_ci struct net_device *ndev; 12318c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 12328c2ecf20Sopenharmony_ci struct resource *res; 12338c2ecf20Sopenharmony_ci int irq, ret; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* Setup network interface for RMII or MII mode */ 12368c2ecf20Sopenharmony_ci lpc32xx_set_phy_interface_mode(lpc_phy_interface_mode(dev)); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* Get platform resources */ 12398c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 12408c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 12418c2ecf20Sopenharmony_ci if (!res || irq < 0) { 12428c2ecf20Sopenharmony_ci dev_err(dev, "error getting resources.\n"); 12438c2ecf20Sopenharmony_ci ret = -ENXIO; 12448c2ecf20Sopenharmony_ci goto err_exit; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* Allocate net driver data structure */ 12488c2ecf20Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct netdata_local)); 12498c2ecf20Sopenharmony_ci if (!ndev) { 12508c2ecf20Sopenharmony_ci dev_err(dev, "could not allocate device.\n"); 12518c2ecf20Sopenharmony_ci ret = -ENOMEM; 12528c2ecf20Sopenharmony_ci goto err_exit; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, dev); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci pldat = netdev_priv(ndev); 12588c2ecf20Sopenharmony_ci pldat->pdev = pdev; 12598c2ecf20Sopenharmony_ci pldat->ndev = ndev; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci spin_lock_init(&pldat->lock); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci /* Save resources */ 12648c2ecf20Sopenharmony_ci ndev->irq = irq; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Get clock for the device */ 12678c2ecf20Sopenharmony_ci pldat->clk = clk_get(dev, NULL); 12688c2ecf20Sopenharmony_ci if (IS_ERR(pldat->clk)) { 12698c2ecf20Sopenharmony_ci dev_err(dev, "error getting clock.\n"); 12708c2ecf20Sopenharmony_ci ret = PTR_ERR(pldat->clk); 12718c2ecf20Sopenharmony_ci goto err_out_free_dev; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci /* Enable network clock */ 12758c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pldat->clk); 12768c2ecf20Sopenharmony_ci if (ret) 12778c2ecf20Sopenharmony_ci goto err_out_clk_put; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* Map IO space */ 12808c2ecf20Sopenharmony_ci pldat->net_base = ioremap(res->start, resource_size(res)); 12818c2ecf20Sopenharmony_ci if (!pldat->net_base) { 12828c2ecf20Sopenharmony_ci dev_err(dev, "failed to map registers\n"); 12838c2ecf20Sopenharmony_ci ret = -ENOMEM; 12848c2ecf20Sopenharmony_ci goto err_out_disable_clocks; 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci ret = request_irq(ndev->irq, __lpc_eth_interrupt, 0, 12878c2ecf20Sopenharmony_ci ndev->name, ndev); 12888c2ecf20Sopenharmony_ci if (ret) { 12898c2ecf20Sopenharmony_ci dev_err(dev, "error requesting interrupt.\n"); 12908c2ecf20Sopenharmony_ci goto err_out_iounmap; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* Setup driver functions */ 12948c2ecf20Sopenharmony_ci ndev->netdev_ops = &lpc_netdev_ops; 12958c2ecf20Sopenharmony_ci ndev->ethtool_ops = &lpc_eth_ethtool_ops; 12968c2ecf20Sopenharmony_ci ndev->watchdog_timeo = msecs_to_jiffies(2500); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* Get size of DMA buffers/descriptors region */ 12998c2ecf20Sopenharmony_ci pldat->dma_buff_size = (ENET_TX_DESC + ENET_RX_DESC) * (ENET_MAXF_SIZE + 13008c2ecf20Sopenharmony_ci sizeof(struct txrx_desc_t) + sizeof(struct rx_status_t)); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (use_iram_for_net(dev)) { 13038c2ecf20Sopenharmony_ci if (pldat->dma_buff_size > 13048c2ecf20Sopenharmony_ci lpc32xx_return_iram(&pldat->dma_buff_base_v, &dma_handle)) { 13058c2ecf20Sopenharmony_ci pldat->dma_buff_base_v = NULL; 13068c2ecf20Sopenharmony_ci pldat->dma_buff_size = 0; 13078c2ecf20Sopenharmony_ci netdev_err(ndev, 13088c2ecf20Sopenharmony_ci "IRAM not big enough for net buffers, using SDRAM instead.\n"); 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (pldat->dma_buff_base_v == NULL) { 13138c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); 13148c2ecf20Sopenharmony_ci if (ret) 13158c2ecf20Sopenharmony_ci goto err_out_free_irq; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci pldat->dma_buff_size = PAGE_ALIGN(pldat->dma_buff_size); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* Allocate a chunk of memory for the DMA ethernet buffers 13208c2ecf20Sopenharmony_ci and descriptors */ 13218c2ecf20Sopenharmony_ci pldat->dma_buff_base_v = 13228c2ecf20Sopenharmony_ci dma_alloc_coherent(dev, 13238c2ecf20Sopenharmony_ci pldat->dma_buff_size, &dma_handle, 13248c2ecf20Sopenharmony_ci GFP_KERNEL); 13258c2ecf20Sopenharmony_ci if (pldat->dma_buff_base_v == NULL) { 13268c2ecf20Sopenharmony_ci ret = -ENOMEM; 13278c2ecf20Sopenharmony_ci goto err_out_free_irq; 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci pldat->dma_buff_base_p = dma_handle; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci netdev_dbg(ndev, "IO address space :%pR\n", res); 13338c2ecf20Sopenharmony_ci netdev_dbg(ndev, "IO address size :%zd\n", 13348c2ecf20Sopenharmony_ci (size_t)resource_size(res)); 13358c2ecf20Sopenharmony_ci netdev_dbg(ndev, "IO address (mapped) :0x%p\n", 13368c2ecf20Sopenharmony_ci pldat->net_base); 13378c2ecf20Sopenharmony_ci netdev_dbg(ndev, "IRQ number :%d\n", ndev->irq); 13388c2ecf20Sopenharmony_ci netdev_dbg(ndev, "DMA buffer size :%zd\n", pldat->dma_buff_size); 13398c2ecf20Sopenharmony_ci netdev_dbg(ndev, "DMA buffer P address :%pad\n", 13408c2ecf20Sopenharmony_ci &pldat->dma_buff_base_p); 13418c2ecf20Sopenharmony_ci netdev_dbg(ndev, "DMA buffer V address :0x%p\n", 13428c2ecf20Sopenharmony_ci pldat->dma_buff_base_v); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci pldat->phy_node = of_parse_phandle(np, "phy-handle", 0); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci /* Get MAC address from current HW setting (POR state is all zeros) */ 13478c2ecf20Sopenharmony_ci __lpc_get_mac(pldat, ndev->dev_addr); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(ndev->dev_addr)) { 13508c2ecf20Sopenharmony_ci const char *macaddr = of_get_mac_address(np); 13518c2ecf20Sopenharmony_ci if (!IS_ERR(macaddr)) 13528c2ecf20Sopenharmony_ci ether_addr_copy(ndev->dev_addr, macaddr); 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(ndev->dev_addr)) 13558c2ecf20Sopenharmony_ci eth_hw_addr_random(ndev); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci /* then shut everything down to save power */ 13588c2ecf20Sopenharmony_ci __lpc_eth_shutdown(pldat); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* Set default parameters */ 13618c2ecf20Sopenharmony_ci pldat->msg_enable = NETIF_MSG_LINK; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci /* Force an MII interface reset and clock setup */ 13648c2ecf20Sopenharmony_ci __lpc_mii_mngt_reset(pldat); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci /* Force default PHY interface setup in chip, this will probably be 13678c2ecf20Sopenharmony_ci changed by the PHY driver */ 13688c2ecf20Sopenharmony_ci pldat->link = 0; 13698c2ecf20Sopenharmony_ci pldat->speed = 100; 13708c2ecf20Sopenharmony_ci pldat->duplex = DUPLEX_FULL; 13718c2ecf20Sopenharmony_ci __lpc_params_setup(pldat); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci netif_napi_add(ndev, &pldat->napi, lpc_eth_poll, NAPI_WEIGHT); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci ret = register_netdev(ndev); 13768c2ecf20Sopenharmony_ci if (ret) { 13778c2ecf20Sopenharmony_ci dev_err(dev, "Cannot register net device, aborting.\n"); 13788c2ecf20Sopenharmony_ci goto err_out_dma_unmap; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ndev); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci ret = lpc_mii_init(pldat); 13838c2ecf20Sopenharmony_ci if (ret) 13848c2ecf20Sopenharmony_ci goto err_out_unregister_netdev; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci netdev_info(ndev, "LPC mac at 0x%08lx irq %d\n", 13878c2ecf20Sopenharmony_ci (unsigned long)res->start, ndev->irq); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci device_init_wakeup(dev, 1); 13908c2ecf20Sopenharmony_ci device_set_wakeup_enable(dev, 0); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci return 0; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_cierr_out_unregister_netdev: 13958c2ecf20Sopenharmony_ci unregister_netdev(ndev); 13968c2ecf20Sopenharmony_cierr_out_dma_unmap: 13978c2ecf20Sopenharmony_ci if (!use_iram_for_net(dev) || 13988c2ecf20Sopenharmony_ci pldat->dma_buff_size > lpc32xx_return_iram(NULL, NULL)) 13998c2ecf20Sopenharmony_ci dma_free_coherent(dev, pldat->dma_buff_size, 14008c2ecf20Sopenharmony_ci pldat->dma_buff_base_v, 14018c2ecf20Sopenharmony_ci pldat->dma_buff_base_p); 14028c2ecf20Sopenharmony_cierr_out_free_irq: 14038c2ecf20Sopenharmony_ci free_irq(ndev->irq, ndev); 14048c2ecf20Sopenharmony_cierr_out_iounmap: 14058c2ecf20Sopenharmony_ci iounmap(pldat->net_base); 14068c2ecf20Sopenharmony_cierr_out_disable_clocks: 14078c2ecf20Sopenharmony_ci clk_disable_unprepare(pldat->clk); 14088c2ecf20Sopenharmony_cierr_out_clk_put: 14098c2ecf20Sopenharmony_ci clk_put(pldat->clk); 14108c2ecf20Sopenharmony_cierr_out_free_dev: 14118c2ecf20Sopenharmony_ci free_netdev(ndev); 14128c2ecf20Sopenharmony_cierr_exit: 14138c2ecf20Sopenharmony_ci pr_err("%s: not found (%d).\n", MODNAME, ret); 14148c2ecf20Sopenharmony_ci return ret; 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cistatic int lpc_eth_drv_remove(struct platform_device *pdev) 14188c2ecf20Sopenharmony_ci{ 14198c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 14208c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci unregister_netdev(ndev); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if (!use_iram_for_net(&pldat->pdev->dev) || 14258c2ecf20Sopenharmony_ci pldat->dma_buff_size > lpc32xx_return_iram(NULL, NULL)) 14268c2ecf20Sopenharmony_ci dma_free_coherent(&pldat->pdev->dev, pldat->dma_buff_size, 14278c2ecf20Sopenharmony_ci pldat->dma_buff_base_v, 14288c2ecf20Sopenharmony_ci pldat->dma_buff_base_p); 14298c2ecf20Sopenharmony_ci free_irq(ndev->irq, ndev); 14308c2ecf20Sopenharmony_ci iounmap(pldat->net_base); 14318c2ecf20Sopenharmony_ci mdiobus_unregister(pldat->mii_bus); 14328c2ecf20Sopenharmony_ci mdiobus_free(pldat->mii_bus); 14338c2ecf20Sopenharmony_ci clk_disable_unprepare(pldat->clk); 14348c2ecf20Sopenharmony_ci clk_put(pldat->clk); 14358c2ecf20Sopenharmony_ci free_netdev(ndev); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci return 0; 14388c2ecf20Sopenharmony_ci} 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 14418c2ecf20Sopenharmony_cistatic int lpc_eth_drv_suspend(struct platform_device *pdev, 14428c2ecf20Sopenharmony_ci pm_message_t state) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 14458c2ecf20Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 14488c2ecf20Sopenharmony_ci enable_irq_wake(ndev->irq); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (ndev) { 14518c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 14528c2ecf20Sopenharmony_ci netif_device_detach(ndev); 14538c2ecf20Sopenharmony_ci __lpc_eth_shutdown(pldat); 14548c2ecf20Sopenharmony_ci clk_disable_unprepare(pldat->clk); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci /* 14578c2ecf20Sopenharmony_ci * Reset again now clock is disable to be sure 14588c2ecf20Sopenharmony_ci * EMC_MDC is down 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_ci __lpc_eth_reset(pldat); 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci return 0; 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_cistatic int lpc_eth_drv_resume(struct platform_device *pdev) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 14708c2ecf20Sopenharmony_ci struct netdata_local *pldat; 14718c2ecf20Sopenharmony_ci int ret; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 14748c2ecf20Sopenharmony_ci disable_irq_wake(ndev->irq); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci if (ndev) { 14778c2ecf20Sopenharmony_ci if (netif_running(ndev)) { 14788c2ecf20Sopenharmony_ci pldat = netdev_priv(ndev); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* Enable interface clock */ 14818c2ecf20Sopenharmony_ci ret = clk_enable(pldat->clk); 14828c2ecf20Sopenharmony_ci if (ret) 14838c2ecf20Sopenharmony_ci return ret; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci /* Reset and initialize */ 14868c2ecf20Sopenharmony_ci __lpc_eth_reset(pldat); 14878c2ecf20Sopenharmony_ci __lpc_eth_init(pldat); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci netif_device_attach(ndev); 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci return 0; 14948c2ecf20Sopenharmony_ci} 14958c2ecf20Sopenharmony_ci#endif 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cistatic const struct of_device_id lpc_eth_match[] = { 14988c2ecf20Sopenharmony_ci { .compatible = "nxp,lpc-eth" }, 14998c2ecf20Sopenharmony_ci { } 15008c2ecf20Sopenharmony_ci}; 15018c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc_eth_match); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cistatic struct platform_driver lpc_eth_driver = { 15048c2ecf20Sopenharmony_ci .probe = lpc_eth_drv_probe, 15058c2ecf20Sopenharmony_ci .remove = lpc_eth_drv_remove, 15068c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 15078c2ecf20Sopenharmony_ci .suspend = lpc_eth_drv_suspend, 15088c2ecf20Sopenharmony_ci .resume = lpc_eth_drv_resume, 15098c2ecf20Sopenharmony_ci#endif 15108c2ecf20Sopenharmony_ci .driver = { 15118c2ecf20Sopenharmony_ci .name = MODNAME, 15128c2ecf20Sopenharmony_ci .of_match_table = lpc_eth_match, 15138c2ecf20Sopenharmony_ci }, 15148c2ecf20Sopenharmony_ci}; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cimodule_platform_driver(lpc_eth_driver); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); 15198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 15208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LPC Ethernet Driver"); 15218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1522