162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/net/ethernet/nxp/lpc_eth.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Kevin Wells <kevin.wells@nxp.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2010 NXP Semiconductors 862306a36Sopenharmony_ci * Copyright (C) 2012 Roland Stigge <stigge@antcom.de> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/clk.h> 1462306a36Sopenharmony_ci#include <linux/crc32.h> 1562306a36Sopenharmony_ci#include <linux/etherdevice.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_mdio.h> 1962306a36Sopenharmony_ci#include <linux/of_net.h> 2062306a36Sopenharmony_ci#include <linux/phy.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/spinlock.h> 2362306a36Sopenharmony_ci#include <linux/soc/nxp/lpc32xx-misc.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MODNAME "lpc-eth" 2662306a36Sopenharmony_ci#define DRV_VERSION "1.00" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define ENET_MAXF_SIZE 1536 2962306a36Sopenharmony_ci#define ENET_RX_DESC 48 3062306a36Sopenharmony_ci#define ENET_TX_DESC 16 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define NAPI_WEIGHT 16 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Ethernet MAC controller Register offsets 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci#define LPC_ENET_MAC1(x) (x + 0x000) 3862306a36Sopenharmony_ci#define LPC_ENET_MAC2(x) (x + 0x004) 3962306a36Sopenharmony_ci#define LPC_ENET_IPGT(x) (x + 0x008) 4062306a36Sopenharmony_ci#define LPC_ENET_IPGR(x) (x + 0x00C) 4162306a36Sopenharmony_ci#define LPC_ENET_CLRT(x) (x + 0x010) 4262306a36Sopenharmony_ci#define LPC_ENET_MAXF(x) (x + 0x014) 4362306a36Sopenharmony_ci#define LPC_ENET_SUPP(x) (x + 0x018) 4462306a36Sopenharmony_ci#define LPC_ENET_TEST(x) (x + 0x01C) 4562306a36Sopenharmony_ci#define LPC_ENET_MCFG(x) (x + 0x020) 4662306a36Sopenharmony_ci#define LPC_ENET_MCMD(x) (x + 0x024) 4762306a36Sopenharmony_ci#define LPC_ENET_MADR(x) (x + 0x028) 4862306a36Sopenharmony_ci#define LPC_ENET_MWTD(x) (x + 0x02C) 4962306a36Sopenharmony_ci#define LPC_ENET_MRDD(x) (x + 0x030) 5062306a36Sopenharmony_ci#define LPC_ENET_MIND(x) (x + 0x034) 5162306a36Sopenharmony_ci#define LPC_ENET_SA0(x) (x + 0x040) 5262306a36Sopenharmony_ci#define LPC_ENET_SA1(x) (x + 0x044) 5362306a36Sopenharmony_ci#define LPC_ENET_SA2(x) (x + 0x048) 5462306a36Sopenharmony_ci#define LPC_ENET_COMMAND(x) (x + 0x100) 5562306a36Sopenharmony_ci#define LPC_ENET_STATUS(x) (x + 0x104) 5662306a36Sopenharmony_ci#define LPC_ENET_RXDESCRIPTOR(x) (x + 0x108) 5762306a36Sopenharmony_ci#define LPC_ENET_RXSTATUS(x) (x + 0x10C) 5862306a36Sopenharmony_ci#define LPC_ENET_RXDESCRIPTORNUMBER(x) (x + 0x110) 5962306a36Sopenharmony_ci#define LPC_ENET_RXPRODUCEINDEX(x) (x + 0x114) 6062306a36Sopenharmony_ci#define LPC_ENET_RXCONSUMEINDEX(x) (x + 0x118) 6162306a36Sopenharmony_ci#define LPC_ENET_TXDESCRIPTOR(x) (x + 0x11C) 6262306a36Sopenharmony_ci#define LPC_ENET_TXSTATUS(x) (x + 0x120) 6362306a36Sopenharmony_ci#define LPC_ENET_TXDESCRIPTORNUMBER(x) (x + 0x124) 6462306a36Sopenharmony_ci#define LPC_ENET_TXPRODUCEINDEX(x) (x + 0x128) 6562306a36Sopenharmony_ci#define LPC_ENET_TXCONSUMEINDEX(x) (x + 0x12C) 6662306a36Sopenharmony_ci#define LPC_ENET_TSV0(x) (x + 0x158) 6762306a36Sopenharmony_ci#define LPC_ENET_TSV1(x) (x + 0x15C) 6862306a36Sopenharmony_ci#define LPC_ENET_RSV(x) (x + 0x160) 6962306a36Sopenharmony_ci#define LPC_ENET_FLOWCONTROLCOUNTER(x) (x + 0x170) 7062306a36Sopenharmony_ci#define LPC_ENET_FLOWCONTROLSTATUS(x) (x + 0x174) 7162306a36Sopenharmony_ci#define LPC_ENET_RXFILTER_CTRL(x) (x + 0x200) 7262306a36Sopenharmony_ci#define LPC_ENET_RXFILTERWOLSTATUS(x) (x + 0x204) 7362306a36Sopenharmony_ci#define LPC_ENET_RXFILTERWOLCLEAR(x) (x + 0x208) 7462306a36Sopenharmony_ci#define LPC_ENET_HASHFILTERL(x) (x + 0x210) 7562306a36Sopenharmony_ci#define LPC_ENET_HASHFILTERH(x) (x + 0x214) 7662306a36Sopenharmony_ci#define LPC_ENET_INTSTATUS(x) (x + 0xFE0) 7762306a36Sopenharmony_ci#define LPC_ENET_INTENABLE(x) (x + 0xFE4) 7862306a36Sopenharmony_ci#define LPC_ENET_INTCLEAR(x) (x + 0xFE8) 7962306a36Sopenharmony_ci#define LPC_ENET_INTSET(x) (x + 0xFEC) 8062306a36Sopenharmony_ci#define LPC_ENET_POWERDOWN(x) (x + 0xFF4) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * mac1 register definitions 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci#define LPC_MAC1_RECV_ENABLE (1 << 0) 8662306a36Sopenharmony_ci#define LPC_MAC1_PASS_ALL_RX_FRAMES (1 << 1) 8762306a36Sopenharmony_ci#define LPC_MAC1_RX_FLOW_CONTROL (1 << 2) 8862306a36Sopenharmony_ci#define LPC_MAC1_TX_FLOW_CONTROL (1 << 3) 8962306a36Sopenharmony_ci#define LPC_MAC1_LOOPBACK (1 << 4) 9062306a36Sopenharmony_ci#define LPC_MAC1_RESET_TX (1 << 8) 9162306a36Sopenharmony_ci#define LPC_MAC1_RESET_MCS_TX (1 << 9) 9262306a36Sopenharmony_ci#define LPC_MAC1_RESET_RX (1 << 10) 9362306a36Sopenharmony_ci#define LPC_MAC1_RESET_MCS_RX (1 << 11) 9462306a36Sopenharmony_ci#define LPC_MAC1_SIMULATION_RESET (1 << 14) 9562306a36Sopenharmony_ci#define LPC_MAC1_SOFT_RESET (1 << 15) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * mac2 register definitions 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci#define LPC_MAC2_FULL_DUPLEX (1 << 0) 10162306a36Sopenharmony_ci#define LPC_MAC2_FRAME_LENGTH_CHECKING (1 << 1) 10262306a36Sopenharmony_ci#define LPC_MAC2_HUGH_LENGTH_CHECKING (1 << 2) 10362306a36Sopenharmony_ci#define LPC_MAC2_DELAYED_CRC (1 << 3) 10462306a36Sopenharmony_ci#define LPC_MAC2_CRC_ENABLE (1 << 4) 10562306a36Sopenharmony_ci#define LPC_MAC2_PAD_CRC_ENABLE (1 << 5) 10662306a36Sopenharmony_ci#define LPC_MAC2_VLAN_PAD_ENABLE (1 << 6) 10762306a36Sopenharmony_ci#define LPC_MAC2_AUTO_DETECT_PAD_ENABLE (1 << 7) 10862306a36Sopenharmony_ci#define LPC_MAC2_PURE_PREAMBLE_ENFORCEMENT (1 << 8) 10962306a36Sopenharmony_ci#define LPC_MAC2_LONG_PREAMBLE_ENFORCEMENT (1 << 9) 11062306a36Sopenharmony_ci#define LPC_MAC2_NO_BACKOFF (1 << 12) 11162306a36Sopenharmony_ci#define LPC_MAC2_BACK_PRESSURE (1 << 13) 11262306a36Sopenharmony_ci#define LPC_MAC2_EXCESS_DEFER (1 << 14) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * ipgt register definitions 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci#define LPC_IPGT_LOAD(n) ((n) & 0x7F) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * ipgr register definitions 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci#define LPC_IPGR_LOAD_PART2(n) ((n) & 0x7F) 12362306a36Sopenharmony_ci#define LPC_IPGR_LOAD_PART1(n) (((n) & 0x7F) << 8) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* 12662306a36Sopenharmony_ci * clrt register definitions 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci#define LPC_CLRT_LOAD_RETRY_MAX(n) ((n) & 0xF) 12962306a36Sopenharmony_ci#define LPC_CLRT_LOAD_COLLISION_WINDOW(n) (((n) & 0x3F) << 8) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * maxf register definitions 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci#define LPC_MAXF_LOAD_MAX_FRAME_LEN(n) ((n) & 0xFFFF) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * supp register definitions 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci#define LPC_SUPP_SPEED (1 << 8) 14062306a36Sopenharmony_ci#define LPC_SUPP_RESET_RMII (1 << 11) 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * test register definitions 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci#define LPC_TEST_SHORTCUT_PAUSE_QUANTA (1 << 0) 14662306a36Sopenharmony_ci#define LPC_TEST_PAUSE (1 << 1) 14762306a36Sopenharmony_ci#define LPC_TEST_BACKPRESSURE (1 << 2) 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * mcfg register definitions 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci#define LPC_MCFG_SCAN_INCREMENT (1 << 0) 15362306a36Sopenharmony_ci#define LPC_MCFG_SUPPRESS_PREAMBLE (1 << 1) 15462306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_SELECT(n) (((n) & 0x7) << 2) 15562306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_4 0 15662306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_6 2 15762306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_8 3 15862306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_10 4 15962306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_14 5 16062306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_20 6 16162306a36Sopenharmony_ci#define LPC_MCFG_CLOCK_HOST_DIV_28 7 16262306a36Sopenharmony_ci#define LPC_MCFG_RESET_MII_MGMT (1 << 15) 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * mcmd register definitions 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci#define LPC_MCMD_READ (1 << 0) 16862306a36Sopenharmony_ci#define LPC_MCMD_SCAN (1 << 1) 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* 17162306a36Sopenharmony_ci * madr register definitions 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci#define LPC_MADR_REGISTER_ADDRESS(n) ((n) & 0x1F) 17462306a36Sopenharmony_ci#define LPC_MADR_PHY_0ADDRESS(n) (((n) & 0x1F) << 8) 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* 17762306a36Sopenharmony_ci * mwtd register definitions 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci#define LPC_MWDT_WRITE(n) ((n) & 0xFFFF) 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* 18262306a36Sopenharmony_ci * mrdd register definitions 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci#define LPC_MRDD_READ_MASK 0xFFFF 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * mind register definitions 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci#define LPC_MIND_BUSY (1 << 0) 19062306a36Sopenharmony_ci#define LPC_MIND_SCANNING (1 << 1) 19162306a36Sopenharmony_ci#define LPC_MIND_NOT_VALID (1 << 2) 19262306a36Sopenharmony_ci#define LPC_MIND_MII_LINK_FAIL (1 << 3) 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * command register definitions 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci#define LPC_COMMAND_RXENABLE (1 << 0) 19862306a36Sopenharmony_ci#define LPC_COMMAND_TXENABLE (1 << 1) 19962306a36Sopenharmony_ci#define LPC_COMMAND_REG_RESET (1 << 3) 20062306a36Sopenharmony_ci#define LPC_COMMAND_TXRESET (1 << 4) 20162306a36Sopenharmony_ci#define LPC_COMMAND_RXRESET (1 << 5) 20262306a36Sopenharmony_ci#define LPC_COMMAND_PASSRUNTFRAME (1 << 6) 20362306a36Sopenharmony_ci#define LPC_COMMAND_PASSRXFILTER (1 << 7) 20462306a36Sopenharmony_ci#define LPC_COMMAND_TXFLOWCONTROL (1 << 8) 20562306a36Sopenharmony_ci#define LPC_COMMAND_RMII (1 << 9) 20662306a36Sopenharmony_ci#define LPC_COMMAND_FULLDUPLEX (1 << 10) 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * status register definitions 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci#define LPC_STATUS_RXACTIVE (1 << 0) 21262306a36Sopenharmony_ci#define LPC_STATUS_TXACTIVE (1 << 1) 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * tsv0 register definitions 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci#define LPC_TSV0_CRC_ERROR (1 << 0) 21862306a36Sopenharmony_ci#define LPC_TSV0_LENGTH_CHECK_ERROR (1 << 1) 21962306a36Sopenharmony_ci#define LPC_TSV0_LENGTH_OUT_OF_RANGE (1 << 2) 22062306a36Sopenharmony_ci#define LPC_TSV0_DONE (1 << 3) 22162306a36Sopenharmony_ci#define LPC_TSV0_MULTICAST (1 << 4) 22262306a36Sopenharmony_ci#define LPC_TSV0_BROADCAST (1 << 5) 22362306a36Sopenharmony_ci#define LPC_TSV0_PACKET_DEFER (1 << 6) 22462306a36Sopenharmony_ci#define LPC_TSV0_ESCESSIVE_DEFER (1 << 7) 22562306a36Sopenharmony_ci#define LPC_TSV0_ESCESSIVE_COLLISION (1 << 8) 22662306a36Sopenharmony_ci#define LPC_TSV0_LATE_COLLISION (1 << 9) 22762306a36Sopenharmony_ci#define LPC_TSV0_GIANT (1 << 10) 22862306a36Sopenharmony_ci#define LPC_TSV0_UNDERRUN (1 << 11) 22962306a36Sopenharmony_ci#define LPC_TSV0_TOTAL_BYTES(n) (((n) >> 12) & 0xFFFF) 23062306a36Sopenharmony_ci#define LPC_TSV0_CONTROL_FRAME (1 << 28) 23162306a36Sopenharmony_ci#define LPC_TSV0_PAUSE (1 << 29) 23262306a36Sopenharmony_ci#define LPC_TSV0_BACKPRESSURE (1 << 30) 23362306a36Sopenharmony_ci#define LPC_TSV0_VLAN (1 << 31) 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * tsv1 register definitions 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci#define LPC_TSV1_TRANSMIT_BYTE_COUNT(n) ((n) & 0xFFFF) 23962306a36Sopenharmony_ci#define LPC_TSV1_COLLISION_COUNT(n) (((n) >> 16) & 0xF) 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* 24262306a36Sopenharmony_ci * rsv register definitions 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci#define LPC_RSV_RECEIVED_BYTE_COUNT(n) ((n) & 0xFFFF) 24562306a36Sopenharmony_ci#define LPC_RSV_RXDV_EVENT_IGNORED (1 << 16) 24662306a36Sopenharmony_ci#define LPC_RSV_RXDV_EVENT_PREVIOUSLY_SEEN (1 << 17) 24762306a36Sopenharmony_ci#define LPC_RSV_CARRIER_EVNT_PREVIOUS_SEEN (1 << 18) 24862306a36Sopenharmony_ci#define LPC_RSV_RECEIVE_CODE_VIOLATION (1 << 19) 24962306a36Sopenharmony_ci#define LPC_RSV_CRC_ERROR (1 << 20) 25062306a36Sopenharmony_ci#define LPC_RSV_LENGTH_CHECK_ERROR (1 << 21) 25162306a36Sopenharmony_ci#define LPC_RSV_LENGTH_OUT_OF_RANGE (1 << 22) 25262306a36Sopenharmony_ci#define LPC_RSV_RECEIVE_OK (1 << 23) 25362306a36Sopenharmony_ci#define LPC_RSV_MULTICAST (1 << 24) 25462306a36Sopenharmony_ci#define LPC_RSV_BROADCAST (1 << 25) 25562306a36Sopenharmony_ci#define LPC_RSV_DRIBBLE_NIBBLE (1 << 26) 25662306a36Sopenharmony_ci#define LPC_RSV_CONTROL_FRAME (1 << 27) 25762306a36Sopenharmony_ci#define LPC_RSV_PAUSE (1 << 28) 25862306a36Sopenharmony_ci#define LPC_RSV_UNSUPPORTED_OPCODE (1 << 29) 25962306a36Sopenharmony_ci#define LPC_RSV_VLAN (1 << 30) 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci/* 26262306a36Sopenharmony_ci * flowcontrolcounter register definitions 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci#define LPC_FCCR_MIRRORCOUNTER(n) ((n) & 0xFFFF) 26562306a36Sopenharmony_ci#define LPC_FCCR_PAUSETIMER(n) (((n) >> 16) & 0xFFFF) 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * flowcontrolstatus register definitions 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ci#define LPC_FCCR_MIRRORCOUNTERCURRENT(n) ((n) & 0xFFFF) 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/* 27362306a36Sopenharmony_ci * rxfilterctrl, rxfilterwolstatus, and rxfilterwolclear shared 27462306a36Sopenharmony_ci * register definitions 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUNICAST (1 << 0) 27762306a36Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUBROADCAST (1 << 1) 27862306a36Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUMULTICAST (1 << 2) 27962306a36Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUNICASTHASH (1 << 3) 28062306a36Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTUMULTICASTHASH (1 << 4) 28162306a36Sopenharmony_ci#define LPC_RXFLTRW_ACCEPTPERFECT (1 << 5) 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci/* 28462306a36Sopenharmony_ci * rxfilterctrl register definitions 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci#define LPC_RXFLTRWSTS_MAGICPACKETENWOL (1 << 12) 28762306a36Sopenharmony_ci#define LPC_RXFLTRWSTS_RXFILTERENWOL (1 << 13) 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* 29062306a36Sopenharmony_ci * rxfilterwolstatus/rxfilterwolclear register definitions 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci#define LPC_RXFLTRWSTS_RXFILTERWOL (1 << 7) 29362306a36Sopenharmony_ci#define LPC_RXFLTRWSTS_MAGICPACKETWOL (1 << 8) 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* 29662306a36Sopenharmony_ci * intstatus, intenable, intclear, and Intset shared register 29762306a36Sopenharmony_ci * definitions 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci#define LPC_MACINT_RXOVERRUNINTEN (1 << 0) 30062306a36Sopenharmony_ci#define LPC_MACINT_RXERRORONINT (1 << 1) 30162306a36Sopenharmony_ci#define LPC_MACINT_RXFINISHEDINTEN (1 << 2) 30262306a36Sopenharmony_ci#define LPC_MACINT_RXDONEINTEN (1 << 3) 30362306a36Sopenharmony_ci#define LPC_MACINT_TXUNDERRUNINTEN (1 << 4) 30462306a36Sopenharmony_ci#define LPC_MACINT_TXERRORINTEN (1 << 5) 30562306a36Sopenharmony_ci#define LPC_MACINT_TXFINISHEDINTEN (1 << 6) 30662306a36Sopenharmony_ci#define LPC_MACINT_TXDONEINTEN (1 << 7) 30762306a36Sopenharmony_ci#define LPC_MACINT_SOFTINTEN (1 << 12) 30862306a36Sopenharmony_ci#define LPC_MACINT_WAKEUPINTEN (1 << 13) 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* 31162306a36Sopenharmony_ci * powerdown register definitions 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci#define LPC_POWERDOWN_MACAHB (1 << 31) 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic phy_interface_t lpc_phy_interface_mode(struct device *dev) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci if (dev && dev->of_node) { 31862306a36Sopenharmony_ci const char *mode = of_get_property(dev->of_node, 31962306a36Sopenharmony_ci "phy-mode", NULL); 32062306a36Sopenharmony_ci if (mode && !strcmp(mode, "mii")) 32162306a36Sopenharmony_ci return PHY_INTERFACE_MODE_MII; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci return PHY_INTERFACE_MODE_RMII; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic bool use_iram_for_net(struct device *dev) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci if (dev && dev->of_node) 32962306a36Sopenharmony_ci return of_property_read_bool(dev->of_node, "use-iram"); 33062306a36Sopenharmony_ci return false; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* Receive Status information word */ 33462306a36Sopenharmony_ci#define RXSTATUS_SIZE 0x000007FF 33562306a36Sopenharmony_ci#define RXSTATUS_CONTROL (1 << 18) 33662306a36Sopenharmony_ci#define RXSTATUS_VLAN (1 << 19) 33762306a36Sopenharmony_ci#define RXSTATUS_FILTER (1 << 20) 33862306a36Sopenharmony_ci#define RXSTATUS_MULTICAST (1 << 21) 33962306a36Sopenharmony_ci#define RXSTATUS_BROADCAST (1 << 22) 34062306a36Sopenharmony_ci#define RXSTATUS_CRC (1 << 23) 34162306a36Sopenharmony_ci#define RXSTATUS_SYMBOL (1 << 24) 34262306a36Sopenharmony_ci#define RXSTATUS_LENGTH (1 << 25) 34362306a36Sopenharmony_ci#define RXSTATUS_RANGE (1 << 26) 34462306a36Sopenharmony_ci#define RXSTATUS_ALIGN (1 << 27) 34562306a36Sopenharmony_ci#define RXSTATUS_OVERRUN (1 << 28) 34662306a36Sopenharmony_ci#define RXSTATUS_NODESC (1 << 29) 34762306a36Sopenharmony_ci#define RXSTATUS_LAST (1 << 30) 34862306a36Sopenharmony_ci#define RXSTATUS_ERROR (1 << 31) 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci#define RXSTATUS_STATUS_ERROR \ 35162306a36Sopenharmony_ci (RXSTATUS_NODESC | RXSTATUS_OVERRUN | RXSTATUS_ALIGN | \ 35262306a36Sopenharmony_ci RXSTATUS_RANGE | RXSTATUS_LENGTH | RXSTATUS_SYMBOL | RXSTATUS_CRC) 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/* Receive Descriptor control word */ 35562306a36Sopenharmony_ci#define RXDESC_CONTROL_SIZE 0x000007FF 35662306a36Sopenharmony_ci#define RXDESC_CONTROL_INT (1 << 31) 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci/* Transmit Status information word */ 35962306a36Sopenharmony_ci#define TXSTATUS_COLLISIONS_GET(x) (((x) >> 21) & 0xF) 36062306a36Sopenharmony_ci#define TXSTATUS_DEFER (1 << 25) 36162306a36Sopenharmony_ci#define TXSTATUS_EXCESSDEFER (1 << 26) 36262306a36Sopenharmony_ci#define TXSTATUS_EXCESSCOLL (1 << 27) 36362306a36Sopenharmony_ci#define TXSTATUS_LATECOLL (1 << 28) 36462306a36Sopenharmony_ci#define TXSTATUS_UNDERRUN (1 << 29) 36562306a36Sopenharmony_ci#define TXSTATUS_NODESC (1 << 30) 36662306a36Sopenharmony_ci#define TXSTATUS_ERROR (1 << 31) 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* Transmit Descriptor control word */ 36962306a36Sopenharmony_ci#define TXDESC_CONTROL_SIZE 0x000007FF 37062306a36Sopenharmony_ci#define TXDESC_CONTROL_OVERRIDE (1 << 26) 37162306a36Sopenharmony_ci#define TXDESC_CONTROL_HUGE (1 << 27) 37262306a36Sopenharmony_ci#define TXDESC_CONTROL_PAD (1 << 28) 37362306a36Sopenharmony_ci#define TXDESC_CONTROL_CRC (1 << 29) 37462306a36Sopenharmony_ci#define TXDESC_CONTROL_LAST (1 << 30) 37562306a36Sopenharmony_ci#define TXDESC_CONTROL_INT (1 << 31) 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci * Structure of a TX/RX descriptors and RX status 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistruct txrx_desc_t { 38162306a36Sopenharmony_ci __le32 packet; 38262306a36Sopenharmony_ci __le32 control; 38362306a36Sopenharmony_ci}; 38462306a36Sopenharmony_cistruct rx_status_t { 38562306a36Sopenharmony_ci __le32 statusinfo; 38662306a36Sopenharmony_ci __le32 statushashcrc; 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* 39062306a36Sopenharmony_ci * Device driver data structure 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_cistruct netdata_local { 39362306a36Sopenharmony_ci struct platform_device *pdev; 39462306a36Sopenharmony_ci struct net_device *ndev; 39562306a36Sopenharmony_ci struct device_node *phy_node; 39662306a36Sopenharmony_ci spinlock_t lock; 39762306a36Sopenharmony_ci void __iomem *net_base; 39862306a36Sopenharmony_ci u32 msg_enable; 39962306a36Sopenharmony_ci unsigned int skblen[ENET_TX_DESC]; 40062306a36Sopenharmony_ci unsigned int last_tx_idx; 40162306a36Sopenharmony_ci unsigned int num_used_tx_buffs; 40262306a36Sopenharmony_ci struct mii_bus *mii_bus; 40362306a36Sopenharmony_ci struct clk *clk; 40462306a36Sopenharmony_ci dma_addr_t dma_buff_base_p; 40562306a36Sopenharmony_ci void *dma_buff_base_v; 40662306a36Sopenharmony_ci size_t dma_buff_size; 40762306a36Sopenharmony_ci struct txrx_desc_t *tx_desc_v; 40862306a36Sopenharmony_ci u32 *tx_stat_v; 40962306a36Sopenharmony_ci void *tx_buff_v; 41062306a36Sopenharmony_ci struct txrx_desc_t *rx_desc_v; 41162306a36Sopenharmony_ci struct rx_status_t *rx_stat_v; 41262306a36Sopenharmony_ci void *rx_buff_v; 41362306a36Sopenharmony_ci int link; 41462306a36Sopenharmony_ci int speed; 41562306a36Sopenharmony_ci int duplex; 41662306a36Sopenharmony_ci struct napi_struct napi; 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/* 42062306a36Sopenharmony_ci * MAC support functions 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_cistatic void __lpc_set_mac(struct netdata_local *pldat, const u8 *mac) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci u32 tmp; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Set station address */ 42762306a36Sopenharmony_ci tmp = mac[0] | ((u32)mac[1] << 8); 42862306a36Sopenharmony_ci writel(tmp, LPC_ENET_SA2(pldat->net_base)); 42962306a36Sopenharmony_ci tmp = mac[2] | ((u32)mac[3] << 8); 43062306a36Sopenharmony_ci writel(tmp, LPC_ENET_SA1(pldat->net_base)); 43162306a36Sopenharmony_ci tmp = mac[4] | ((u32)mac[5] << 8); 43262306a36Sopenharmony_ci writel(tmp, LPC_ENET_SA0(pldat->net_base)); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci netdev_dbg(pldat->ndev, "Ethernet MAC address %pM\n", mac); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void __lpc_get_mac(struct netdata_local *pldat, u8 *mac) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci u32 tmp; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* Get station address */ 44262306a36Sopenharmony_ci tmp = readl(LPC_ENET_SA2(pldat->net_base)); 44362306a36Sopenharmony_ci mac[0] = tmp & 0xFF; 44462306a36Sopenharmony_ci mac[1] = tmp >> 8; 44562306a36Sopenharmony_ci tmp = readl(LPC_ENET_SA1(pldat->net_base)); 44662306a36Sopenharmony_ci mac[2] = tmp & 0xFF; 44762306a36Sopenharmony_ci mac[3] = tmp >> 8; 44862306a36Sopenharmony_ci tmp = readl(LPC_ENET_SA0(pldat->net_base)); 44962306a36Sopenharmony_ci mac[4] = tmp & 0xFF; 45062306a36Sopenharmony_ci mac[5] = tmp >> 8; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void __lpc_params_setup(struct netdata_local *pldat) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci u32 tmp; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (pldat->duplex == DUPLEX_FULL) { 45862306a36Sopenharmony_ci tmp = readl(LPC_ENET_MAC2(pldat->net_base)); 45962306a36Sopenharmony_ci tmp |= LPC_MAC2_FULL_DUPLEX; 46062306a36Sopenharmony_ci writel(tmp, LPC_ENET_MAC2(pldat->net_base)); 46162306a36Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 46262306a36Sopenharmony_ci tmp |= LPC_COMMAND_FULLDUPLEX; 46362306a36Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 46462306a36Sopenharmony_ci writel(LPC_IPGT_LOAD(0x15), LPC_ENET_IPGT(pldat->net_base)); 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci tmp = readl(LPC_ENET_MAC2(pldat->net_base)); 46762306a36Sopenharmony_ci tmp &= ~LPC_MAC2_FULL_DUPLEX; 46862306a36Sopenharmony_ci writel(tmp, LPC_ENET_MAC2(pldat->net_base)); 46962306a36Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 47062306a36Sopenharmony_ci tmp &= ~LPC_COMMAND_FULLDUPLEX; 47162306a36Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 47262306a36Sopenharmony_ci writel(LPC_IPGT_LOAD(0x12), LPC_ENET_IPGT(pldat->net_base)); 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (pldat->speed == SPEED_100) 47662306a36Sopenharmony_ci writel(LPC_SUPP_SPEED, LPC_ENET_SUPP(pldat->net_base)); 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci writel(0, LPC_ENET_SUPP(pldat->net_base)); 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void __lpc_eth_reset(struct netdata_local *pldat) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci /* Reset all MAC logic */ 48462306a36Sopenharmony_ci writel((LPC_MAC1_RESET_TX | LPC_MAC1_RESET_MCS_TX | LPC_MAC1_RESET_RX | 48562306a36Sopenharmony_ci LPC_MAC1_RESET_MCS_RX | LPC_MAC1_SIMULATION_RESET | 48662306a36Sopenharmony_ci LPC_MAC1_SOFT_RESET), LPC_ENET_MAC1(pldat->net_base)); 48762306a36Sopenharmony_ci writel((LPC_COMMAND_REG_RESET | LPC_COMMAND_TXRESET | 48862306a36Sopenharmony_ci LPC_COMMAND_RXRESET), LPC_ENET_COMMAND(pldat->net_base)); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int __lpc_mii_mngt_reset(struct netdata_local *pldat) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci /* Reset MII management hardware */ 49462306a36Sopenharmony_ci writel(LPC_MCFG_RESET_MII_MGMT, LPC_ENET_MCFG(pldat->net_base)); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* Setup MII clock to slowest rate with a /28 divider */ 49762306a36Sopenharmony_ci writel(LPC_MCFG_CLOCK_SELECT(LPC_MCFG_CLOCK_HOST_DIV_28), 49862306a36Sopenharmony_ci LPC_ENET_MCFG(pldat->net_base)); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic inline phys_addr_t __va_to_pa(void *addr, struct netdata_local *pldat) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci phys_addr_t phaddr; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci phaddr = addr - pldat->dma_buff_base_v; 50862306a36Sopenharmony_ci phaddr += pldat->dma_buff_base_p; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return phaddr; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void lpc_eth_enable_int(void __iomem *regbase) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci writel((LPC_MACINT_RXDONEINTEN | LPC_MACINT_TXDONEINTEN), 51662306a36Sopenharmony_ci LPC_ENET_INTENABLE(regbase)); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void lpc_eth_disable_int(void __iomem *regbase) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci writel(0, LPC_ENET_INTENABLE(regbase)); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/* Setup TX/RX descriptors */ 52562306a36Sopenharmony_cistatic void __lpc_txrx_desc_setup(struct netdata_local *pldat) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci u32 *ptxstat; 52862306a36Sopenharmony_ci void *tbuff; 52962306a36Sopenharmony_ci int i; 53062306a36Sopenharmony_ci struct txrx_desc_t *ptxrxdesc; 53162306a36Sopenharmony_ci struct rx_status_t *prxstat; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci tbuff = PTR_ALIGN(pldat->dma_buff_base_v, 16); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Setup TX descriptors, status, and buffers */ 53662306a36Sopenharmony_ci pldat->tx_desc_v = tbuff; 53762306a36Sopenharmony_ci tbuff += sizeof(struct txrx_desc_t) * ENET_TX_DESC; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pldat->tx_stat_v = tbuff; 54062306a36Sopenharmony_ci tbuff += sizeof(u32) * ENET_TX_DESC; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci tbuff = PTR_ALIGN(tbuff, 16); 54362306a36Sopenharmony_ci pldat->tx_buff_v = tbuff; 54462306a36Sopenharmony_ci tbuff += ENET_MAXF_SIZE * ENET_TX_DESC; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci /* Setup RX descriptors, status, and buffers */ 54762306a36Sopenharmony_ci pldat->rx_desc_v = tbuff; 54862306a36Sopenharmony_ci tbuff += sizeof(struct txrx_desc_t) * ENET_RX_DESC; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci tbuff = PTR_ALIGN(tbuff, 16); 55162306a36Sopenharmony_ci pldat->rx_stat_v = tbuff; 55262306a36Sopenharmony_ci tbuff += sizeof(struct rx_status_t) * ENET_RX_DESC; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci tbuff = PTR_ALIGN(tbuff, 16); 55562306a36Sopenharmony_ci pldat->rx_buff_v = tbuff; 55662306a36Sopenharmony_ci tbuff += ENET_MAXF_SIZE * ENET_RX_DESC; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* Map the TX descriptors to the TX buffers in hardware */ 55962306a36Sopenharmony_ci for (i = 0; i < ENET_TX_DESC; i++) { 56062306a36Sopenharmony_ci ptxstat = &pldat->tx_stat_v[i]; 56162306a36Sopenharmony_ci ptxrxdesc = &pldat->tx_desc_v[i]; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ptxrxdesc->packet = __va_to_pa( 56462306a36Sopenharmony_ci pldat->tx_buff_v + i * ENET_MAXF_SIZE, pldat); 56562306a36Sopenharmony_ci ptxrxdesc->control = 0; 56662306a36Sopenharmony_ci *ptxstat = 0; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Map the RX descriptors to the RX buffers in hardware */ 57062306a36Sopenharmony_ci for (i = 0; i < ENET_RX_DESC; i++) { 57162306a36Sopenharmony_ci prxstat = &pldat->rx_stat_v[i]; 57262306a36Sopenharmony_ci ptxrxdesc = &pldat->rx_desc_v[i]; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci ptxrxdesc->packet = __va_to_pa( 57562306a36Sopenharmony_ci pldat->rx_buff_v + i * ENET_MAXF_SIZE, pldat); 57662306a36Sopenharmony_ci ptxrxdesc->control = RXDESC_CONTROL_INT | (ENET_MAXF_SIZE - 1); 57762306a36Sopenharmony_ci prxstat->statusinfo = 0; 57862306a36Sopenharmony_ci prxstat->statushashcrc = 0; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Setup base addresses in hardware to point to buffers and 58262306a36Sopenharmony_ci * descriptors 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci writel((ENET_TX_DESC - 1), 58562306a36Sopenharmony_ci LPC_ENET_TXDESCRIPTORNUMBER(pldat->net_base)); 58662306a36Sopenharmony_ci writel(__va_to_pa(pldat->tx_desc_v, pldat), 58762306a36Sopenharmony_ci LPC_ENET_TXDESCRIPTOR(pldat->net_base)); 58862306a36Sopenharmony_ci writel(__va_to_pa(pldat->tx_stat_v, pldat), 58962306a36Sopenharmony_ci LPC_ENET_TXSTATUS(pldat->net_base)); 59062306a36Sopenharmony_ci writel((ENET_RX_DESC - 1), 59162306a36Sopenharmony_ci LPC_ENET_RXDESCRIPTORNUMBER(pldat->net_base)); 59262306a36Sopenharmony_ci writel(__va_to_pa(pldat->rx_desc_v, pldat), 59362306a36Sopenharmony_ci LPC_ENET_RXDESCRIPTOR(pldat->net_base)); 59462306a36Sopenharmony_ci writel(__va_to_pa(pldat->rx_stat_v, pldat), 59562306a36Sopenharmony_ci LPC_ENET_RXSTATUS(pldat->net_base)); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic void __lpc_eth_init(struct netdata_local *pldat) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci u32 tmp; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* Disable controller and reset */ 60362306a36Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 60462306a36Sopenharmony_ci tmp &= ~LPC_COMMAND_RXENABLE | LPC_COMMAND_TXENABLE; 60562306a36Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 60662306a36Sopenharmony_ci tmp = readl(LPC_ENET_MAC1(pldat->net_base)); 60762306a36Sopenharmony_ci tmp &= ~LPC_MAC1_RECV_ENABLE; 60862306a36Sopenharmony_ci writel(tmp, LPC_ENET_MAC1(pldat->net_base)); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* Initial MAC setup */ 61162306a36Sopenharmony_ci writel(LPC_MAC1_PASS_ALL_RX_FRAMES, LPC_ENET_MAC1(pldat->net_base)); 61262306a36Sopenharmony_ci writel((LPC_MAC2_PAD_CRC_ENABLE | LPC_MAC2_CRC_ENABLE), 61362306a36Sopenharmony_ci LPC_ENET_MAC2(pldat->net_base)); 61462306a36Sopenharmony_ci writel(ENET_MAXF_SIZE, LPC_ENET_MAXF(pldat->net_base)); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* Collision window, gap */ 61762306a36Sopenharmony_ci writel((LPC_CLRT_LOAD_RETRY_MAX(0xF) | 61862306a36Sopenharmony_ci LPC_CLRT_LOAD_COLLISION_WINDOW(0x37)), 61962306a36Sopenharmony_ci LPC_ENET_CLRT(pldat->net_base)); 62062306a36Sopenharmony_ci writel(LPC_IPGR_LOAD_PART2(0x12), LPC_ENET_IPGR(pldat->net_base)); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII) 62362306a36Sopenharmony_ci writel(LPC_COMMAND_PASSRUNTFRAME, 62462306a36Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 62562306a36Sopenharmony_ci else { 62662306a36Sopenharmony_ci writel((LPC_COMMAND_PASSRUNTFRAME | LPC_COMMAND_RMII), 62762306a36Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 62862306a36Sopenharmony_ci writel(LPC_SUPP_RESET_RMII, LPC_ENET_SUPP(pldat->net_base)); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci __lpc_params_setup(pldat); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Setup TX and RX descriptors */ 63462306a36Sopenharmony_ci __lpc_txrx_desc_setup(pldat); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Setup packet filtering */ 63762306a36Sopenharmony_ci writel((LPC_RXFLTRW_ACCEPTUBROADCAST | LPC_RXFLTRW_ACCEPTPERFECT), 63862306a36Sopenharmony_ci LPC_ENET_RXFILTER_CTRL(pldat->net_base)); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Get the next TX buffer output index */ 64162306a36Sopenharmony_ci pldat->num_used_tx_buffs = 0; 64262306a36Sopenharmony_ci pldat->last_tx_idx = 64362306a36Sopenharmony_ci readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Clear and enable interrupts */ 64662306a36Sopenharmony_ci writel(0xFFFF, LPC_ENET_INTCLEAR(pldat->net_base)); 64762306a36Sopenharmony_ci smp_wmb(); 64862306a36Sopenharmony_ci lpc_eth_enable_int(pldat->net_base); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* Enable controller */ 65162306a36Sopenharmony_ci tmp = readl(LPC_ENET_COMMAND(pldat->net_base)); 65262306a36Sopenharmony_ci tmp |= LPC_COMMAND_RXENABLE | LPC_COMMAND_TXENABLE; 65362306a36Sopenharmony_ci writel(tmp, LPC_ENET_COMMAND(pldat->net_base)); 65462306a36Sopenharmony_ci tmp = readl(LPC_ENET_MAC1(pldat->net_base)); 65562306a36Sopenharmony_ci tmp |= LPC_MAC1_RECV_ENABLE; 65662306a36Sopenharmony_ci writel(tmp, LPC_ENET_MAC1(pldat->net_base)); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void __lpc_eth_shutdown(struct netdata_local *pldat) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci /* Reset ethernet and power down PHY */ 66262306a36Sopenharmony_ci __lpc_eth_reset(pldat); 66362306a36Sopenharmony_ci writel(0, LPC_ENET_MAC1(pldat->net_base)); 66462306a36Sopenharmony_ci writel(0, LPC_ENET_MAC2(pldat->net_base)); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci/* 66862306a36Sopenharmony_ci * MAC<--->PHY support functions 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_cistatic int lpc_mdio_read(struct mii_bus *bus, int phy_id, int phyreg) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct netdata_local *pldat = bus->priv; 67362306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(100); 67462306a36Sopenharmony_ci int lps; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci writel(((phy_id << 8) | phyreg), LPC_ENET_MADR(pldat->net_base)); 67762306a36Sopenharmony_ci writel(LPC_MCMD_READ, LPC_ENET_MCMD(pldat->net_base)); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Wait for unbusy status */ 68062306a36Sopenharmony_ci while (readl(LPC_ENET_MIND(pldat->net_base)) & LPC_MIND_BUSY) { 68162306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 68262306a36Sopenharmony_ci return -EIO; 68362306a36Sopenharmony_ci cpu_relax(); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci lps = readl(LPC_ENET_MRDD(pldat->net_base)); 68762306a36Sopenharmony_ci writel(0, LPC_ENET_MCMD(pldat->net_base)); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return lps; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int lpc_mdio_write(struct mii_bus *bus, int phy_id, int phyreg, 69362306a36Sopenharmony_ci u16 phydata) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct netdata_local *pldat = bus->priv; 69662306a36Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(100); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci writel(((phy_id << 8) | phyreg), LPC_ENET_MADR(pldat->net_base)); 69962306a36Sopenharmony_ci writel(phydata, LPC_ENET_MWTD(pldat->net_base)); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* Wait for completion */ 70262306a36Sopenharmony_ci while (readl(LPC_ENET_MIND(pldat->net_base)) & LPC_MIND_BUSY) { 70362306a36Sopenharmony_ci if (time_after(jiffies, timeout)) 70462306a36Sopenharmony_ci return -EIO; 70562306a36Sopenharmony_ci cpu_relax(); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic int lpc_mdio_reset(struct mii_bus *bus) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci return __lpc_mii_mngt_reset((struct netdata_local *)bus->priv); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void lpc_handle_link_change(struct net_device *ndev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 71962306a36Sopenharmony_ci struct phy_device *phydev = ndev->phydev; 72062306a36Sopenharmony_ci unsigned long flags; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci bool status_change = false; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (phydev->link) { 72762306a36Sopenharmony_ci if ((pldat->speed != phydev->speed) || 72862306a36Sopenharmony_ci (pldat->duplex != phydev->duplex)) { 72962306a36Sopenharmony_ci pldat->speed = phydev->speed; 73062306a36Sopenharmony_ci pldat->duplex = phydev->duplex; 73162306a36Sopenharmony_ci status_change = true; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (phydev->link != pldat->link) { 73662306a36Sopenharmony_ci if (!phydev->link) { 73762306a36Sopenharmony_ci pldat->speed = 0; 73862306a36Sopenharmony_ci pldat->duplex = -1; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci pldat->link = phydev->link; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci status_change = true; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (status_change) 74862306a36Sopenharmony_ci __lpc_params_setup(pldat); 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic int lpc_mii_probe(struct net_device *ndev) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 75462306a36Sopenharmony_ci struct phy_device *phydev; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Attach to the PHY */ 75762306a36Sopenharmony_ci if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII) 75862306a36Sopenharmony_ci netdev_info(ndev, "using MII interface\n"); 75962306a36Sopenharmony_ci else 76062306a36Sopenharmony_ci netdev_info(ndev, "using RMII interface\n"); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (pldat->phy_node) 76362306a36Sopenharmony_ci phydev = of_phy_find_device(pldat->phy_node); 76462306a36Sopenharmony_ci else 76562306a36Sopenharmony_ci phydev = phy_find_first(pldat->mii_bus); 76662306a36Sopenharmony_ci if (!phydev) { 76762306a36Sopenharmony_ci netdev_err(ndev, "no PHY found\n"); 76862306a36Sopenharmony_ci return -ENODEV; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci phydev = phy_connect(ndev, phydev_name(phydev), 77262306a36Sopenharmony_ci &lpc_handle_link_change, 77362306a36Sopenharmony_ci lpc_phy_interface_mode(&pldat->pdev->dev)); 77462306a36Sopenharmony_ci if (IS_ERR(phydev)) { 77562306a36Sopenharmony_ci netdev_err(ndev, "Could not attach to PHY\n"); 77662306a36Sopenharmony_ci return PTR_ERR(phydev); 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci phy_set_max_speed(phydev, SPEED_100); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci pldat->link = 0; 78262306a36Sopenharmony_ci pldat->speed = 0; 78362306a36Sopenharmony_ci pldat->duplex = -1; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci phy_attached_info(phydev); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic int lpc_mii_init(struct netdata_local *pldat) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci struct device_node *node; 79362306a36Sopenharmony_ci int err = -ENXIO; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci pldat->mii_bus = mdiobus_alloc(); 79662306a36Sopenharmony_ci if (!pldat->mii_bus) { 79762306a36Sopenharmony_ci err = -ENOMEM; 79862306a36Sopenharmony_ci goto err_out; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Setup MII mode */ 80262306a36Sopenharmony_ci if (lpc_phy_interface_mode(&pldat->pdev->dev) == PHY_INTERFACE_MODE_MII) 80362306a36Sopenharmony_ci writel(LPC_COMMAND_PASSRUNTFRAME, 80462306a36Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 80562306a36Sopenharmony_ci else { 80662306a36Sopenharmony_ci writel((LPC_COMMAND_PASSRUNTFRAME | LPC_COMMAND_RMII), 80762306a36Sopenharmony_ci LPC_ENET_COMMAND(pldat->net_base)); 80862306a36Sopenharmony_ci writel(LPC_SUPP_RESET_RMII, LPC_ENET_SUPP(pldat->net_base)); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci pldat->mii_bus->name = "lpc_mii_bus"; 81262306a36Sopenharmony_ci pldat->mii_bus->read = &lpc_mdio_read; 81362306a36Sopenharmony_ci pldat->mii_bus->write = &lpc_mdio_write; 81462306a36Sopenharmony_ci pldat->mii_bus->reset = &lpc_mdio_reset; 81562306a36Sopenharmony_ci snprintf(pldat->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", 81662306a36Sopenharmony_ci pldat->pdev->name, pldat->pdev->id); 81762306a36Sopenharmony_ci pldat->mii_bus->priv = pldat; 81862306a36Sopenharmony_ci pldat->mii_bus->parent = &pldat->pdev->dev; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci node = of_get_child_by_name(pldat->pdev->dev.of_node, "mdio"); 82162306a36Sopenharmony_ci err = of_mdiobus_register(pldat->mii_bus, node); 82262306a36Sopenharmony_ci of_node_put(node); 82362306a36Sopenharmony_ci if (err) 82462306a36Sopenharmony_ci goto err_out_unregister_bus; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci err = lpc_mii_probe(pldat->ndev); 82762306a36Sopenharmony_ci if (err) 82862306a36Sopenharmony_ci goto err_out_unregister_bus; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cierr_out_unregister_bus: 83362306a36Sopenharmony_ci mdiobus_unregister(pldat->mii_bus); 83462306a36Sopenharmony_ci mdiobus_free(pldat->mii_bus); 83562306a36Sopenharmony_cierr_out: 83662306a36Sopenharmony_ci return err; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic void __lpc_handle_xmit(struct net_device *ndev) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 84262306a36Sopenharmony_ci u32 txcidx, *ptxstat, txstat; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci txcidx = readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); 84562306a36Sopenharmony_ci while (pldat->last_tx_idx != txcidx) { 84662306a36Sopenharmony_ci unsigned int skblen = pldat->skblen[pldat->last_tx_idx]; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci /* A buffer is available, get buffer status */ 84962306a36Sopenharmony_ci ptxstat = &pldat->tx_stat_v[pldat->last_tx_idx]; 85062306a36Sopenharmony_ci txstat = *ptxstat; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Next buffer and decrement used buffer counter */ 85362306a36Sopenharmony_ci pldat->num_used_tx_buffs--; 85462306a36Sopenharmony_ci pldat->last_tx_idx++; 85562306a36Sopenharmony_ci if (pldat->last_tx_idx >= ENET_TX_DESC) 85662306a36Sopenharmony_ci pldat->last_tx_idx = 0; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* Update collision counter */ 85962306a36Sopenharmony_ci ndev->stats.collisions += TXSTATUS_COLLISIONS_GET(txstat); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* Any errors occurred? */ 86262306a36Sopenharmony_ci if (txstat & TXSTATUS_ERROR) { 86362306a36Sopenharmony_ci if (txstat & TXSTATUS_UNDERRUN) { 86462306a36Sopenharmony_ci /* FIFO underrun */ 86562306a36Sopenharmony_ci ndev->stats.tx_fifo_errors++; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci if (txstat & TXSTATUS_LATECOLL) { 86862306a36Sopenharmony_ci /* Late collision */ 86962306a36Sopenharmony_ci ndev->stats.tx_aborted_errors++; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci if (txstat & TXSTATUS_EXCESSCOLL) { 87262306a36Sopenharmony_ci /* Excessive collision */ 87362306a36Sopenharmony_ci ndev->stats.tx_aborted_errors++; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci if (txstat & TXSTATUS_EXCESSDEFER) { 87662306a36Sopenharmony_ci /* Defer limit */ 87762306a36Sopenharmony_ci ndev->stats.tx_aborted_errors++; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci ndev->stats.tx_errors++; 88062306a36Sopenharmony_ci } else { 88162306a36Sopenharmony_ci /* Update stats */ 88262306a36Sopenharmony_ci ndev->stats.tx_packets++; 88362306a36Sopenharmony_ci ndev->stats.tx_bytes += skblen; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci txcidx = readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (pldat->num_used_tx_buffs <= ENET_TX_DESC/2) { 89062306a36Sopenharmony_ci if (netif_queue_stopped(ndev)) 89162306a36Sopenharmony_ci netif_wake_queue(ndev); 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic int __lpc_handle_recv(struct net_device *ndev, int budget) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 89862306a36Sopenharmony_ci struct sk_buff *skb; 89962306a36Sopenharmony_ci u32 rxconsidx, len, ethst; 90062306a36Sopenharmony_ci struct rx_status_t *prxstat; 90162306a36Sopenharmony_ci int rx_done = 0; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* Get the current RX buffer indexes */ 90462306a36Sopenharmony_ci rxconsidx = readl(LPC_ENET_RXCONSUMEINDEX(pldat->net_base)); 90562306a36Sopenharmony_ci while (rx_done < budget && rxconsidx != 90662306a36Sopenharmony_ci readl(LPC_ENET_RXPRODUCEINDEX(pldat->net_base))) { 90762306a36Sopenharmony_ci /* Get pointer to receive status */ 90862306a36Sopenharmony_ci prxstat = &pldat->rx_stat_v[rxconsidx]; 90962306a36Sopenharmony_ci len = (prxstat->statusinfo & RXSTATUS_SIZE) + 1; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci /* Status error? */ 91262306a36Sopenharmony_ci ethst = prxstat->statusinfo; 91362306a36Sopenharmony_ci if ((ethst & (RXSTATUS_ERROR | RXSTATUS_STATUS_ERROR)) == 91462306a36Sopenharmony_ci (RXSTATUS_ERROR | RXSTATUS_RANGE)) 91562306a36Sopenharmony_ci ethst &= ~RXSTATUS_ERROR; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (ethst & RXSTATUS_ERROR) { 91862306a36Sopenharmony_ci int si = prxstat->statusinfo; 91962306a36Sopenharmony_ci /* Check statuses */ 92062306a36Sopenharmony_ci if (si & RXSTATUS_OVERRUN) { 92162306a36Sopenharmony_ci /* Overrun error */ 92262306a36Sopenharmony_ci ndev->stats.rx_fifo_errors++; 92362306a36Sopenharmony_ci } else if (si & RXSTATUS_CRC) { 92462306a36Sopenharmony_ci /* CRC error */ 92562306a36Sopenharmony_ci ndev->stats.rx_crc_errors++; 92662306a36Sopenharmony_ci } else if (si & RXSTATUS_LENGTH) { 92762306a36Sopenharmony_ci /* Length error */ 92862306a36Sopenharmony_ci ndev->stats.rx_length_errors++; 92962306a36Sopenharmony_ci } else if (si & RXSTATUS_ERROR) { 93062306a36Sopenharmony_ci /* Other error */ 93162306a36Sopenharmony_ci ndev->stats.rx_length_errors++; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci ndev->stats.rx_errors++; 93462306a36Sopenharmony_ci } else { 93562306a36Sopenharmony_ci /* Packet is good */ 93662306a36Sopenharmony_ci skb = dev_alloc_skb(len); 93762306a36Sopenharmony_ci if (!skb) { 93862306a36Sopenharmony_ci ndev->stats.rx_dropped++; 93962306a36Sopenharmony_ci } else { 94062306a36Sopenharmony_ci /* Copy packet from buffer */ 94162306a36Sopenharmony_ci skb_put_data(skb, 94262306a36Sopenharmony_ci pldat->rx_buff_v + rxconsidx * ENET_MAXF_SIZE, 94362306a36Sopenharmony_ci len); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* Pass to upper layer */ 94662306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 94762306a36Sopenharmony_ci netif_receive_skb(skb); 94862306a36Sopenharmony_ci ndev->stats.rx_packets++; 94962306a36Sopenharmony_ci ndev->stats.rx_bytes += len; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* Increment consume index */ 95462306a36Sopenharmony_ci rxconsidx = rxconsidx + 1; 95562306a36Sopenharmony_ci if (rxconsidx >= ENET_RX_DESC) 95662306a36Sopenharmony_ci rxconsidx = 0; 95762306a36Sopenharmony_ci writel(rxconsidx, 95862306a36Sopenharmony_ci LPC_ENET_RXCONSUMEINDEX(pldat->net_base)); 95962306a36Sopenharmony_ci rx_done++; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return rx_done; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int lpc_eth_poll(struct napi_struct *napi, int budget) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct netdata_local *pldat = container_of(napi, 96862306a36Sopenharmony_ci struct netdata_local, napi); 96962306a36Sopenharmony_ci struct net_device *ndev = pldat->ndev; 97062306a36Sopenharmony_ci int rx_done = 0; 97162306a36Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(ndev, 0); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci __netif_tx_lock(txq, smp_processor_id()); 97462306a36Sopenharmony_ci __lpc_handle_xmit(ndev); 97562306a36Sopenharmony_ci __netif_tx_unlock(txq); 97662306a36Sopenharmony_ci rx_done = __lpc_handle_recv(ndev, budget); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (rx_done < budget) { 97962306a36Sopenharmony_ci napi_complete_done(napi, rx_done); 98062306a36Sopenharmony_ci lpc_eth_enable_int(pldat->net_base); 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci return rx_done; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic irqreturn_t __lpc_eth_interrupt(int irq, void *dev_id) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct net_device *ndev = dev_id; 98962306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 99062306a36Sopenharmony_ci u32 tmp; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci spin_lock(&pldat->lock); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci tmp = readl(LPC_ENET_INTSTATUS(pldat->net_base)); 99562306a36Sopenharmony_ci /* Clear interrupts */ 99662306a36Sopenharmony_ci writel(tmp, LPC_ENET_INTCLEAR(pldat->net_base)); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci lpc_eth_disable_int(pldat->net_base); 99962306a36Sopenharmony_ci if (likely(napi_schedule_prep(&pldat->napi))) 100062306a36Sopenharmony_ci __napi_schedule(&pldat->napi); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci spin_unlock(&pldat->lock); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci return IRQ_HANDLED; 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic int lpc_eth_close(struct net_device *ndev) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci unsigned long flags; 101062306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (netif_msg_ifdown(pldat)) 101362306a36Sopenharmony_ci dev_dbg(&pldat->pdev->dev, "shutting down %s\n", ndev->name); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci napi_disable(&pldat->napi); 101662306a36Sopenharmony_ci netif_stop_queue(ndev); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 101962306a36Sopenharmony_ci __lpc_eth_reset(pldat); 102062306a36Sopenharmony_ci netif_carrier_off(ndev); 102162306a36Sopenharmony_ci writel(0, LPC_ENET_MAC1(pldat->net_base)); 102262306a36Sopenharmony_ci writel(0, LPC_ENET_MAC2(pldat->net_base)); 102362306a36Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (ndev->phydev) 102662306a36Sopenharmony_ci phy_stop(ndev->phydev); 102762306a36Sopenharmony_ci clk_disable_unprepare(pldat->clk); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return 0; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic netdev_tx_t lpc_eth_hard_start_xmit(struct sk_buff *skb, 103362306a36Sopenharmony_ci struct net_device *ndev) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 103662306a36Sopenharmony_ci u32 len, txidx; 103762306a36Sopenharmony_ci u32 *ptxstat; 103862306a36Sopenharmony_ci struct txrx_desc_t *ptxrxdesc; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci len = skb->len; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci spin_lock_irq(&pldat->lock); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1)) { 104562306a36Sopenharmony_ci /* This function should never be called when there are no 104662306a36Sopenharmony_ci * buffers 104762306a36Sopenharmony_ci */ 104862306a36Sopenharmony_ci netif_stop_queue(ndev); 104962306a36Sopenharmony_ci spin_unlock_irq(&pldat->lock); 105062306a36Sopenharmony_ci WARN(1, "BUG! TX request when no free TX buffers!\n"); 105162306a36Sopenharmony_ci return NETDEV_TX_BUSY; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* Get the next TX descriptor index */ 105562306a36Sopenharmony_ci txidx = readl(LPC_ENET_TXPRODUCEINDEX(pldat->net_base)); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Setup control for the transfer */ 105862306a36Sopenharmony_ci ptxstat = &pldat->tx_stat_v[txidx]; 105962306a36Sopenharmony_ci *ptxstat = 0; 106062306a36Sopenharmony_ci ptxrxdesc = &pldat->tx_desc_v[txidx]; 106162306a36Sopenharmony_ci ptxrxdesc->control = 106262306a36Sopenharmony_ci (len - 1) | TXDESC_CONTROL_LAST | TXDESC_CONTROL_INT; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci /* Copy data to the DMA buffer */ 106562306a36Sopenharmony_ci memcpy(pldat->tx_buff_v + txidx * ENET_MAXF_SIZE, skb->data, len); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci /* Save the buffer and increment the buffer counter */ 106862306a36Sopenharmony_ci pldat->skblen[txidx] = len; 106962306a36Sopenharmony_ci pldat->num_used_tx_buffs++; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci /* Start transmit */ 107262306a36Sopenharmony_ci txidx++; 107362306a36Sopenharmony_ci if (txidx >= ENET_TX_DESC) 107462306a36Sopenharmony_ci txidx = 0; 107562306a36Sopenharmony_ci writel(txidx, LPC_ENET_TXPRODUCEINDEX(pldat->net_base)); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* Stop queue if no more TX buffers */ 107862306a36Sopenharmony_ci if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1)) 107962306a36Sopenharmony_ci netif_stop_queue(ndev); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci spin_unlock_irq(&pldat->lock); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci dev_kfree_skb(skb); 108462306a36Sopenharmony_ci return NETDEV_TX_OK; 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic int lpc_set_mac_address(struct net_device *ndev, void *p) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct sockaddr *addr = p; 109062306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 109162306a36Sopenharmony_ci unsigned long flags; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 109462306a36Sopenharmony_ci return -EADDRNOTAVAIL; 109562306a36Sopenharmony_ci eth_hw_addr_set(ndev, addr->sa_data); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* Set station address */ 110062306a36Sopenharmony_ci __lpc_set_mac(pldat, ndev->dev_addr); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci return 0; 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic void lpc_eth_set_multicast_list(struct net_device *ndev) 110862306a36Sopenharmony_ci{ 110962306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 111062306a36Sopenharmony_ci struct netdev_hw_addr_list *mcptr = &ndev->mc; 111162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 111262306a36Sopenharmony_ci u32 tmp32, hash_val, hashlo, hashhi; 111362306a36Sopenharmony_ci unsigned long flags; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci spin_lock_irqsave(&pldat->lock, flags); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci /* Set station address */ 111862306a36Sopenharmony_ci __lpc_set_mac(pldat, ndev->dev_addr); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci tmp32 = LPC_RXFLTRW_ACCEPTUBROADCAST | LPC_RXFLTRW_ACCEPTPERFECT; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (ndev->flags & IFF_PROMISC) 112362306a36Sopenharmony_ci tmp32 |= LPC_RXFLTRW_ACCEPTUNICAST | 112462306a36Sopenharmony_ci LPC_RXFLTRW_ACCEPTUMULTICAST; 112562306a36Sopenharmony_ci if (ndev->flags & IFF_ALLMULTI) 112662306a36Sopenharmony_ci tmp32 |= LPC_RXFLTRW_ACCEPTUMULTICAST; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (netdev_hw_addr_list_count(mcptr)) 112962306a36Sopenharmony_ci tmp32 |= LPC_RXFLTRW_ACCEPTUMULTICASTHASH; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci writel(tmp32, LPC_ENET_RXFILTER_CTRL(pldat->net_base)); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* Set initial hash table */ 113562306a36Sopenharmony_ci hashlo = 0x0; 113662306a36Sopenharmony_ci hashhi = 0x0; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* 64 bits : multicast address in hash table */ 113962306a36Sopenharmony_ci netdev_hw_addr_list_for_each(ha, mcptr) { 114062306a36Sopenharmony_ci hash_val = (ether_crc(6, ha->addr) >> 23) & 0x3F; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (hash_val >= 32) 114362306a36Sopenharmony_ci hashhi |= 1 << (hash_val - 32); 114462306a36Sopenharmony_ci else 114562306a36Sopenharmony_ci hashlo |= 1 << hash_val; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci writel(hashlo, LPC_ENET_HASHFILTERL(pldat->net_base)); 114962306a36Sopenharmony_ci writel(hashhi, LPC_ENET_HASHFILTERH(pldat->net_base)); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci spin_unlock_irqrestore(&pldat->lock, flags); 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic int lpc_eth_open(struct net_device *ndev) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 115762306a36Sopenharmony_ci int ret; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (netif_msg_ifup(pldat)) 116062306a36Sopenharmony_ci dev_dbg(&pldat->pdev->dev, "enabling %s\n", ndev->name); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci ret = clk_prepare_enable(pldat->clk); 116362306a36Sopenharmony_ci if (ret) 116462306a36Sopenharmony_ci return ret; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* Suspended PHY makes LPC ethernet core block, so resume now */ 116762306a36Sopenharmony_ci phy_resume(ndev->phydev); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Reset and initialize */ 117062306a36Sopenharmony_ci __lpc_eth_reset(pldat); 117162306a36Sopenharmony_ci __lpc_eth_init(pldat); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* schedule a link state check */ 117462306a36Sopenharmony_ci phy_start(ndev->phydev); 117562306a36Sopenharmony_ci netif_start_queue(ndev); 117662306a36Sopenharmony_ci napi_enable(&pldat->napi); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci return 0; 117962306a36Sopenharmony_ci} 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci/* 118262306a36Sopenharmony_ci * Ethtool ops 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_cistatic void lpc_eth_ethtool_getdrvinfo(struct net_device *ndev, 118562306a36Sopenharmony_ci struct ethtool_drvinfo *info) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci strscpy(info->driver, MODNAME, sizeof(info->driver)); 118862306a36Sopenharmony_ci strscpy(info->version, DRV_VERSION, sizeof(info->version)); 118962306a36Sopenharmony_ci strscpy(info->bus_info, dev_name(ndev->dev.parent), 119062306a36Sopenharmony_ci sizeof(info->bus_info)); 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic u32 lpc_eth_ethtool_getmsglevel(struct net_device *ndev) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return pldat->msg_enable; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic void lpc_eth_ethtool_setmsglevel(struct net_device *ndev, u32 level) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci pldat->msg_enable = level; 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic const struct ethtool_ops lpc_eth_ethtool_ops = { 120862306a36Sopenharmony_ci .get_drvinfo = lpc_eth_ethtool_getdrvinfo, 120962306a36Sopenharmony_ci .get_msglevel = lpc_eth_ethtool_getmsglevel, 121062306a36Sopenharmony_ci .set_msglevel = lpc_eth_ethtool_setmsglevel, 121162306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 121262306a36Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 121362306a36Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 121462306a36Sopenharmony_ci}; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic const struct net_device_ops lpc_netdev_ops = { 121762306a36Sopenharmony_ci .ndo_open = lpc_eth_open, 121862306a36Sopenharmony_ci .ndo_stop = lpc_eth_close, 121962306a36Sopenharmony_ci .ndo_start_xmit = lpc_eth_hard_start_xmit, 122062306a36Sopenharmony_ci .ndo_set_rx_mode = lpc_eth_set_multicast_list, 122162306a36Sopenharmony_ci .ndo_eth_ioctl = phy_do_ioctl_running, 122262306a36Sopenharmony_ci .ndo_set_mac_address = lpc_set_mac_address, 122362306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 122462306a36Sopenharmony_ci}; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic int lpc_eth_drv_probe(struct platform_device *pdev) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 122962306a36Sopenharmony_ci struct device_node *np = dev->of_node; 123062306a36Sopenharmony_ci struct netdata_local *pldat; 123162306a36Sopenharmony_ci struct net_device *ndev; 123262306a36Sopenharmony_ci dma_addr_t dma_handle; 123362306a36Sopenharmony_ci struct resource *res; 123462306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 123562306a36Sopenharmony_ci int irq, ret; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* Setup network interface for RMII or MII mode */ 123862306a36Sopenharmony_ci lpc32xx_set_phy_interface_mode(lpc_phy_interface_mode(dev)); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* Get platform resources */ 124162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 124262306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 124362306a36Sopenharmony_ci if (!res || irq < 0) { 124462306a36Sopenharmony_ci dev_err(dev, "error getting resources.\n"); 124562306a36Sopenharmony_ci ret = -ENXIO; 124662306a36Sopenharmony_ci goto err_exit; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* Allocate net driver data structure */ 125062306a36Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct netdata_local)); 125162306a36Sopenharmony_ci if (!ndev) { 125262306a36Sopenharmony_ci dev_err(dev, "could not allocate device.\n"); 125362306a36Sopenharmony_ci ret = -ENOMEM; 125462306a36Sopenharmony_ci goto err_exit; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, dev); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci pldat = netdev_priv(ndev); 126062306a36Sopenharmony_ci pldat->pdev = pdev; 126162306a36Sopenharmony_ci pldat->ndev = ndev; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci spin_lock_init(&pldat->lock); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* Save resources */ 126662306a36Sopenharmony_ci ndev->irq = irq; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci /* Get clock for the device */ 126962306a36Sopenharmony_ci pldat->clk = clk_get(dev, NULL); 127062306a36Sopenharmony_ci if (IS_ERR(pldat->clk)) { 127162306a36Sopenharmony_ci dev_err(dev, "error getting clock.\n"); 127262306a36Sopenharmony_ci ret = PTR_ERR(pldat->clk); 127362306a36Sopenharmony_ci goto err_out_free_dev; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci /* Enable network clock */ 127762306a36Sopenharmony_ci ret = clk_prepare_enable(pldat->clk); 127862306a36Sopenharmony_ci if (ret) 127962306a36Sopenharmony_ci goto err_out_clk_put; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci /* Map IO space */ 128262306a36Sopenharmony_ci pldat->net_base = ioremap(res->start, resource_size(res)); 128362306a36Sopenharmony_ci if (!pldat->net_base) { 128462306a36Sopenharmony_ci dev_err(dev, "failed to map registers\n"); 128562306a36Sopenharmony_ci ret = -ENOMEM; 128662306a36Sopenharmony_ci goto err_out_disable_clocks; 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci ret = request_irq(ndev->irq, __lpc_eth_interrupt, 0, 128962306a36Sopenharmony_ci ndev->name, ndev); 129062306a36Sopenharmony_ci if (ret) { 129162306a36Sopenharmony_ci dev_err(dev, "error requesting interrupt.\n"); 129262306a36Sopenharmony_ci goto err_out_iounmap; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci /* Setup driver functions */ 129662306a36Sopenharmony_ci ndev->netdev_ops = &lpc_netdev_ops; 129762306a36Sopenharmony_ci ndev->ethtool_ops = &lpc_eth_ethtool_ops; 129862306a36Sopenharmony_ci ndev->watchdog_timeo = msecs_to_jiffies(2500); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* Get size of DMA buffers/descriptors region */ 130162306a36Sopenharmony_ci pldat->dma_buff_size = (ENET_TX_DESC + ENET_RX_DESC) * (ENET_MAXF_SIZE + 130262306a36Sopenharmony_ci sizeof(struct txrx_desc_t) + sizeof(struct rx_status_t)); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (use_iram_for_net(dev)) { 130562306a36Sopenharmony_ci if (pldat->dma_buff_size > 130662306a36Sopenharmony_ci lpc32xx_return_iram(&pldat->dma_buff_base_v, &dma_handle)) { 130762306a36Sopenharmony_ci pldat->dma_buff_base_v = NULL; 130862306a36Sopenharmony_ci pldat->dma_buff_size = 0; 130962306a36Sopenharmony_ci netdev_err(ndev, 131062306a36Sopenharmony_ci "IRAM not big enough for net buffers, using SDRAM instead.\n"); 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (pldat->dma_buff_base_v == NULL) { 131562306a36Sopenharmony_ci ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); 131662306a36Sopenharmony_ci if (ret) 131762306a36Sopenharmony_ci goto err_out_free_irq; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci pldat->dma_buff_size = PAGE_ALIGN(pldat->dma_buff_size); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* Allocate a chunk of memory for the DMA ethernet buffers 132262306a36Sopenharmony_ci * and descriptors 132362306a36Sopenharmony_ci */ 132462306a36Sopenharmony_ci pldat->dma_buff_base_v = 132562306a36Sopenharmony_ci dma_alloc_coherent(dev, 132662306a36Sopenharmony_ci pldat->dma_buff_size, &dma_handle, 132762306a36Sopenharmony_ci GFP_KERNEL); 132862306a36Sopenharmony_ci if (pldat->dma_buff_base_v == NULL) { 132962306a36Sopenharmony_ci ret = -ENOMEM; 133062306a36Sopenharmony_ci goto err_out_free_irq; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci pldat->dma_buff_base_p = dma_handle; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci netdev_dbg(ndev, "IO address space :%pR\n", res); 133662306a36Sopenharmony_ci netdev_dbg(ndev, "IO address size :%zd\n", 133762306a36Sopenharmony_ci (size_t)resource_size(res)); 133862306a36Sopenharmony_ci netdev_dbg(ndev, "IO address (mapped) :0x%p\n", 133962306a36Sopenharmony_ci pldat->net_base); 134062306a36Sopenharmony_ci netdev_dbg(ndev, "IRQ number :%d\n", ndev->irq); 134162306a36Sopenharmony_ci netdev_dbg(ndev, "DMA buffer size :%zd\n", pldat->dma_buff_size); 134262306a36Sopenharmony_ci netdev_dbg(ndev, "DMA buffer P address :%pad\n", 134362306a36Sopenharmony_ci &pldat->dma_buff_base_p); 134462306a36Sopenharmony_ci netdev_dbg(ndev, "DMA buffer V address :0x%p\n", 134562306a36Sopenharmony_ci pldat->dma_buff_base_v); 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci pldat->phy_node = of_parse_phandle(np, "phy-handle", 0); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci /* Get MAC address from current HW setting (POR state is all zeros) */ 135062306a36Sopenharmony_ci __lpc_get_mac(pldat, addr); 135162306a36Sopenharmony_ci eth_hw_addr_set(ndev, addr); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (!is_valid_ether_addr(ndev->dev_addr)) { 135462306a36Sopenharmony_ci of_get_ethdev_address(np, ndev); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci if (!is_valid_ether_addr(ndev->dev_addr)) 135762306a36Sopenharmony_ci eth_hw_addr_random(ndev); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci /* then shut everything down to save power */ 136062306a36Sopenharmony_ci __lpc_eth_shutdown(pldat); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci /* Set default parameters */ 136362306a36Sopenharmony_ci pldat->msg_enable = NETIF_MSG_LINK; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci /* Force an MII interface reset and clock setup */ 136662306a36Sopenharmony_ci __lpc_mii_mngt_reset(pldat); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* Force default PHY interface setup in chip, this will probably be 136962306a36Sopenharmony_ci * changed by the PHY driver 137062306a36Sopenharmony_ci */ 137162306a36Sopenharmony_ci pldat->link = 0; 137262306a36Sopenharmony_ci pldat->speed = 100; 137362306a36Sopenharmony_ci pldat->duplex = DUPLEX_FULL; 137462306a36Sopenharmony_ci __lpc_params_setup(pldat); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci netif_napi_add_weight(ndev, &pldat->napi, lpc_eth_poll, NAPI_WEIGHT); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci ret = register_netdev(ndev); 137962306a36Sopenharmony_ci if (ret) { 138062306a36Sopenharmony_ci dev_err(dev, "Cannot register net device, aborting.\n"); 138162306a36Sopenharmony_ci goto err_out_dma_unmap; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci platform_set_drvdata(pdev, ndev); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci ret = lpc_mii_init(pldat); 138662306a36Sopenharmony_ci if (ret) 138762306a36Sopenharmony_ci goto err_out_unregister_netdev; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci netdev_info(ndev, "LPC mac at 0x%08lx irq %d\n", 139062306a36Sopenharmony_ci (unsigned long)res->start, ndev->irq); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci device_init_wakeup(dev, 1); 139362306a36Sopenharmony_ci device_set_wakeup_enable(dev, 0); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci return 0; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cierr_out_unregister_netdev: 139862306a36Sopenharmony_ci unregister_netdev(ndev); 139962306a36Sopenharmony_cierr_out_dma_unmap: 140062306a36Sopenharmony_ci if (!use_iram_for_net(dev) || 140162306a36Sopenharmony_ci pldat->dma_buff_size > lpc32xx_return_iram(NULL, NULL)) 140262306a36Sopenharmony_ci dma_free_coherent(dev, pldat->dma_buff_size, 140362306a36Sopenharmony_ci pldat->dma_buff_base_v, 140462306a36Sopenharmony_ci pldat->dma_buff_base_p); 140562306a36Sopenharmony_cierr_out_free_irq: 140662306a36Sopenharmony_ci free_irq(ndev->irq, ndev); 140762306a36Sopenharmony_cierr_out_iounmap: 140862306a36Sopenharmony_ci iounmap(pldat->net_base); 140962306a36Sopenharmony_cierr_out_disable_clocks: 141062306a36Sopenharmony_ci clk_disable_unprepare(pldat->clk); 141162306a36Sopenharmony_cierr_out_clk_put: 141262306a36Sopenharmony_ci clk_put(pldat->clk); 141362306a36Sopenharmony_cierr_out_free_dev: 141462306a36Sopenharmony_ci free_netdev(ndev); 141562306a36Sopenharmony_cierr_exit: 141662306a36Sopenharmony_ci pr_err("%s: not found (%d).\n", MODNAME, ret); 141762306a36Sopenharmony_ci return ret; 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_cistatic int lpc_eth_drv_remove(struct platform_device *pdev) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 142362306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci unregister_netdev(ndev); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci if (!use_iram_for_net(&pldat->pdev->dev) || 142862306a36Sopenharmony_ci pldat->dma_buff_size > lpc32xx_return_iram(NULL, NULL)) 142962306a36Sopenharmony_ci dma_free_coherent(&pldat->pdev->dev, pldat->dma_buff_size, 143062306a36Sopenharmony_ci pldat->dma_buff_base_v, 143162306a36Sopenharmony_ci pldat->dma_buff_base_p); 143262306a36Sopenharmony_ci free_irq(ndev->irq, ndev); 143362306a36Sopenharmony_ci iounmap(pldat->net_base); 143462306a36Sopenharmony_ci mdiobus_unregister(pldat->mii_bus); 143562306a36Sopenharmony_ci mdiobus_free(pldat->mii_bus); 143662306a36Sopenharmony_ci clk_disable_unprepare(pldat->clk); 143762306a36Sopenharmony_ci clk_put(pldat->clk); 143862306a36Sopenharmony_ci free_netdev(ndev); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci return 0; 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci#ifdef CONFIG_PM 144462306a36Sopenharmony_cistatic int lpc_eth_drv_suspend(struct platform_device *pdev, 144562306a36Sopenharmony_ci pm_message_t state) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 144862306a36Sopenharmony_ci struct netdata_local *pldat = netdev_priv(ndev); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 145162306a36Sopenharmony_ci enable_irq_wake(ndev->irq); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci if (ndev) { 145462306a36Sopenharmony_ci if (netif_running(ndev)) { 145562306a36Sopenharmony_ci netif_device_detach(ndev); 145662306a36Sopenharmony_ci __lpc_eth_shutdown(pldat); 145762306a36Sopenharmony_ci clk_disable_unprepare(pldat->clk); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci /* 146062306a36Sopenharmony_ci * Reset again now clock is disable to be sure 146162306a36Sopenharmony_ci * EMC_MDC is down 146262306a36Sopenharmony_ci */ 146362306a36Sopenharmony_ci __lpc_eth_reset(pldat); 146462306a36Sopenharmony_ci } 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci return 0; 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_cistatic int lpc_eth_drv_resume(struct platform_device *pdev) 147162306a36Sopenharmony_ci{ 147262306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 147362306a36Sopenharmony_ci struct netdata_local *pldat; 147462306a36Sopenharmony_ci int ret; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 147762306a36Sopenharmony_ci disable_irq_wake(ndev->irq); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (ndev) { 148062306a36Sopenharmony_ci if (netif_running(ndev)) { 148162306a36Sopenharmony_ci pldat = netdev_priv(ndev); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci /* Enable interface clock */ 148462306a36Sopenharmony_ci ret = clk_enable(pldat->clk); 148562306a36Sopenharmony_ci if (ret) 148662306a36Sopenharmony_ci return ret; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci /* Reset and initialize */ 148962306a36Sopenharmony_ci __lpc_eth_reset(pldat); 149062306a36Sopenharmony_ci __lpc_eth_init(pldat); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci netif_device_attach(ndev); 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci return 0; 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ci#endif 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic const struct of_device_id lpc_eth_match[] = { 150162306a36Sopenharmony_ci { .compatible = "nxp,lpc-eth" }, 150262306a36Sopenharmony_ci { } 150362306a36Sopenharmony_ci}; 150462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc_eth_match); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_cistatic struct platform_driver lpc_eth_driver = { 150762306a36Sopenharmony_ci .probe = lpc_eth_drv_probe, 150862306a36Sopenharmony_ci .remove = lpc_eth_drv_remove, 150962306a36Sopenharmony_ci#ifdef CONFIG_PM 151062306a36Sopenharmony_ci .suspend = lpc_eth_drv_suspend, 151162306a36Sopenharmony_ci .resume = lpc_eth_drv_resume, 151262306a36Sopenharmony_ci#endif 151362306a36Sopenharmony_ci .driver = { 151462306a36Sopenharmony_ci .name = MODNAME, 151562306a36Sopenharmony_ci .of_match_table = lpc_eth_match, 151662306a36Sopenharmony_ci }, 151762306a36Sopenharmony_ci}; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_cimodule_platform_driver(lpc_eth_driver); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ciMODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); 152262306a36Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 152362306a36Sopenharmony_ciMODULE_DESCRIPTION("LPC Ethernet Driver"); 152462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1525