2012-03-08 11:49:17 +00:00
/*
* drivers / net / ethernet / nxp / lpc_eth . c
*
* Author : Kevin Wells < kevin . wells @ nxp . com >
*
* Copyright ( C ) 2010 NXP Semiconductors
* Copyright ( C ) 2012 Roland Stigge < stigge @ antcom . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/clk.h>
2018-10-19 01:53:25 +03:00
# include <linux/crc32.h>
2012-03-08 11:49:17 +00:00
# include <linux/etherdevice.h>
2018-10-19 01:53:25 +03:00
# include <linux/module.h>
2012-03-08 11:49:17 +00:00
# include <linux/of_net.h>
2018-10-19 01:53:25 +03:00
# include <linux/phy.h>
# include <linux/platform_device.h>
# include <linux/spinlock.h>
2012-03-08 11:49:17 +00:00
# include <mach/board.h>
# include <mach/hardware.h>
2018-10-19 01:53:25 +03:00
# include <mach/platform.h>
2012-03-08 11:49:17 +00:00
# define MODNAME "lpc-eth"
# define DRV_VERSION "1.00"
# define ENET_MAXF_SIZE 1536
# define ENET_RX_DESC 48
# define ENET_TX_DESC 16
# define NAPI_WEIGHT 16
/*
* Ethernet MAC controller Register offsets
*/
# define LPC_ENET_MAC1(x) (x + 0x000)
# define LPC_ENET_MAC2(x) (x + 0x004)
# define LPC_ENET_IPGT(x) (x + 0x008)
# define LPC_ENET_IPGR(x) (x + 0x00C)
# define LPC_ENET_CLRT(x) (x + 0x010)
# define LPC_ENET_MAXF(x) (x + 0x014)
# define LPC_ENET_SUPP(x) (x + 0x018)
# define LPC_ENET_TEST(x) (x + 0x01C)
# define LPC_ENET_MCFG(x) (x + 0x020)
# define LPC_ENET_MCMD(x) (x + 0x024)
# define LPC_ENET_MADR(x) (x + 0x028)
# define LPC_ENET_MWTD(x) (x + 0x02C)
# define LPC_ENET_MRDD(x) (x + 0x030)
# define LPC_ENET_MIND(x) (x + 0x034)
# define LPC_ENET_SA0(x) (x + 0x040)
# define LPC_ENET_SA1(x) (x + 0x044)
# define LPC_ENET_SA2(x) (x + 0x048)
# define LPC_ENET_COMMAND(x) (x + 0x100)
# define LPC_ENET_STATUS(x) (x + 0x104)
# define LPC_ENET_RXDESCRIPTOR(x) (x + 0x108)
# define LPC_ENET_RXSTATUS(x) (x + 0x10C)
# define LPC_ENET_RXDESCRIPTORNUMBER(x) (x + 0x110)
# define LPC_ENET_RXPRODUCEINDEX(x) (x + 0x114)
# define LPC_ENET_RXCONSUMEINDEX(x) (x + 0x118)
# define LPC_ENET_TXDESCRIPTOR(x) (x + 0x11C)
# define LPC_ENET_TXSTATUS(x) (x + 0x120)
# define LPC_ENET_TXDESCRIPTORNUMBER(x) (x + 0x124)
# define LPC_ENET_TXPRODUCEINDEX(x) (x + 0x128)
# define LPC_ENET_TXCONSUMEINDEX(x) (x + 0x12C)
# define LPC_ENET_TSV0(x) (x + 0x158)
# define LPC_ENET_TSV1(x) (x + 0x15C)
# define LPC_ENET_RSV(x) (x + 0x160)
# define LPC_ENET_FLOWCONTROLCOUNTER(x) (x + 0x170)
# define LPC_ENET_FLOWCONTROLSTATUS(x) (x + 0x174)
# define LPC_ENET_RXFILTER_CTRL(x) (x + 0x200)
# define LPC_ENET_RXFILTERWOLSTATUS(x) (x + 0x204)
# define LPC_ENET_RXFILTERWOLCLEAR(x) (x + 0x208)
# define LPC_ENET_HASHFILTERL(x) (x + 0x210)
# define LPC_ENET_HASHFILTERH(x) (x + 0x214)
# define LPC_ENET_INTSTATUS(x) (x + 0xFE0)
# define LPC_ENET_INTENABLE(x) (x + 0xFE4)
# define LPC_ENET_INTCLEAR(x) (x + 0xFE8)
# define LPC_ENET_INTSET(x) (x + 0xFEC)
# define LPC_ENET_POWERDOWN(x) (x + 0xFF4)
/*
* mac1 register definitions
*/
# define LPC_MAC1_RECV_ENABLE (1 << 0)
# define LPC_MAC1_PASS_ALL_RX_FRAMES (1 << 1)
# define LPC_MAC1_RX_FLOW_CONTROL (1 << 2)
# define LPC_MAC1_TX_FLOW_CONTROL (1 << 3)
# define LPC_MAC1_LOOPBACK (1 << 4)
# define LPC_MAC1_RESET_TX (1 << 8)
# define LPC_MAC1_RESET_MCS_TX (1 << 9)
# define LPC_MAC1_RESET_RX (1 << 10)
# define LPC_MAC1_RESET_MCS_RX (1 << 11)
# define LPC_MAC1_SIMULATION_RESET (1 << 14)
# define LPC_MAC1_SOFT_RESET (1 << 15)
/*
* mac2 register definitions
*/
# define LPC_MAC2_FULL_DUPLEX (1 << 0)
# define LPC_MAC2_FRAME_LENGTH_CHECKING (1 << 1)
# define LPC_MAC2_HUGH_LENGTH_CHECKING (1 << 2)
# define LPC_MAC2_DELAYED_CRC (1 << 3)
# define LPC_MAC2_CRC_ENABLE (1 << 4)
# define LPC_MAC2_PAD_CRC_ENABLE (1 << 5)
# define LPC_MAC2_VLAN_PAD_ENABLE (1 << 6)
# define LPC_MAC2_AUTO_DETECT_PAD_ENABLE (1 << 7)
# define LPC_MAC2_PURE_PREAMBLE_ENFORCEMENT (1 << 8)
# define LPC_MAC2_LONG_PREAMBLE_ENFORCEMENT (1 << 9)
# define LPC_MAC2_NO_BACKOFF (1 << 12)
# define LPC_MAC2_BACK_PRESSURE (1 << 13)
# define LPC_MAC2_EXCESS_DEFER (1 << 14)
/*
* ipgt register definitions
*/
# define LPC_IPGT_LOAD(n) ((n) & 0x7F)
/*
* ipgr register definitions
*/
# define LPC_IPGR_LOAD_PART2(n) ((n) & 0x7F)
# define LPC_IPGR_LOAD_PART1(n) (((n) & 0x7F) << 8)
/*
* clrt register definitions
*/
# define LPC_CLRT_LOAD_RETRY_MAX(n) ((n) & 0xF)
# define LPC_CLRT_LOAD_COLLISION_WINDOW(n) (((n) & 0x3F) << 8)
/*
* maxf register definitions
*/
# define LPC_MAXF_LOAD_MAX_FRAME_LEN(n) ((n) & 0xFFFF)
/*
* supp register definitions
*/
# define LPC_SUPP_SPEED (1 << 8)
# define LPC_SUPP_RESET_RMII (1 << 11)
/*
* test register definitions
*/
# define LPC_TEST_SHORTCUT_PAUSE_QUANTA (1 << 0)
# define LPC_TEST_PAUSE (1 << 1)
# define LPC_TEST_BACKPRESSURE (1 << 2)
/*
* mcfg register definitions
*/
# define LPC_MCFG_SCAN_INCREMENT (1 << 0)
# define LPC_MCFG_SUPPRESS_PREAMBLE (1 << 1)
# define LPC_MCFG_CLOCK_SELECT(n) (((n) & 0x7) << 2)
# define LPC_MCFG_CLOCK_HOST_DIV_4 0
# define LPC_MCFG_CLOCK_HOST_DIV_6 2
# define LPC_MCFG_CLOCK_HOST_DIV_8 3
# define LPC_MCFG_CLOCK_HOST_DIV_10 4
# define LPC_MCFG_CLOCK_HOST_DIV_14 5
# define LPC_MCFG_CLOCK_HOST_DIV_20 6
# define LPC_MCFG_CLOCK_HOST_DIV_28 7
# define LPC_MCFG_RESET_MII_MGMT (1 << 15)
/*
* mcmd register definitions
*/
# define LPC_MCMD_READ (1 << 0)
# define LPC_MCMD_SCAN (1 << 1)
/*
* madr register definitions
*/
# define LPC_MADR_REGISTER_ADDRESS(n) ((n) & 0x1F)
# define LPC_MADR_PHY_0ADDRESS(n) (((n) & 0x1F) << 8)
/*
* mwtd register definitions
*/
# define LPC_MWDT_WRITE(n) ((n) & 0xFFFF)
/*
* mrdd register definitions
*/
# define LPC_MRDD_READ_MASK 0xFFFF
/*
* mind register definitions
*/
# define LPC_MIND_BUSY (1 << 0)
# define LPC_MIND_SCANNING (1 << 1)
# define LPC_MIND_NOT_VALID (1 << 2)
# define LPC_MIND_MII_LINK_FAIL (1 << 3)
/*
* command register definitions
*/
# define LPC_COMMAND_RXENABLE (1 << 0)
# define LPC_COMMAND_TXENABLE (1 << 1)
# define LPC_COMMAND_REG_RESET (1 << 3)
# define LPC_COMMAND_TXRESET (1 << 4)
# define LPC_COMMAND_RXRESET (1 << 5)
# define LPC_COMMAND_PASSRUNTFRAME (1 << 6)
# define LPC_COMMAND_PASSRXFILTER (1 << 7)
# define LPC_COMMAND_TXFLOWCONTROL (1 << 8)
# define LPC_COMMAND_RMII (1 << 9)
# define LPC_COMMAND_FULLDUPLEX (1 << 10)
/*
* status register definitions
*/
# define LPC_STATUS_RXACTIVE (1 << 0)
# define LPC_STATUS_TXACTIVE (1 << 1)
/*
* tsv0 register definitions
*/
# define LPC_TSV0_CRC_ERROR (1 << 0)
# define LPC_TSV0_LENGTH_CHECK_ERROR (1 << 1)
# define LPC_TSV0_LENGTH_OUT_OF_RANGE (1 << 2)
# define LPC_TSV0_DONE (1 << 3)
# define LPC_TSV0_MULTICAST (1 << 4)
# define LPC_TSV0_BROADCAST (1 << 5)
# define LPC_TSV0_PACKET_DEFER (1 << 6)
# define LPC_TSV0_ESCESSIVE_DEFER (1 << 7)
# define LPC_TSV0_ESCESSIVE_COLLISION (1 << 8)
# define LPC_TSV0_LATE_COLLISION (1 << 9)
# define LPC_TSV0_GIANT (1 << 10)
# define LPC_TSV0_UNDERRUN (1 << 11)
# define LPC_TSV0_TOTAL_BYTES(n) (((n) >> 12) & 0xFFFF)
# define LPC_TSV0_CONTROL_FRAME (1 << 28)
# define LPC_TSV0_PAUSE (1 << 29)
# define LPC_TSV0_BACKPRESSURE (1 << 30)
# define LPC_TSV0_VLAN (1 << 31)
/*
* tsv1 register definitions
*/
# define LPC_TSV1_TRANSMIT_BYTE_COUNT(n) ((n) & 0xFFFF)
# define LPC_TSV1_COLLISION_COUNT(n) (((n) >> 16) & 0xF)
/*
* rsv register definitions
*/
# define LPC_RSV_RECEIVED_BYTE_COUNT(n) ((n) & 0xFFFF)
# define LPC_RSV_RXDV_EVENT_IGNORED (1 << 16)
# define LPC_RSV_RXDV_EVENT_PREVIOUSLY_SEEN (1 << 17)
# define LPC_RSV_CARRIER_EVNT_PREVIOUS_SEEN (1 << 18)
# define LPC_RSV_RECEIVE_CODE_VIOLATION (1 << 19)
# define LPC_RSV_CRC_ERROR (1 << 20)
# define LPC_RSV_LENGTH_CHECK_ERROR (1 << 21)
# define LPC_RSV_LENGTH_OUT_OF_RANGE (1 << 22)
# define LPC_RSV_RECEIVE_OK (1 << 23)
# define LPC_RSV_MULTICAST (1 << 24)
# define LPC_RSV_BROADCAST (1 << 25)
# define LPC_RSV_DRIBBLE_NIBBLE (1 << 26)
# define LPC_RSV_CONTROL_FRAME (1 << 27)
# define LPC_RSV_PAUSE (1 << 28)
# define LPC_RSV_UNSUPPORTED_OPCODE (1 << 29)
# define LPC_RSV_VLAN (1 << 30)
/*
* flowcontrolcounter register definitions
*/
# define LPC_FCCR_MIRRORCOUNTER(n) ((n) & 0xFFFF)
# define LPC_FCCR_PAUSETIMER(n) (((n) >> 16) & 0xFFFF)
/*
* flowcontrolstatus register definitions
*/
# define LPC_FCCR_MIRRORCOUNTERCURRENT(n) ((n) & 0xFFFF)
/*
* rxfliterctrl , rxfilterwolstatus , and rxfilterwolclear shared
* register definitions
*/
# define LPC_RXFLTRW_ACCEPTUNICAST (1 << 0)
# define LPC_RXFLTRW_ACCEPTUBROADCAST (1 << 1)
# define LPC_RXFLTRW_ACCEPTUMULTICAST (1 << 2)
# define LPC_RXFLTRW_ACCEPTUNICASTHASH (1 << 3)
# define LPC_RXFLTRW_ACCEPTUMULTICASTHASH (1 << 4)
# define LPC_RXFLTRW_ACCEPTPERFECT (1 << 5)
/*
* rxfliterctrl register definitions
*/
# define LPC_RXFLTRWSTS_MAGICPACKETENWOL (1 << 12)
# define LPC_RXFLTRWSTS_RXFILTERENWOL (1 << 13)
/*
* rxfilterwolstatus / rxfilterwolclear register definitions
*/
# define LPC_RXFLTRWSTS_RXFILTERWOL (1 << 7)
# define LPC_RXFLTRWSTS_MAGICPACKETWOL (1 << 8)
/*
* intstatus , intenable , intclear , and Intset shared register
* definitions
*/
# define LPC_MACINT_RXOVERRUNINTEN (1 << 0)
# define LPC_MACINT_RXERRORONINT (1 << 1)
# define LPC_MACINT_RXFINISHEDINTEN (1 << 2)
# define LPC_MACINT_RXDONEINTEN (1 << 3)
# define LPC_MACINT_TXUNDERRUNINTEN (1 << 4)
# define LPC_MACINT_TXERRORINTEN (1 << 5)
# define LPC_MACINT_TXFINISHEDINTEN (1 << 6)
# define LPC_MACINT_TXDONEINTEN (1 << 7)
# define LPC_MACINT_SOFTINTEN (1 << 12)
# define LPC_MACINT_WAKEUPINTEN (1 << 13)
/*
* powerdown register definitions
*/
# define LPC_POWERDOWN_MACAHB (1 << 31)
2012-04-22 12:01:19 +02:00
static phy_interface_t lpc_phy_interface_mode ( struct device * dev )
2012-03-08 11:49:17 +00:00
{
2012-04-22 12:01:19 +02:00
if ( dev & & dev - > of_node ) {
const char * mode = of_get_property ( dev - > of_node ,
" phy-mode " , NULL ) ;
if ( mode & & ! strcmp ( mode , " mii " ) )
return PHY_INTERFACE_MODE_MII ;
}
2012-03-08 11:49:17 +00:00
return PHY_INTERFACE_MODE_RMII ;
}
2012-04-22 12:01:19 +02:00
static bool use_iram_for_net ( struct device * dev )
2012-03-08 11:49:17 +00:00
{
2012-04-22 12:01:19 +02:00
if ( dev & & dev - > of_node )
return of_property_read_bool ( dev - > of_node , " use-iram " ) ;
return false ;
2012-03-08 11:49:17 +00:00
}
/* Receive Status information word */
# define RXSTATUS_SIZE 0x000007FF
# define RXSTATUS_CONTROL (1 << 18)
# define RXSTATUS_VLAN (1 << 19)
# define RXSTATUS_FILTER (1 << 20)
# define RXSTATUS_MULTICAST (1 << 21)
# define RXSTATUS_BROADCAST (1 << 22)
# define RXSTATUS_CRC (1 << 23)
# define RXSTATUS_SYMBOL (1 << 24)
# define RXSTATUS_LENGTH (1 << 25)
# define RXSTATUS_RANGE (1 << 26)
# define RXSTATUS_ALIGN (1 << 27)
# define RXSTATUS_OVERRUN (1 << 28)
# define RXSTATUS_NODESC (1 << 29)
# define RXSTATUS_LAST (1 << 30)
# define RXSTATUS_ERROR (1 << 31)
# define RXSTATUS_STATUS_ERROR \
( RXSTATUS_NODESC | RXSTATUS_OVERRUN | RXSTATUS_ALIGN | \
RXSTATUS_RANGE | RXSTATUS_LENGTH | RXSTATUS_SYMBOL | RXSTATUS_CRC )
/* Receive Descriptor control word */
# define RXDESC_CONTROL_SIZE 0x000007FF
# define RXDESC_CONTROL_INT (1 << 31)
/* Transmit Status information word */
# define TXSTATUS_COLLISIONS_GET(x) (((x) >> 21) & 0xF)
# define TXSTATUS_DEFER (1 << 25)
# define TXSTATUS_EXCESSDEFER (1 << 26)
# define TXSTATUS_EXCESSCOLL (1 << 27)
# define TXSTATUS_LATECOLL (1 << 28)
# define TXSTATUS_UNDERRUN (1 << 29)
# define TXSTATUS_NODESC (1 << 30)
# define TXSTATUS_ERROR (1 << 31)
/* Transmit Descriptor control word */
# define TXDESC_CONTROL_SIZE 0x000007FF
# define TXDESC_CONTROL_OVERRIDE (1 << 26)
# define TXDESC_CONTROL_HUGE (1 << 27)
# define TXDESC_CONTROL_PAD (1 << 28)
# define TXDESC_CONTROL_CRC (1 << 29)
# define TXDESC_CONTROL_LAST (1 << 30)
# define TXDESC_CONTROL_INT (1 << 31)
/*
* Structure of a TX / RX descriptors and RX status
*/
struct txrx_desc_t {
__le32 packet ;
__le32 control ;
} ;
struct rx_status_t {
__le32 statusinfo ;
__le32 statushashcrc ;
} ;
/*
* Device driver data structure
*/
struct netdata_local {
struct platform_device * pdev ;
struct net_device * ndev ;
spinlock_t lock ;
void __iomem * net_base ;
u32 msg_enable ;
2012-06-12 23:58:16 +00:00
unsigned int skblen [ ENET_TX_DESC ] ;
2012-03-08 11:49:17 +00:00
unsigned int last_tx_idx ;
unsigned int num_used_tx_buffs ;
struct mii_bus * mii_bus ;
struct clk * clk ;
dma_addr_t dma_buff_base_p ;
void * dma_buff_base_v ;
size_t dma_buff_size ;
struct txrx_desc_t * tx_desc_v ;
u32 * tx_stat_v ;
void * tx_buff_v ;
struct txrx_desc_t * rx_desc_v ;
struct rx_status_t * rx_stat_v ;
void * rx_buff_v ;
int link ;
int speed ;
int duplex ;
struct napi_struct napi ;
} ;
/*
* MAC support functions
*/
static void __lpc_set_mac ( struct netdata_local * pldat , u8 * mac )
{
u32 tmp ;
/* Set station address */
tmp = mac [ 0 ] | ( ( u32 ) mac [ 1 ] < < 8 ) ;
writel ( tmp , LPC_ENET_SA2 ( pldat - > net_base ) ) ;
tmp = mac [ 2 ] | ( ( u32 ) mac [ 3 ] < < 8 ) ;
writel ( tmp , LPC_ENET_SA1 ( pldat - > net_base ) ) ;
tmp = mac [ 4 ] | ( ( u32 ) mac [ 5 ] < < 8 ) ;
writel ( tmp , LPC_ENET_SA0 ( pldat - > net_base ) ) ;
netdev_dbg ( pldat - > ndev , " Ethernet MAC address %pM \n " , mac ) ;
}
static void __lpc_get_mac ( struct netdata_local * pldat , u8 * mac )
{
u32 tmp ;
/* Get station address */
tmp = readl ( LPC_ENET_SA2 ( pldat - > net_base ) ) ;
mac [ 0 ] = tmp & 0xFF ;
mac [ 1 ] = tmp > > 8 ;
tmp = readl ( LPC_ENET_SA1 ( pldat - > net_base ) ) ;
mac [ 2 ] = tmp & 0xFF ;
mac [ 3 ] = tmp > > 8 ;
tmp = readl ( LPC_ENET_SA0 ( pldat - > net_base ) ) ;
mac [ 4 ] = tmp & 0xFF ;
mac [ 5 ] = tmp > > 8 ;
}
static void __lpc_params_setup ( struct netdata_local * pldat )
{
u32 tmp ;
if ( pldat - > duplex = = DUPLEX_FULL ) {
tmp = readl ( LPC_ENET_MAC2 ( pldat - > net_base ) ) ;
tmp | = LPC_MAC2_FULL_DUPLEX ;
writel ( tmp , LPC_ENET_MAC2 ( pldat - > net_base ) ) ;
tmp = readl ( LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
tmp | = LPC_COMMAND_FULLDUPLEX ;
writel ( tmp , LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
writel ( LPC_IPGT_LOAD ( 0x15 ) , LPC_ENET_IPGT ( pldat - > net_base ) ) ;
} else {
tmp = readl ( LPC_ENET_MAC2 ( pldat - > net_base ) ) ;
tmp & = ~ LPC_MAC2_FULL_DUPLEX ;
writel ( tmp , LPC_ENET_MAC2 ( pldat - > net_base ) ) ;
tmp = readl ( LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
tmp & = ~ LPC_COMMAND_FULLDUPLEX ;
writel ( tmp , LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
writel ( LPC_IPGT_LOAD ( 0x12 ) , LPC_ENET_IPGT ( pldat - > net_base ) ) ;
}
if ( pldat - > speed = = SPEED_100 )
writel ( LPC_SUPP_SPEED , LPC_ENET_SUPP ( pldat - > net_base ) ) ;
else
writel ( 0 , LPC_ENET_SUPP ( pldat - > net_base ) ) ;
}
static void __lpc_eth_reset ( struct netdata_local * pldat )
{
/* Reset all MAC logic */
writel ( ( LPC_MAC1_RESET_TX | LPC_MAC1_RESET_MCS_TX | LPC_MAC1_RESET_RX |
LPC_MAC1_RESET_MCS_RX | LPC_MAC1_SIMULATION_RESET |
LPC_MAC1_SOFT_RESET ) , LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
writel ( ( LPC_COMMAND_REG_RESET | LPC_COMMAND_TXRESET |
LPC_COMMAND_RXRESET ) , LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
}
static int __lpc_mii_mngt_reset ( struct netdata_local * pldat )
{
/* Reset MII management hardware */
writel ( LPC_MCFG_RESET_MII_MGMT , LPC_ENET_MCFG ( pldat - > net_base ) ) ;
/* Setup MII clock to slowest rate with a /28 divider */
writel ( LPC_MCFG_CLOCK_SELECT ( LPC_MCFG_CLOCK_HOST_DIV_28 ) ,
LPC_ENET_MCFG ( pldat - > net_base ) ) ;
return 0 ;
}
static inline phys_addr_t __va_to_pa ( void * addr , struct netdata_local * pldat )
{
phys_addr_t phaddr ;
phaddr = addr - pldat - > dma_buff_base_v ;
phaddr + = pldat - > dma_buff_base_p ;
return phaddr ;
}
static void lpc_eth_enable_int ( void __iomem * regbase )
{
writel ( ( LPC_MACINT_RXDONEINTEN | LPC_MACINT_TXDONEINTEN ) ,
LPC_ENET_INTENABLE ( regbase ) ) ;
}
static void lpc_eth_disable_int ( void __iomem * regbase )
{
writel ( 0 , LPC_ENET_INTENABLE ( regbase ) ) ;
}
/* Setup TX/RX descriptors */
static void __lpc_txrx_desc_setup ( struct netdata_local * pldat )
{
u32 * ptxstat ;
void * tbuff ;
int i ;
struct txrx_desc_t * ptxrxdesc ;
struct rx_status_t * prxstat ;
tbuff = PTR_ALIGN ( pldat - > dma_buff_base_v , 16 ) ;
/* Setup TX descriptors, status, and buffers */
pldat - > tx_desc_v = tbuff ;
tbuff + = sizeof ( struct txrx_desc_t ) * ENET_TX_DESC ;
pldat - > tx_stat_v = tbuff ;
tbuff + = sizeof ( u32 ) * ENET_TX_DESC ;
tbuff = PTR_ALIGN ( tbuff , 16 ) ;
pldat - > tx_buff_v = tbuff ;
tbuff + = ENET_MAXF_SIZE * ENET_TX_DESC ;
/* Setup RX descriptors, status, and buffers */
pldat - > rx_desc_v = tbuff ;
tbuff + = sizeof ( struct txrx_desc_t ) * ENET_RX_DESC ;
tbuff = PTR_ALIGN ( tbuff , 16 ) ;
pldat - > rx_stat_v = tbuff ;
tbuff + = sizeof ( struct rx_status_t ) * ENET_RX_DESC ;
tbuff = PTR_ALIGN ( tbuff , 16 ) ;
pldat - > rx_buff_v = tbuff ;
tbuff + = ENET_MAXF_SIZE * ENET_RX_DESC ;
/* Map the TX descriptors to the TX buffers in hardware */
for ( i = 0 ; i < ENET_TX_DESC ; i + + ) {
ptxstat = & pldat - > tx_stat_v [ i ] ;
ptxrxdesc = & pldat - > tx_desc_v [ i ] ;
ptxrxdesc - > packet = __va_to_pa (
pldat - > tx_buff_v + i * ENET_MAXF_SIZE , pldat ) ;
ptxrxdesc - > control = 0 ;
* ptxstat = 0 ;
}
/* Map the RX descriptors to the RX buffers in hardware */
for ( i = 0 ; i < ENET_RX_DESC ; i + + ) {
prxstat = & pldat - > rx_stat_v [ i ] ;
ptxrxdesc = & pldat - > rx_desc_v [ i ] ;
ptxrxdesc - > packet = __va_to_pa (
pldat - > rx_buff_v + i * ENET_MAXF_SIZE , pldat ) ;
ptxrxdesc - > control = RXDESC_CONTROL_INT | ( ENET_MAXF_SIZE - 1 ) ;
prxstat - > statusinfo = 0 ;
prxstat - > statushashcrc = 0 ;
}
/* Setup base addresses in hardware to point to buffers and
* descriptors
*/
writel ( ( ENET_TX_DESC - 1 ) ,
LPC_ENET_TXDESCRIPTORNUMBER ( pldat - > net_base ) ) ;
writel ( __va_to_pa ( pldat - > tx_desc_v , pldat ) ,
LPC_ENET_TXDESCRIPTOR ( pldat - > net_base ) ) ;
writel ( __va_to_pa ( pldat - > tx_stat_v , pldat ) ,
LPC_ENET_TXSTATUS ( pldat - > net_base ) ) ;
writel ( ( ENET_RX_DESC - 1 ) ,
LPC_ENET_RXDESCRIPTORNUMBER ( pldat - > net_base ) ) ;
writel ( __va_to_pa ( pldat - > rx_desc_v , pldat ) ,
LPC_ENET_RXDESCRIPTOR ( pldat - > net_base ) ) ;
writel ( __va_to_pa ( pldat - > rx_stat_v , pldat ) ,
LPC_ENET_RXSTATUS ( pldat - > net_base ) ) ;
}
static void __lpc_eth_init ( struct netdata_local * pldat )
{
u32 tmp ;
/* Disable controller and reset */
tmp = readl ( LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
tmp & = ~ LPC_COMMAND_RXENABLE | LPC_COMMAND_TXENABLE ;
writel ( tmp , LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
tmp = readl ( LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
tmp & = ~ LPC_MAC1_RECV_ENABLE ;
writel ( tmp , LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
/* Initial MAC setup */
writel ( LPC_MAC1_PASS_ALL_RX_FRAMES , LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
writel ( ( LPC_MAC2_PAD_CRC_ENABLE | LPC_MAC2_CRC_ENABLE ) ,
LPC_ENET_MAC2 ( pldat - > net_base ) ) ;
writel ( ENET_MAXF_SIZE , LPC_ENET_MAXF ( pldat - > net_base ) ) ;
/* Collision window, gap */
writel ( ( LPC_CLRT_LOAD_RETRY_MAX ( 0xF ) |
LPC_CLRT_LOAD_COLLISION_WINDOW ( 0x37 ) ) ,
LPC_ENET_CLRT ( pldat - > net_base ) ) ;
writel ( LPC_IPGR_LOAD_PART2 ( 0x12 ) , LPC_ENET_IPGR ( pldat - > net_base ) ) ;
2012-04-22 12:01:19 +02:00
if ( lpc_phy_interface_mode ( & pldat - > pdev - > dev ) = = PHY_INTERFACE_MODE_MII )
2012-03-08 11:49:17 +00:00
writel ( LPC_COMMAND_PASSRUNTFRAME ,
LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
else {
writel ( ( LPC_COMMAND_PASSRUNTFRAME | LPC_COMMAND_RMII ) ,
LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
writel ( LPC_SUPP_RESET_RMII , LPC_ENET_SUPP ( pldat - > net_base ) ) ;
}
__lpc_params_setup ( pldat ) ;
/* Setup TX and RX descriptors */
__lpc_txrx_desc_setup ( pldat ) ;
/* Setup packet filtering */
writel ( ( LPC_RXFLTRW_ACCEPTUBROADCAST | LPC_RXFLTRW_ACCEPTPERFECT ) ,
LPC_ENET_RXFILTER_CTRL ( pldat - > net_base ) ) ;
/* Get the next TX buffer output index */
pldat - > num_used_tx_buffs = 0 ;
pldat - > last_tx_idx =
readl ( LPC_ENET_TXCONSUMEINDEX ( pldat - > net_base ) ) ;
/* Clear and enable interrupts */
writel ( 0xFFFF , LPC_ENET_INTCLEAR ( pldat - > net_base ) ) ;
smp_wmb ( ) ;
lpc_eth_enable_int ( pldat - > net_base ) ;
/* Enable controller */
tmp = readl ( LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
tmp | = LPC_COMMAND_RXENABLE | LPC_COMMAND_TXENABLE ;
writel ( tmp , LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
tmp = readl ( LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
tmp | = LPC_MAC1_RECV_ENABLE ;
writel ( tmp , LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
}
static void __lpc_eth_shutdown ( struct netdata_local * pldat )
{
/* Reset ethernet and power down PHY */
__lpc_eth_reset ( pldat ) ;
writel ( 0 , LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
writel ( 0 , LPC_ENET_MAC2 ( pldat - > net_base ) ) ;
}
/*
* MAC < - - - > PHY support functions
*/
static int lpc_mdio_read ( struct mii_bus * bus , int phy_id , int phyreg )
{
struct netdata_local * pldat = bus - > priv ;
unsigned long timeout = jiffies + msecs_to_jiffies ( 100 ) ;
int lps ;
writel ( ( ( phy_id < < 8 ) | phyreg ) , LPC_ENET_MADR ( pldat - > net_base ) ) ;
writel ( LPC_MCMD_READ , LPC_ENET_MCMD ( pldat - > net_base ) ) ;
/* Wait for unbusy status */
while ( readl ( LPC_ENET_MIND ( pldat - > net_base ) ) & LPC_MIND_BUSY ) {
if ( time_after ( jiffies , timeout ) )
return - EIO ;
cpu_relax ( ) ;
}
lps = readl ( LPC_ENET_MRDD ( pldat - > net_base ) ) ;
writel ( 0 , LPC_ENET_MCMD ( pldat - > net_base ) ) ;
return lps ;
}
static int lpc_mdio_write ( struct mii_bus * bus , int phy_id , int phyreg ,
u16 phydata )
{
struct netdata_local * pldat = bus - > priv ;
unsigned long timeout = jiffies + msecs_to_jiffies ( 100 ) ;
writel ( ( ( phy_id < < 8 ) | phyreg ) , LPC_ENET_MADR ( pldat - > net_base ) ) ;
writel ( phydata , LPC_ENET_MWTD ( pldat - > net_base ) ) ;
/* Wait for completion */
while ( readl ( LPC_ENET_MIND ( pldat - > net_base ) ) & LPC_MIND_BUSY ) {
if ( time_after ( jiffies , timeout ) )
return - EIO ;
cpu_relax ( ) ;
}
return 0 ;
}
static int lpc_mdio_reset ( struct mii_bus * bus )
{
return __lpc_mii_mngt_reset ( ( struct netdata_local * ) bus - > priv ) ;
}
static void lpc_handle_link_change ( struct net_device * ndev )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
2016-06-28 23:59:44 +02:00
struct phy_device * phydev = ndev - > phydev ;
2012-03-08 11:49:17 +00:00
unsigned long flags ;
bool status_change = false ;
spin_lock_irqsave ( & pldat - > lock , flags ) ;
if ( phydev - > link ) {
if ( ( pldat - > speed ! = phydev - > speed ) | |
( pldat - > duplex ! = phydev - > duplex ) ) {
pldat - > speed = phydev - > speed ;
pldat - > duplex = phydev - > duplex ;
status_change = true ;
}
}
if ( phydev - > link ! = pldat - > link ) {
if ( ! phydev - > link ) {
pldat - > speed = 0 ;
pldat - > duplex = - 1 ;
}
pldat - > link = phydev - > link ;
status_change = true ;
}
spin_unlock_irqrestore ( & pldat - > lock , flags ) ;
if ( status_change )
__lpc_params_setup ( pldat ) ;
}
static int lpc_mii_probe ( struct net_device * ndev )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
struct phy_device * phydev = phy_find_first ( pldat - > mii_bus ) ;
if ( ! phydev ) {
netdev_err ( ndev , " no PHY found \n " ) ;
return - ENODEV ;
}
/* Attach to the PHY */
2012-04-22 12:01:19 +02:00
if ( lpc_phy_interface_mode ( & pldat - > pdev - > dev ) = = PHY_INTERFACE_MODE_MII )
2012-03-08 11:49:17 +00:00
netdev_info ( ndev , " using MII interface \n " ) ;
else
netdev_info ( ndev , " using RMII interface \n " ) ;
2016-01-06 20:11:10 +01:00
phydev = phy_connect ( ndev , phydev_name ( phydev ) ,
2013-01-14 00:52:52 +00:00
& lpc_handle_link_change ,
2012-04-22 12:01:19 +02:00
lpc_phy_interface_mode ( & pldat - > pdev - > dev ) ) ;
2012-03-08 11:49:17 +00:00
if ( IS_ERR ( phydev ) ) {
netdev_err ( ndev , " Could not attach to PHY \n " ) ;
return PTR_ERR ( phydev ) ;
}
2018-09-12 01:53:11 +02:00
phy_set_max_speed ( phydev , SPEED_100 ) ;
2012-03-08 11:49:17 +00:00
phydev - > advertising = phydev - > supported ;
pldat - > link = 0 ;
pldat - > speed = 0 ;
pldat - > duplex = - 1 ;
2016-01-06 20:11:13 +01:00
phy_attached_info ( phydev ) ;
2012-03-08 11:49:17 +00:00
return 0 ;
}
static int lpc_mii_init ( struct netdata_local * pldat )
{
2016-01-08 10:19:16 -02:00
int err = - ENXIO ;
2012-03-08 11:49:17 +00:00
pldat - > mii_bus = mdiobus_alloc ( ) ;
if ( ! pldat - > mii_bus ) {
err = - ENOMEM ;
goto err_out ;
}
/* Setup MII mode */
2012-04-22 12:01:19 +02:00
if ( lpc_phy_interface_mode ( & pldat - > pdev - > dev ) = = PHY_INTERFACE_MODE_MII )
2012-03-08 11:49:17 +00:00
writel ( LPC_COMMAND_PASSRUNTFRAME ,
LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
else {
writel ( ( LPC_COMMAND_PASSRUNTFRAME | LPC_COMMAND_RMII ) ,
LPC_ENET_COMMAND ( pldat - > net_base ) ) ;
writel ( LPC_SUPP_RESET_RMII , LPC_ENET_SUPP ( pldat - > net_base ) ) ;
}
pldat - > mii_bus - > name = " lpc_mii_bus " ;
pldat - > mii_bus - > read = & lpc_mdio_read ;
pldat - > mii_bus - > write = & lpc_mdio_write ;
pldat - > mii_bus - > reset = & lpc_mdio_reset ;
snprintf ( pldat - > mii_bus - > id , MII_BUS_ID_SIZE , " %s-%x " ,
pldat - > pdev - > name , pldat - > pdev - > id ) ;
pldat - > mii_bus - > priv = pldat ;
pldat - > mii_bus - > parent = & pldat - > pdev - > dev ;
platform_set_drvdata ( pldat - > pdev , pldat - > mii_bus ) ;
if ( mdiobus_register ( pldat - > mii_bus ) )
2016-01-06 20:11:15 +01:00
goto err_out_unregister_bus ;
2012-03-08 11:49:17 +00:00
if ( lpc_mii_probe ( pldat - > ndev ) ! = 0 )
goto err_out_unregister_bus ;
return 0 ;
err_out_unregister_bus :
mdiobus_unregister ( pldat - > mii_bus ) ;
mdiobus_free ( pldat - > mii_bus ) ;
err_out :
return err ;
}
static void __lpc_handle_xmit ( struct net_device * ndev )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
u32 txcidx , * ptxstat , txstat ;
txcidx = readl ( LPC_ENET_TXCONSUMEINDEX ( pldat - > net_base ) ) ;
while ( pldat - > last_tx_idx ! = txcidx ) {
2012-06-12 23:58:16 +00:00
unsigned int skblen = pldat - > skblen [ pldat - > last_tx_idx ] ;
2012-03-08 11:49:17 +00:00
/* A buffer is available, get buffer status */
ptxstat = & pldat - > tx_stat_v [ pldat - > last_tx_idx ] ;
txstat = * ptxstat ;
/* Next buffer and decrement used buffer counter */
pldat - > num_used_tx_buffs - - ;
pldat - > last_tx_idx + + ;
if ( pldat - > last_tx_idx > = ENET_TX_DESC )
pldat - > last_tx_idx = 0 ;
/* Update collision counter */
ndev - > stats . collisions + = TXSTATUS_COLLISIONS_GET ( txstat ) ;
/* Any errors occurred? */
if ( txstat & TXSTATUS_ERROR ) {
if ( txstat & TXSTATUS_UNDERRUN ) {
/* FIFO underrun */
ndev - > stats . tx_fifo_errors + + ;
}
if ( txstat & TXSTATUS_LATECOLL ) {
/* Late collision */
ndev - > stats . tx_aborted_errors + + ;
}
if ( txstat & TXSTATUS_EXCESSCOLL ) {
/* Excessive collision */
ndev - > stats . tx_aborted_errors + + ;
}
if ( txstat & TXSTATUS_EXCESSDEFER ) {
/* Defer limit */
ndev - > stats . tx_aborted_errors + + ;
}
ndev - > stats . tx_errors + + ;
} else {
/* Update stats */
ndev - > stats . tx_packets + + ;
2012-06-12 23:58:16 +00:00
ndev - > stats . tx_bytes + = skblen ;
2012-03-08 11:49:17 +00:00
}
txcidx = readl ( LPC_ENET_TXCONSUMEINDEX ( pldat - > net_base ) ) ;
}
2012-06-11 07:21:36 +00:00
if ( pldat - > num_used_tx_buffs < = ENET_TX_DESC / 2 ) {
if ( netif_queue_stopped ( ndev ) )
netif_wake_queue ( ndev ) ;
}
2012-03-08 11:49:17 +00:00
}
static int __lpc_handle_recv ( struct net_device * ndev , int budget )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
struct sk_buff * skb ;
u32 rxconsidx , len , ethst ;
struct rx_status_t * prxstat ;
int rx_done = 0 ;
/* Get the current RX buffer indexes */
rxconsidx = readl ( LPC_ENET_RXCONSUMEINDEX ( pldat - > net_base ) ) ;
while ( rx_done < budget & & rxconsidx ! =
readl ( LPC_ENET_RXPRODUCEINDEX ( pldat - > net_base ) ) ) {
/* Get pointer to receive status */
prxstat = & pldat - > rx_stat_v [ rxconsidx ] ;
len = ( prxstat - > statusinfo & RXSTATUS_SIZE ) + 1 ;
/* Status error? */
ethst = prxstat - > statusinfo ;
if ( ( ethst & ( RXSTATUS_ERROR | RXSTATUS_STATUS_ERROR ) ) = =
( RXSTATUS_ERROR | RXSTATUS_RANGE ) )
ethst & = ~ RXSTATUS_ERROR ;
if ( ethst & RXSTATUS_ERROR ) {
int si = prxstat - > statusinfo ;
/* Check statuses */
if ( si & RXSTATUS_OVERRUN ) {
/* Overrun error */
ndev - > stats . rx_fifo_errors + + ;
} else if ( si & RXSTATUS_CRC ) {
/* CRC error */
ndev - > stats . rx_crc_errors + + ;
} else if ( si & RXSTATUS_LENGTH ) {
/* Length error */
ndev - > stats . rx_length_errors + + ;
} else if ( si & RXSTATUS_ERROR ) {
/* Other error */
ndev - > stats . rx_length_errors + + ;
}
ndev - > stats . rx_errors + + ;
} else {
/* Packet is good */
2012-04-03 12:02:11 +00:00
skb = dev_alloc_skb ( len ) ;
if ( ! skb ) {
2012-03-08 11:49:17 +00:00
ndev - > stats . rx_dropped + + ;
2012-04-03 12:02:11 +00:00
} else {
2012-03-08 11:49:17 +00:00
/* Copy packet from buffer */
2017-06-18 22:52:04 +08:00
skb_put_data ( skb ,
pldat - > rx_buff_v + rxconsidx * ENET_MAXF_SIZE ,
len ) ;
2012-03-08 11:49:17 +00:00
/* Pass to upper layer */
skb - > protocol = eth_type_trans ( skb , ndev ) ;
netif_receive_skb ( skb ) ;
ndev - > stats . rx_packets + + ;
ndev - > stats . rx_bytes + = len ;
}
}
/* Increment consume index */
rxconsidx = rxconsidx + 1 ;
if ( rxconsidx > = ENET_RX_DESC )
rxconsidx = 0 ;
writel ( rxconsidx ,
LPC_ENET_RXCONSUMEINDEX ( pldat - > net_base ) ) ;
rx_done + + ;
}
return rx_done ;
}
static int lpc_eth_poll ( struct napi_struct * napi , int budget )
{
struct netdata_local * pldat = container_of ( napi ,
struct netdata_local , napi ) ;
struct net_device * ndev = pldat - > ndev ;
int rx_done = 0 ;
struct netdev_queue * txq = netdev_get_tx_queue ( ndev , 0 ) ;
__netif_tx_lock ( txq , smp_processor_id ( ) ) ;
__lpc_handle_xmit ( ndev ) ;
__netif_tx_unlock ( txq ) ;
rx_done = __lpc_handle_recv ( ndev , budget ) ;
if ( rx_done < budget ) {
2017-01-30 08:22:01 -08:00
napi_complete_done ( napi , rx_done ) ;
2012-03-08 11:49:17 +00:00
lpc_eth_enable_int ( pldat - > net_base ) ;
}
return rx_done ;
}
static irqreturn_t __lpc_eth_interrupt ( int irq , void * dev_id )
{
struct net_device * ndev = dev_id ;
struct netdata_local * pldat = netdev_priv ( ndev ) ;
u32 tmp ;
spin_lock ( & pldat - > lock ) ;
tmp = readl ( LPC_ENET_INTSTATUS ( pldat - > net_base ) ) ;
/* Clear interrupts */
writel ( tmp , LPC_ENET_INTCLEAR ( pldat - > net_base ) ) ;
lpc_eth_disable_int ( pldat - > net_base ) ;
if ( likely ( napi_schedule_prep ( & pldat - > napi ) ) )
__napi_schedule ( & pldat - > napi ) ;
spin_unlock ( & pldat - > lock ) ;
return IRQ_HANDLED ;
}
static int lpc_eth_close ( struct net_device * ndev )
{
unsigned long flags ;
struct netdata_local * pldat = netdev_priv ( ndev ) ;
if ( netif_msg_ifdown ( pldat ) )
dev_dbg ( & pldat - > pdev - > dev , " shutting down %s \n " , ndev - > name ) ;
napi_disable ( & pldat - > napi ) ;
netif_stop_queue ( ndev ) ;
2016-06-28 23:59:44 +02:00
if ( ndev - > phydev )
phy_stop ( ndev - > phydev ) ;
2012-03-08 11:49:17 +00:00
spin_lock_irqsave ( & pldat - > lock , flags ) ;
__lpc_eth_reset ( pldat ) ;
netif_carrier_off ( ndev ) ;
writel ( 0 , LPC_ENET_MAC1 ( pldat - > net_base ) ) ;
writel ( 0 , LPC_ENET_MAC2 ( pldat - > net_base ) ) ;
spin_unlock_irqrestore ( & pldat - > lock , flags ) ;
2016-08-23 09:48:20 -03:00
clk_disable_unprepare ( pldat - > clk ) ;
2012-03-08 11:49:17 +00:00
return 0 ;
}
static int lpc_eth_hard_start_xmit ( struct sk_buff * skb , struct net_device * ndev )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
u32 len , txidx ;
u32 * ptxstat ;
struct txrx_desc_t * ptxrxdesc ;
len = skb - > len ;
spin_lock_irq ( & pldat - > lock ) ;
if ( pldat - > num_used_tx_buffs > = ( ENET_TX_DESC - 1 ) ) {
/* This function should never be called when there are no
buffers */
netif_stop_queue ( ndev ) ;
spin_unlock_irq ( & pldat - > lock ) ;
WARN ( 1 , " BUG! TX request when no free TX buffers! \n " ) ;
return NETDEV_TX_BUSY ;
}
/* Get the next TX descriptor index */
txidx = readl ( LPC_ENET_TXPRODUCEINDEX ( pldat - > net_base ) ) ;
/* Setup control for the transfer */
ptxstat = & pldat - > tx_stat_v [ txidx ] ;
* ptxstat = 0 ;
ptxrxdesc = & pldat - > tx_desc_v [ txidx ] ;
ptxrxdesc - > control =
( len - 1 ) | TXDESC_CONTROL_LAST | TXDESC_CONTROL_INT ;
/* Copy data to the DMA buffer */
memcpy ( pldat - > tx_buff_v + txidx * ENET_MAXF_SIZE , skb - > data , len ) ;
/* Save the buffer and increment the buffer counter */
2012-06-12 23:58:16 +00:00
pldat - > skblen [ txidx ] = len ;
2012-03-08 11:49:17 +00:00
pldat - > num_used_tx_buffs + + ;
/* Start transmit */
txidx + + ;
if ( txidx > = ENET_TX_DESC )
txidx = 0 ;
writel ( txidx , LPC_ENET_TXPRODUCEINDEX ( pldat - > net_base ) ) ;
/* Stop queue if no more TX buffers */
if ( pldat - > num_used_tx_buffs > = ( ENET_TX_DESC - 1 ) )
netif_stop_queue ( ndev ) ;
spin_unlock_irq ( & pldat - > lock ) ;
2012-06-12 23:58:16 +00:00
dev_kfree_skb ( skb ) ;
2012-03-08 11:49:17 +00:00
return NETDEV_TX_OK ;
}
static int lpc_set_mac_address ( struct net_device * ndev , void * p )
{
struct sockaddr * addr = p ;
struct netdata_local * pldat = netdev_priv ( ndev ) ;
unsigned long flags ;
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
memcpy ( ndev - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
spin_lock_irqsave ( & pldat - > lock , flags ) ;
/* Set station address */
__lpc_set_mac ( pldat , ndev - > dev_addr ) ;
spin_unlock_irqrestore ( & pldat - > lock , flags ) ;
return 0 ;
}
static void lpc_eth_set_multicast_list ( struct net_device * ndev )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
struct netdev_hw_addr_list * mcptr = & ndev - > mc ;
struct netdev_hw_addr * ha ;
u32 tmp32 , hash_val , hashlo , hashhi ;
unsigned long flags ;
spin_lock_irqsave ( & pldat - > lock , flags ) ;
/* Set station address */
__lpc_set_mac ( pldat , ndev - > dev_addr ) ;
tmp32 = LPC_RXFLTRW_ACCEPTUBROADCAST | LPC_RXFLTRW_ACCEPTPERFECT ;
if ( ndev - > flags & IFF_PROMISC )
tmp32 | = LPC_RXFLTRW_ACCEPTUNICAST |
LPC_RXFLTRW_ACCEPTUMULTICAST ;
if ( ndev - > flags & IFF_ALLMULTI )
tmp32 | = LPC_RXFLTRW_ACCEPTUMULTICAST ;
if ( netdev_hw_addr_list_count ( mcptr ) )
tmp32 | = LPC_RXFLTRW_ACCEPTUMULTICASTHASH ;
writel ( tmp32 , LPC_ENET_RXFILTER_CTRL ( pldat - > net_base ) ) ;
/* Set initial hash table */
hashlo = 0x0 ;
hashhi = 0x0 ;
/* 64 bits : multicast address in hash table */
netdev_hw_addr_list_for_each ( ha , mcptr ) {
hash_val = ( ether_crc ( 6 , ha - > addr ) > > 23 ) & 0x3F ;
if ( hash_val > = 32 )
hashhi | = 1 < < ( hash_val - 32 ) ;
else
hashlo | = 1 < < hash_val ;
}
writel ( hashlo , LPC_ENET_HASHFILTERL ( pldat - > net_base ) ) ;
writel ( hashhi , LPC_ENET_HASHFILTERH ( pldat - > net_base ) ) ;
spin_unlock_irqrestore ( & pldat - > lock , flags ) ;
}
static int lpc_eth_ioctl ( struct net_device * ndev , struct ifreq * req , int cmd )
{
2016-06-28 23:59:44 +02:00
struct phy_device * phydev = ndev - > phydev ;
2012-03-08 11:49:17 +00:00
if ( ! netif_running ( ndev ) )
return - EINVAL ;
if ( ! phydev )
return - ENODEV ;
return phy_mii_ioctl ( phydev , req , cmd ) ;
}
static int lpc_eth_open ( struct net_device * ndev )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
2016-08-23 09:48:20 -03:00
int ret ;
2012-03-08 11:49:17 +00:00
if ( netif_msg_ifup ( pldat ) )
dev_dbg ( & pldat - > pdev - > dev , " enabling %s \n " , ndev - > name ) ;
2016-08-23 09:48:20 -03:00
ret = clk_prepare_enable ( pldat - > clk ) ;
if ( ret )
return ret ;
2012-03-08 11:49:17 +00:00
2014-09-01 13:46:46 +02:00
/* Suspended PHY makes LPC ethernet core block, so resume now */
2016-06-28 23:59:44 +02:00
phy_resume ( ndev - > phydev ) ;
2014-09-01 13:46:46 +02:00
2012-03-08 11:49:17 +00:00
/* Reset and initialize */
__lpc_eth_reset ( pldat ) ;
__lpc_eth_init ( pldat ) ;
/* schedule a link state check */
2016-06-28 23:59:44 +02:00
phy_start ( ndev - > phydev ) ;
2012-03-08 11:49:17 +00:00
netif_start_queue ( ndev ) ;
napi_enable ( & pldat - > napi ) ;
return 0 ;
}
/*
* Ethtool ops
*/
static void lpc_eth_ethtool_getdrvinfo ( struct net_device * ndev ,
struct ethtool_drvinfo * info )
{
2013-01-06 00:44:26 +00:00
strlcpy ( info - > driver , MODNAME , sizeof ( info - > driver ) ) ;
strlcpy ( info - > version , DRV_VERSION , sizeof ( info - > version ) ) ;
strlcpy ( info - > bus_info , dev_name ( ndev - > dev . parent ) ,
sizeof ( info - > bus_info ) ) ;
2012-03-08 11:49:17 +00:00
}
static u32 lpc_eth_ethtool_getmsglevel ( struct net_device * ndev )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
return pldat - > msg_enable ;
}
static void lpc_eth_ethtool_setmsglevel ( struct net_device * ndev , u32 level )
{
struct netdata_local * pldat = netdev_priv ( ndev ) ;
pldat - > msg_enable = level ;
}
static const struct ethtool_ops lpc_eth_ethtool_ops = {
. get_drvinfo = lpc_eth_ethtool_getdrvinfo ,
. get_msglevel = lpc_eth_ethtool_getmsglevel ,
. set_msglevel = lpc_eth_ethtool_setmsglevel ,
. get_link = ethtool_op_get_link ,
2016-06-28 23:59:45 +02:00
. get_link_ksettings = phy_ethtool_get_link_ksettings ,
. set_link_ksettings = phy_ethtool_set_link_ksettings ,
2012-03-08 11:49:17 +00:00
} ;
static const struct net_device_ops lpc_netdev_ops = {
. ndo_open = lpc_eth_open ,
. ndo_stop = lpc_eth_close ,
. ndo_start_xmit = lpc_eth_hard_start_xmit ,
. ndo_set_rx_mode = lpc_eth_set_multicast_list ,
. ndo_do_ioctl = lpc_eth_ioctl ,
. ndo_set_mac_address = lpc_set_mac_address ,
2012-11-16 04:47:15 +00:00
. ndo_validate_addr = eth_validate_addr ,
2012-03-08 11:49:17 +00:00
} ;
static int lpc_eth_drv_probe ( struct platform_device * pdev )
{
2018-10-19 02:25:11 +03:00
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
2012-03-08 11:49:17 +00:00
struct netdata_local * pldat ;
2018-10-19 02:25:11 +03:00
struct net_device * ndev ;
2012-03-08 11:49:17 +00:00
dma_addr_t dma_handle ;
2018-10-19 02:25:11 +03:00
struct resource * res ;
2012-03-08 11:49:17 +00:00
int irq , ret ;
2012-04-22 12:01:19 +02:00
u32 tmp ;
/* Setup network interface for RMII or MII mode */
tmp = __raw_readl ( LPC32XX_CLKPWR_MACCLK_CTRL ) ;
tmp & = ~ LPC32XX_CLKPWR_MACCTRL_PINS_MSK ;
2018-10-19 02:25:11 +03:00
if ( lpc_phy_interface_mode ( dev ) = = PHY_INTERFACE_MODE_MII )
2012-04-22 12:01:19 +02:00
tmp | = LPC32XX_CLKPWR_MACCTRL_USE_MII_PINS ;
else
tmp | = LPC32XX_CLKPWR_MACCTRL_USE_RMII_PINS ;
__raw_writel ( tmp , LPC32XX_CLKPWR_MACCLK_CTRL ) ;
2012-03-08 11:49:17 +00:00
/* Get platform resources */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
irq = platform_get_irq ( pdev , 0 ) ;
2015-12-02 08:12:13 +02:00
if ( ! res | | irq < 0 ) {
2018-10-19 02:25:11 +03:00
dev_err ( dev , " error getting resources. \n " ) ;
2012-03-08 11:49:17 +00:00
ret = - ENXIO ;
goto err_exit ;
}
/* Allocate net driver data structure */
ndev = alloc_etherdev ( sizeof ( struct netdata_local ) ) ;
if ( ! ndev ) {
2018-10-19 02:25:11 +03:00
dev_err ( dev , " could not allocate device. \n " ) ;
2012-03-08 11:49:17 +00:00
ret = - ENOMEM ;
goto err_exit ;
}
2018-10-19 02:25:11 +03:00
SET_NETDEV_DEV ( ndev , dev ) ;
2012-03-08 11:49:17 +00:00
pldat = netdev_priv ( ndev ) ;
pldat - > pdev = pdev ;
pldat - > ndev = ndev ;
spin_lock_init ( & pldat - > lock ) ;
/* Save resources */
ndev - > irq = irq ;
/* Get clock for the device */
2018-10-19 02:25:11 +03:00
pldat - > clk = clk_get ( dev , NULL ) ;
2012-03-08 11:49:17 +00:00
if ( IS_ERR ( pldat - > clk ) ) {
2018-10-19 02:25:11 +03:00
dev_err ( dev , " error getting clock. \n " ) ;
2012-03-08 11:49:17 +00:00
ret = PTR_ERR ( pldat - > clk ) ;
goto err_out_free_dev ;
}
/* Enable network clock */
2016-08-23 09:48:20 -03:00
ret = clk_prepare_enable ( pldat - > clk ) ;
if ( ret )
goto err_out_clk_put ;
2012-03-08 11:49:17 +00:00
/* Map IO space */
2014-06-03 12:45:59 +02:00
pldat - > net_base = ioremap ( res - > start , resource_size ( res ) ) ;
2012-03-08 11:49:17 +00:00
if ( ! pldat - > net_base ) {
2018-10-19 02:25:11 +03:00
dev_err ( dev , " failed to map registers \n " ) ;
2012-03-08 11:49:17 +00:00
ret = - ENOMEM ;
goto err_out_disable_clocks ;
}
ret = request_irq ( ndev - > irq , __lpc_eth_interrupt , 0 ,
ndev - > name , ndev ) ;
if ( ret ) {
2018-10-19 02:25:11 +03:00
dev_err ( dev , " error requesting interrupt. \n " ) ;
2012-03-08 11:49:17 +00:00
goto err_out_iounmap ;
}
/* Setup driver functions */
ndev - > netdev_ops = & lpc_netdev_ops ;
ndev - > ethtool_ops = & lpc_eth_ethtool_ops ;
ndev - > watchdog_timeo = msecs_to_jiffies ( 2500 ) ;
/* Get size of DMA buffers/descriptors region */
pldat - > dma_buff_size = ( ENET_TX_DESC + ENET_RX_DESC ) * ( ENET_MAXF_SIZE +
sizeof ( struct txrx_desc_t ) + sizeof ( struct rx_status_t ) ) ;
pldat - > dma_buff_base_v = 0 ;
2018-10-19 02:25:11 +03:00
if ( use_iram_for_net ( dev ) ) {
2012-04-22 12:01:19 +02:00
dma_handle = LPC32XX_IRAM_BASE ;
2012-03-08 11:49:17 +00:00
if ( pldat - > dma_buff_size < = lpc32xx_return_iram_size ( ) )
pldat - > dma_buff_base_v =
2012-04-22 12:01:19 +02:00
io_p2v ( LPC32XX_IRAM_BASE ) ;
2012-03-08 11:49:17 +00:00
else
netdev_err ( ndev ,
" IRAM not big enough for net buffers, using SDRAM instead. \n " ) ;
}
if ( pldat - > dma_buff_base_v = = 0 ) {
2018-10-19 02:25:11 +03:00
ret = dma_coerce_mask_and_coherent ( dev , DMA_BIT_MASK ( 32 ) ) ;
2013-06-27 14:03:13 +01:00
if ( ret )
goto err_out_free_irq ;
2012-03-08 11:49:17 +00:00
pldat - > dma_buff_size = PAGE_ALIGN ( pldat - > dma_buff_size ) ;
/* Allocate a chunk of memory for the DMA ethernet buffers
and descriptors */
pldat - > dma_buff_base_v =
2018-10-19 02:25:11 +03:00
dma_alloc_coherent ( dev ,
2012-03-08 11:49:17 +00:00
pldat - > dma_buff_size , & dma_handle ,
GFP_KERNEL ) ;
if ( pldat - > dma_buff_base_v = = NULL ) {
ret = - ENOMEM ;
goto err_out_free_irq ;
}
}
pldat - > dma_buff_base_p = dma_handle ;
2014-06-03 12:45:59 +02:00
netdev_dbg ( ndev , " IO address space :%pR \n " , res ) ;
netdev_dbg ( ndev , " IO address size :%d \n " , resource_size ( res ) ) ;
2012-06-18 10:14:42 +00:00
netdev_dbg ( ndev , " IO address (mapped) :0x%p \n " ,
2012-03-08 11:49:17 +00:00
pldat - > net_base ) ;
netdev_dbg ( ndev , " IRQ number :%d \n " , ndev - > irq ) ;
netdev_dbg ( ndev , " DMA buffer size :%d \n " , pldat - > dma_buff_size ) ;
netdev_dbg ( ndev , " DMA buffer P address :0x%08x \n " ,
pldat - > dma_buff_base_p ) ;
netdev_dbg ( ndev , " DMA buffer V address :0x%p \n " ,
pldat - > dma_buff_base_v ) ;
/* Get MAC address from current HW setting (POR state is all zeros) */
__lpc_get_mac ( pldat , ndev - > dev_addr ) ;
if ( ! is_valid_ether_addr ( ndev - > dev_addr ) ) {
2018-10-19 02:25:11 +03:00
const char * macaddr = of_get_mac_address ( np ) ;
2012-03-08 11:49:17 +00:00
if ( macaddr )
memcpy ( ndev - > dev_addr , macaddr , ETH_ALEN ) ;
}
if ( ! is_valid_ether_addr ( ndev - > dev_addr ) )
2012-03-28 12:36:26 +00:00
eth_hw_addr_random ( ndev ) ;
2012-03-08 11:49:17 +00:00
/* Reset the ethernet controller */
__lpc_eth_reset ( pldat ) ;
/* then shut everything down to save power */
__lpc_eth_shutdown ( pldat ) ;
/* Set default parameters */
pldat - > msg_enable = NETIF_MSG_LINK ;
/* Force an MII interface reset and clock setup */
__lpc_mii_mngt_reset ( pldat ) ;
/* Force default PHY interface setup in chip, this will probably be
changed by the PHY driver */
pldat - > link = 0 ;
pldat - > speed = 100 ;
pldat - > duplex = DUPLEX_FULL ;
__lpc_params_setup ( pldat ) ;
netif_napi_add ( ndev , & pldat - > napi , lpc_eth_poll , NAPI_WEIGHT ) ;
ret = register_netdev ( ndev ) ;
if ( ret ) {
2018-10-19 02:25:11 +03:00
dev_err ( dev , " Cannot register net device, aborting. \n " ) ;
2012-03-08 11:49:17 +00:00
goto err_out_dma_unmap ;
}
platform_set_drvdata ( pdev , ndev ) ;
2013-03-20 02:21:48 +00:00
ret = lpc_mii_init ( pldat ) ;
if ( ret )
2012-03-08 11:49:17 +00:00
goto err_out_unregister_netdev ;
netdev_info ( ndev , " LPC mac at 0x%08x irq %d \n " ,
res - > start , ndev - > irq ) ;
2018-10-19 02:25:11 +03:00
device_init_wakeup ( dev , 1 ) ;
device_set_wakeup_enable ( dev , 0 ) ;
2012-03-08 11:49:17 +00:00
return 0 ;
err_out_unregister_netdev :
unregister_netdev ( ndev ) ;
err_out_dma_unmap :
2018-10-19 02:25:11 +03:00
if ( ! use_iram_for_net ( dev ) | |
2012-03-08 11:49:17 +00:00
pldat - > dma_buff_size > lpc32xx_return_iram_size ( ) )
2018-10-19 02:25:11 +03:00
dma_free_coherent ( dev , pldat - > dma_buff_size ,
2012-03-08 11:49:17 +00:00
pldat - > dma_buff_base_v ,
pldat - > dma_buff_base_p ) ;
err_out_free_irq :
free_irq ( ndev - > irq , ndev ) ;
err_out_iounmap :
iounmap ( pldat - > net_base ) ;
err_out_disable_clocks :
2015-10-01 00:37:43 +03:00
clk_disable_unprepare ( pldat - > clk ) ;
2016-08-23 09:48:20 -03:00
err_out_clk_put :
2012-03-08 11:49:17 +00:00
clk_put ( pldat - > clk ) ;
err_out_free_dev :
free_netdev ( ndev ) ;
err_exit :
pr_err ( " %s: not found (%d). \n " , MODNAME , ret ) ;
return ret ;
}
static int lpc_eth_drv_remove ( struct platform_device * pdev )
{
struct net_device * ndev = platform_get_drvdata ( pdev ) ;
struct netdata_local * pldat = netdev_priv ( ndev ) ;
unregister_netdev ( ndev ) ;
2012-04-22 12:01:19 +02:00
if ( ! use_iram_for_net ( & pldat - > pdev - > dev ) | |
2012-03-08 11:49:17 +00:00
pldat - > dma_buff_size > lpc32xx_return_iram_size ( ) )
dma_free_coherent ( & pldat - > pdev - > dev , pldat - > dma_buff_size ,
pldat - > dma_buff_base_v ,
pldat - > dma_buff_base_p ) ;
free_irq ( ndev - > irq , ndev ) ;
iounmap ( pldat - > net_base ) ;
2012-10-28 06:12:00 +00:00
mdiobus_unregister ( pldat - > mii_bus ) ;
2012-03-08 11:49:17 +00:00
mdiobus_free ( pldat - > mii_bus ) ;
2015-10-01 00:37:43 +03:00
clk_disable_unprepare ( pldat - > clk ) ;
2012-03-08 11:49:17 +00:00
clk_put ( pldat - > clk ) ;
free_netdev ( ndev ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int lpc_eth_drv_suspend ( struct platform_device * pdev ,
pm_message_t state )
{
struct net_device * ndev = platform_get_drvdata ( pdev ) ;
struct netdata_local * pldat = netdev_priv ( ndev ) ;
if ( device_may_wakeup ( & pdev - > dev ) )
enable_irq_wake ( ndev - > irq ) ;
if ( ndev ) {
if ( netif_running ( ndev ) ) {
netif_device_detach ( ndev ) ;
__lpc_eth_shutdown ( pldat ) ;
2015-10-01 00:37:43 +03:00
clk_disable_unprepare ( pldat - > clk ) ;
2012-03-08 11:49:17 +00:00
/*
* Reset again now clock is disable to be sure
* EMC_MDC is down
*/
__lpc_eth_reset ( pldat ) ;
}
}
return 0 ;
}
static int lpc_eth_drv_resume ( struct platform_device * pdev )
{
struct net_device * ndev = platform_get_drvdata ( pdev ) ;
struct netdata_local * pldat ;
if ( device_may_wakeup ( & pdev - > dev ) )
disable_irq_wake ( ndev - > irq ) ;
if ( ndev ) {
if ( netif_running ( ndev ) ) {
pldat = netdev_priv ( ndev ) ;
/* Enable interface clock */
clk_enable ( pldat - > clk ) ;
/* Reset and initialize */
__lpc_eth_reset ( pldat ) ;
__lpc_eth_init ( pldat ) ;
netif_device_attach ( ndev ) ;
}
}
return 0 ;
}
# endif
2012-04-22 12:01:19 +02:00
static const struct of_device_id lpc_eth_match [ ] = {
{ . compatible = " nxp,lpc-eth " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , lpc_eth_match ) ;
2012-03-08 11:49:17 +00:00
static struct platform_driver lpc_eth_driver = {
. probe = lpc_eth_drv_probe ,
2012-12-03 09:23:21 -05:00
. remove = lpc_eth_drv_remove ,
2012-03-08 11:49:17 +00:00
# ifdef CONFIG_PM
. suspend = lpc_eth_drv_suspend ,
. resume = lpc_eth_drv_resume ,
# endif
. driver = {
. name = MODNAME ,
2018-10-19 01:58:41 +03:00
. of_match_table = lpc_eth_match ,
2012-03-08 11:49:17 +00:00
} ,
} ;
module_platform_driver ( lpc_eth_driver ) ;
MODULE_AUTHOR ( " Kevin Wells <kevin.wells@nxp.com> " ) ;
MODULE_AUTHOR ( " Roland Stigge <stigge@antcom.de> " ) ;
MODULE_DESCRIPTION ( " LPC Ethernet Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;