162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Faraday FTMAC100 10/100 Ethernet 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright 2009-2011 Faraday Technology 662306a36Sopenharmony_ci * Po-Yu Chuang <ratbert@faraday-tech.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/etherdevice.h> 1362306a36Sopenharmony_ci#include <linux/ethtool.h> 1462306a36Sopenharmony_ci#include <linux/if_ether.h> 1562306a36Sopenharmony_ci#include <linux/if_vlan.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/mii.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2262306a36Sopenharmony_ci#include <linux/netdevice.h> 2362306a36Sopenharmony_ci#include <linux/platform_device.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "ftmac100.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DRV_NAME "ftmac100" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define RX_QUEUE_ENTRIES 128 /* must be power of 2 */ 3062306a36Sopenharmony_ci#define TX_QUEUE_ENTRIES 16 /* must be power of 2 */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define RX_BUF_SIZE 2044 /* must be smaller than 0x7ff */ 3362306a36Sopenharmony_ci#define MAX_PKT_SIZE RX_BUF_SIZE /* multi-segment not supported */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#if MAX_PKT_SIZE > 0x7ff 3662306a36Sopenharmony_ci#error invalid MAX_PKT_SIZE 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#if RX_BUF_SIZE > 0x7ff || RX_BUF_SIZE > PAGE_SIZE 4062306a36Sopenharmony_ci#error invalid RX_BUF_SIZE 4162306a36Sopenharmony_ci#endif 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/****************************************************************************** 4462306a36Sopenharmony_ci * private data 4562306a36Sopenharmony_ci *****************************************************************************/ 4662306a36Sopenharmony_cistruct ftmac100_descs { 4762306a36Sopenharmony_ci struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; 4862306a36Sopenharmony_ci struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES]; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct ftmac100 { 5262306a36Sopenharmony_ci struct resource *res; 5362306a36Sopenharmony_ci void __iomem *base; 5462306a36Sopenharmony_ci int irq; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci struct ftmac100_descs *descs; 5762306a36Sopenharmony_ci dma_addr_t descs_dma_addr; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci unsigned int rx_pointer; 6062306a36Sopenharmony_ci unsigned int tx_clean_pointer; 6162306a36Sopenharmony_ci unsigned int tx_pointer; 6262306a36Sopenharmony_ci unsigned int tx_pending; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci spinlock_t tx_lock; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci struct net_device *netdev; 6762306a36Sopenharmony_ci struct device *dev; 6862306a36Sopenharmony_ci struct napi_struct napi; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci struct mii_if_info mii; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int ftmac100_alloc_rx_page(struct ftmac100 *priv, 7462306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes, gfp_t gfp); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/****************************************************************************** 7762306a36Sopenharmony_ci * internal functions (hardware register access) 7862306a36Sopenharmony_ci *****************************************************************************/ 7962306a36Sopenharmony_ci#define INT_MASK_ALL_ENABLED (FTMAC100_INT_RPKT_FINISH | \ 8062306a36Sopenharmony_ci FTMAC100_INT_NORXBUF | \ 8162306a36Sopenharmony_ci FTMAC100_INT_XPKT_OK | \ 8262306a36Sopenharmony_ci FTMAC100_INT_XPKT_LOST | \ 8362306a36Sopenharmony_ci FTMAC100_INT_RPKT_LOST | \ 8462306a36Sopenharmony_ci FTMAC100_INT_AHB_ERR | \ 8562306a36Sopenharmony_ci FTMAC100_INT_PHYSTS_CHG) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define INT_MASK_ALL_DISABLED 0 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void ftmac100_enable_all_int(struct ftmac100 *priv) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTMAC100_OFFSET_IMR); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void ftmac100_disable_all_int(struct ftmac100 *priv) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci iowrite32(INT_MASK_ALL_DISABLED, priv->base + FTMAC100_OFFSET_IMR); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void ftmac100_set_rx_ring_base(struct ftmac100 *priv, dma_addr_t addr) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void ftmac100_set_tx_ring_base(struct ftmac100 *priv, dma_addr_t addr) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void ftmac100_txdma_start_polling(struct ftmac100 *priv) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int ftmac100_reset(struct ftmac100 *priv) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* NOTE: reset clears all registers */ 12062306a36Sopenharmony_ci iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 12362306a36Sopenharmony_ci unsigned int maccr; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR); 12662306a36Sopenharmony_ci if (!(maccr & FTMAC100_MACCR_SW_RST)) { 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * FTMAC100_MACCR_SW_RST cleared does not indicate 12962306a36Sopenharmony_ci * that hardware reset completed (what the f*ck). 13062306a36Sopenharmony_ci * We still need to wait for a while. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci udelay(500); 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci udelay(1000); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci netdev_err(netdev, "software reset failed\n"); 14062306a36Sopenharmony_ci return -EIO; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci unsigned int maddr = mac[0] << 8 | mac[1]; 14662306a36Sopenharmony_ci unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci iowrite32(maddr, priv->base + FTMAC100_OFFSET_MAC_MADR); 14962306a36Sopenharmony_ci iowrite32(laddr, priv->base + FTMAC100_OFFSET_MAC_LADR); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void ftmac100_setup_mc_ht(struct ftmac100 *priv) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct netdev_hw_addr *ha; 15562306a36Sopenharmony_ci u64 maht = 0; /* Multicast Address Hash Table */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, priv->netdev) { 15862306a36Sopenharmony_ci u32 hash = ether_crc(ETH_ALEN, ha->addr) >> 26; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci maht |= BIT_ULL(hash); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci iowrite32(lower_32_bits(maht), priv->base + FTMAC100_OFFSET_MAHT0); 16462306a36Sopenharmony_ci iowrite32(upper_32_bits(maht), priv->base + FTMAC100_OFFSET_MAHT1); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void ftmac100_set_rx_bits(struct ftmac100 *priv, unsigned int *maccr) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Clear all */ 17262306a36Sopenharmony_ci *maccr &= ~(FTMAC100_MACCR_RCV_ALL | FTMAC100_MACCR_RX_MULTIPKT | 17362306a36Sopenharmony_ci FTMAC100_MACCR_HT_MULTI_EN); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Set the requested bits */ 17662306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) 17762306a36Sopenharmony_ci *maccr |= FTMAC100_MACCR_RCV_ALL; 17862306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) 17962306a36Sopenharmony_ci *maccr |= FTMAC100_MACCR_RX_MULTIPKT; 18062306a36Sopenharmony_ci else if (netdev_mc_count(netdev)) { 18162306a36Sopenharmony_ci *maccr |= FTMAC100_MACCR_HT_MULTI_EN; 18262306a36Sopenharmony_ci ftmac100_setup_mc_ht(priv); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#define MACCR_ENABLE_ALL (FTMAC100_MACCR_XMT_EN | \ 18762306a36Sopenharmony_ci FTMAC100_MACCR_RCV_EN | \ 18862306a36Sopenharmony_ci FTMAC100_MACCR_XDMA_EN | \ 18962306a36Sopenharmony_ci FTMAC100_MACCR_RDMA_EN | \ 19062306a36Sopenharmony_ci FTMAC100_MACCR_CRC_APD | \ 19162306a36Sopenharmony_ci FTMAC100_MACCR_FULLDUP | \ 19262306a36Sopenharmony_ci FTMAC100_MACCR_RX_RUNT | \ 19362306a36Sopenharmony_ci FTMAC100_MACCR_RX_BROADPKT) 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int ftmac100_start_hw(struct ftmac100 *priv) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 19862306a36Sopenharmony_ci unsigned int maccr = MACCR_ENABLE_ALL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (ftmac100_reset(priv)) 20162306a36Sopenharmony_ci return -EIO; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* setup ring buffer base registers */ 20462306a36Sopenharmony_ci ftmac100_set_rx_ring_base(priv, 20562306a36Sopenharmony_ci priv->descs_dma_addr + 20662306a36Sopenharmony_ci offsetof(struct ftmac100_descs, rxdes)); 20762306a36Sopenharmony_ci ftmac100_set_tx_ring_base(priv, 20862306a36Sopenharmony_ci priv->descs_dma_addr + 20962306a36Sopenharmony_ci offsetof(struct ftmac100_descs, txdes)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci iowrite32(FTMAC100_APTC_RXPOLL_CNT(1), priv->base + FTMAC100_OFFSET_APTC); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ftmac100_set_mac(priv, netdev->dev_addr); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* See ftmac100_change_mtu() */ 21662306a36Sopenharmony_ci if (netdev->mtu > ETH_DATA_LEN) 21762306a36Sopenharmony_ci maccr |= FTMAC100_MACCR_RX_FTL; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci ftmac100_set_rx_bits(priv, &maccr); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void ftmac100_stop_hw(struct ftmac100 *priv) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/****************************************************************************** 23162306a36Sopenharmony_ci * internal functions (receive descriptor) 23262306a36Sopenharmony_ci *****************************************************************************/ 23362306a36Sopenharmony_cistatic bool ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FRS); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic bool ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_LRS); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic bool ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci /* clear status bits */ 25162306a36Sopenharmony_ci rxdes->rxdes0 = cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic bool ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ERR); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_CRC_ERR); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RUNT); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic bool ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ODD_NB); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic unsigned int ftmac100_rxdes_frame_length(struct ftmac100_rxdes *rxdes) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RFL; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic bool ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_MULTICAST); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes, 28562306a36Sopenharmony_ci unsigned int size) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci rxdes->rxdes1 &= cpu_to_le32(FTMAC100_RXDES1_EDORR); 28862306a36Sopenharmony_ci rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_RXBUF_SIZE(size)); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_EDORR); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes, 29762306a36Sopenharmony_ci dma_addr_t addr) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci rxdes->rxdes2 = cpu_to_le32(addr); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci return le32_to_cpu(rxdes->rxdes2); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* 30862306a36Sopenharmony_ci * rxdes3 is not used by hardware. We use it to keep track of page. 30962306a36Sopenharmony_ci * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_cistatic void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci rxdes->rxdes3 = (unsigned int)page; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci return (struct page *)rxdes->rxdes3; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci/****************************************************************************** 32262306a36Sopenharmony_ci * internal functions (receive) 32362306a36Sopenharmony_ci *****************************************************************************/ 32462306a36Sopenharmony_cistatic int ftmac100_next_rx_pointer(int pointer) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void ftmac100_rx_pointer_advance(struct ftmac100 *priv) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic struct ftmac100_rxdes *ftmac100_current_rxdes(struct ftmac100 *priv) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci return &priv->descs->rxdes[priv->rx_pointer]; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic struct ftmac100_rxdes * 34062306a36Sopenharmony_ciftmac100_rx_locate_first_segment(struct ftmac100 *priv) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci while (!ftmac100_rxdes_owned_by_dma(rxdes)) { 34562306a36Sopenharmony_ci if (ftmac100_rxdes_first_segment(rxdes)) 34662306a36Sopenharmony_ci return rxdes; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ftmac100_rxdes_set_dma_own(rxdes); 34962306a36Sopenharmony_ci ftmac100_rx_pointer_advance(priv); 35062306a36Sopenharmony_ci rxdes = ftmac100_current_rxdes(priv); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return NULL; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic bool ftmac100_rx_packet_error(struct ftmac100 *priv, 35762306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 36062306a36Sopenharmony_ci bool error = false; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (unlikely(ftmac100_rxdes_rx_error(rxdes))) { 36362306a36Sopenharmony_ci if (net_ratelimit()) 36462306a36Sopenharmony_ci netdev_info(netdev, "rx err\n"); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci netdev->stats.rx_errors++; 36762306a36Sopenharmony_ci error = true; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (unlikely(ftmac100_rxdes_crc_error(rxdes))) { 37162306a36Sopenharmony_ci if (net_ratelimit()) 37262306a36Sopenharmony_ci netdev_info(netdev, "rx crc err\n"); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci netdev->stats.rx_crc_errors++; 37562306a36Sopenharmony_ci error = true; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (unlikely(ftmac100_rxdes_runt(rxdes))) { 37962306a36Sopenharmony_ci if (net_ratelimit()) 38062306a36Sopenharmony_ci netdev_info(netdev, "rx runt\n"); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci netdev->stats.rx_length_errors++; 38362306a36Sopenharmony_ci error = true; 38462306a36Sopenharmony_ci } else if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) { 38562306a36Sopenharmony_ci if (net_ratelimit()) 38662306a36Sopenharmony_ci netdev_info(netdev, "rx odd nibble\n"); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci netdev->stats.rx_length_errors++; 38962306a36Sopenharmony_ci error = true; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci * FTMAC100_RXDES0_FTL is not an error, it just indicates that the 39362306a36Sopenharmony_ci * frame is longer than 1518 octets. Receiving these is possible when 39462306a36Sopenharmony_ci * we told the hardware not to drop them, via FTMAC100_MACCR_RX_FTL. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return error; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void ftmac100_rx_drop_packet(struct ftmac100 *priv) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 40362306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv); 40462306a36Sopenharmony_ci bool done = false; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (net_ratelimit()) 40762306a36Sopenharmony_ci netdev_dbg(netdev, "drop packet %p\n", rxdes); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci do { 41062306a36Sopenharmony_ci if (ftmac100_rxdes_last_segment(rxdes)) 41162306a36Sopenharmony_ci done = true; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ftmac100_rxdes_set_dma_own(rxdes); 41462306a36Sopenharmony_ci ftmac100_rx_pointer_advance(priv); 41562306a36Sopenharmony_ci rxdes = ftmac100_current_rxdes(priv); 41662306a36Sopenharmony_ci } while (!done && !ftmac100_rxdes_owned_by_dma(rxdes)); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci netdev->stats.rx_dropped++; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 42462306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes; 42562306a36Sopenharmony_ci struct sk_buff *skb; 42662306a36Sopenharmony_ci struct page *page; 42762306a36Sopenharmony_ci dma_addr_t map; 42862306a36Sopenharmony_ci int length; 42962306a36Sopenharmony_ci bool ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci rxdes = ftmac100_rx_locate_first_segment(priv); 43262306a36Sopenharmony_ci if (!rxdes) 43362306a36Sopenharmony_ci return false; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) { 43662306a36Sopenharmony_ci ftmac100_rx_drop_packet(priv); 43762306a36Sopenharmony_ci return true; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* We don't support multi-segment packets for now, so drop them. */ 44162306a36Sopenharmony_ci ret = ftmac100_rxdes_last_segment(rxdes); 44262306a36Sopenharmony_ci if (unlikely(!ret)) { 44362306a36Sopenharmony_ci netdev->stats.rx_length_errors++; 44462306a36Sopenharmony_ci ftmac100_rx_drop_packet(priv); 44562306a36Sopenharmony_ci return true; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* start processing */ 44962306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(netdev, 128); 45062306a36Sopenharmony_ci if (unlikely(!skb)) { 45162306a36Sopenharmony_ci if (net_ratelimit()) 45262306a36Sopenharmony_ci netdev_err(netdev, "rx skb alloc failed\n"); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci ftmac100_rx_drop_packet(priv); 45562306a36Sopenharmony_ci return true; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (unlikely(ftmac100_rxdes_multicast(rxdes))) 45962306a36Sopenharmony_ci netdev->stats.multicast++; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci map = ftmac100_rxdes_get_dma_addr(rxdes); 46262306a36Sopenharmony_ci dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci length = ftmac100_rxdes_frame_length(rxdes); 46562306a36Sopenharmony_ci page = ftmac100_rxdes_get_page(rxdes); 46662306a36Sopenharmony_ci skb_fill_page_desc(skb, 0, page, 0, length); 46762306a36Sopenharmony_ci skb->len += length; 46862306a36Sopenharmony_ci skb->data_len += length; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (length > 128) { 47162306a36Sopenharmony_ci skb->truesize += PAGE_SIZE; 47262306a36Sopenharmony_ci /* We pull the minimum amount into linear part */ 47362306a36Sopenharmony_ci __pskb_pull_tail(skb, ETH_HLEN); 47462306a36Sopenharmony_ci } else { 47562306a36Sopenharmony_ci /* Small frames are copied into linear part to free one page */ 47662306a36Sopenharmony_ci __pskb_pull_tail(skb, length); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ftmac100_rx_pointer_advance(priv); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci netdev->stats.rx_packets++; 48562306a36Sopenharmony_ci netdev->stats.rx_bytes += skb->len; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* push packet to protocol stack */ 48862306a36Sopenharmony_ci netif_receive_skb(skb); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci (*processed)++; 49162306a36Sopenharmony_ci return true; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/****************************************************************************** 49562306a36Sopenharmony_ci * internal functions (transmit descriptor) 49662306a36Sopenharmony_ci *****************************************************************************/ 49762306a36Sopenharmony_cistatic void ftmac100_txdes_reset(struct ftmac100_txdes *txdes) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci /* clear all except end of ring bit */ 50062306a36Sopenharmony_ci txdes->txdes0 = 0; 50162306a36Sopenharmony_ci txdes->txdes1 &= cpu_to_le32(FTMAC100_TXDES1_EDOTR); 50262306a36Sopenharmony_ci txdes->txdes2 = 0; 50362306a36Sopenharmony_ci txdes->txdes3 = 0; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic bool ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci /* 51462306a36Sopenharmony_ci * Make sure dma own bit will not be set before any other 51562306a36Sopenharmony_ci * descriptor fields. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_ci wmb(); 51862306a36Sopenharmony_ci txdes->txdes0 |= cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic bool ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_EXSCOL); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_cistatic bool ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_LATECOL); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_EDOTR); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_FTS); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_LTS); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXIC); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes, 55262306a36Sopenharmony_ci unsigned int len) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXBUF_SIZE(len)); 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes, 55862306a36Sopenharmony_ci dma_addr_t addr) 55962306a36Sopenharmony_ci{ 56062306a36Sopenharmony_ci txdes->txdes2 = cpu_to_le32(addr); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci return le32_to_cpu(txdes->txdes2); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci/* 56962306a36Sopenharmony_ci * txdes3 is not used by hardware. We use it to keep track of socket buffer. 57062306a36Sopenharmony_ci * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_cistatic void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci txdes->txdes3 = (unsigned int)skb; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci return (struct sk_buff *)txdes->txdes3; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/****************************************************************************** 58362306a36Sopenharmony_ci * internal functions (transmit) 58462306a36Sopenharmony_ci *****************************************************************************/ 58562306a36Sopenharmony_cistatic int ftmac100_next_tx_pointer(int pointer) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic void ftmac100_tx_pointer_advance(struct ftmac100 *priv) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci priv->tx_clean_pointer = ftmac100_next_tx_pointer(priv->tx_clean_pointer); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci return &priv->descs->txdes[priv->tx_pointer]; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic struct ftmac100_txdes *ftmac100_current_clean_txdes(struct ftmac100 *priv) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci return &priv->descs->txdes[priv->tx_clean_pointer]; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic bool ftmac100_tx_complete_packet(struct ftmac100 *priv) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 61362306a36Sopenharmony_ci struct ftmac100_txdes *txdes; 61462306a36Sopenharmony_ci struct sk_buff *skb; 61562306a36Sopenharmony_ci dma_addr_t map; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (priv->tx_pending == 0) 61862306a36Sopenharmony_ci return false; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci txdes = ftmac100_current_clean_txdes(priv); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (ftmac100_txdes_owned_by_dma(txdes)) 62362306a36Sopenharmony_ci return false; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci skb = ftmac100_txdes_get_skb(txdes); 62662306a36Sopenharmony_ci map = ftmac100_txdes_get_dma_addr(txdes); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (unlikely(ftmac100_txdes_excessive_collision(txdes) || 62962306a36Sopenharmony_ci ftmac100_txdes_late_collision(txdes))) { 63062306a36Sopenharmony_ci /* 63162306a36Sopenharmony_ci * packet transmitted to ethernet lost due to late collision 63262306a36Sopenharmony_ci * or excessive collision 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci netdev->stats.tx_aborted_errors++; 63562306a36Sopenharmony_ci } else { 63662306a36Sopenharmony_ci netdev->stats.tx_packets++; 63762306a36Sopenharmony_ci netdev->stats.tx_bytes += skb->len; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); 64162306a36Sopenharmony_ci dev_kfree_skb(skb); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci ftmac100_txdes_reset(txdes); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci ftmac100_tx_clean_pointer_advance(priv); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci spin_lock(&priv->tx_lock); 64862306a36Sopenharmony_ci priv->tx_pending--; 64962306a36Sopenharmony_ci spin_unlock(&priv->tx_lock); 65062306a36Sopenharmony_ci netif_wake_queue(netdev); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return true; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic void ftmac100_tx_complete(struct ftmac100 *priv) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci while (ftmac100_tx_complete_packet(priv)) 65862306a36Sopenharmony_ci ; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic netdev_tx_t ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb, 66262306a36Sopenharmony_ci dma_addr_t map) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 66562306a36Sopenharmony_ci struct ftmac100_txdes *txdes; 66662306a36Sopenharmony_ci unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci txdes = ftmac100_current_txdes(priv); 66962306a36Sopenharmony_ci ftmac100_tx_pointer_advance(priv); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* setup TX descriptor */ 67262306a36Sopenharmony_ci ftmac100_txdes_set_skb(txdes, skb); 67362306a36Sopenharmony_ci ftmac100_txdes_set_dma_addr(txdes, map); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ftmac100_txdes_set_first_segment(txdes); 67662306a36Sopenharmony_ci ftmac100_txdes_set_last_segment(txdes); 67762306a36Sopenharmony_ci ftmac100_txdes_set_txint(txdes); 67862306a36Sopenharmony_ci ftmac100_txdes_set_buffer_size(txdes, len); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci spin_lock(&priv->tx_lock); 68162306a36Sopenharmony_ci priv->tx_pending++; 68262306a36Sopenharmony_ci if (priv->tx_pending == TX_QUEUE_ENTRIES) 68362306a36Sopenharmony_ci netif_stop_queue(netdev); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* start transmit */ 68662306a36Sopenharmony_ci ftmac100_txdes_set_dma_own(txdes); 68762306a36Sopenharmony_ci spin_unlock(&priv->tx_lock); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci ftmac100_txdma_start_polling(priv); 69062306a36Sopenharmony_ci return NETDEV_TX_OK; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/****************************************************************************** 69462306a36Sopenharmony_ci * internal functions (buffer) 69562306a36Sopenharmony_ci *****************************************************************************/ 69662306a36Sopenharmony_cistatic int ftmac100_alloc_rx_page(struct ftmac100 *priv, 69762306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes, gfp_t gfp) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 70062306a36Sopenharmony_ci struct page *page; 70162306a36Sopenharmony_ci dma_addr_t map; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci page = alloc_page(gfp); 70462306a36Sopenharmony_ci if (!page) { 70562306a36Sopenharmony_ci if (net_ratelimit()) 70662306a36Sopenharmony_ci netdev_err(netdev, "failed to allocate rx page\n"); 70762306a36Sopenharmony_ci return -ENOMEM; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); 71162306a36Sopenharmony_ci if (unlikely(dma_mapping_error(priv->dev, map))) { 71262306a36Sopenharmony_ci if (net_ratelimit()) 71362306a36Sopenharmony_ci netdev_err(netdev, "failed to map rx page\n"); 71462306a36Sopenharmony_ci __free_page(page); 71562306a36Sopenharmony_ci return -ENOMEM; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci ftmac100_rxdes_set_page(rxdes, page); 71962306a36Sopenharmony_ci ftmac100_rxdes_set_dma_addr(rxdes, map); 72062306a36Sopenharmony_ci ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE); 72162306a36Sopenharmony_ci ftmac100_rxdes_set_dma_own(rxdes); 72262306a36Sopenharmony_ci return 0; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void ftmac100_free_buffers(struct ftmac100 *priv) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci int i; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci for (i = 0; i < RX_QUEUE_ENTRIES; i++) { 73062306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; 73162306a36Sopenharmony_ci struct page *page = ftmac100_rxdes_get_page(rxdes); 73262306a36Sopenharmony_ci dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!page) 73562306a36Sopenharmony_ci continue; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); 73862306a36Sopenharmony_ci __free_page(page); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci for (i = 0; i < TX_QUEUE_ENTRIES; i++) { 74262306a36Sopenharmony_ci struct ftmac100_txdes *txdes = &priv->descs->txdes[i]; 74362306a36Sopenharmony_ci struct sk_buff *skb = ftmac100_txdes_get_skb(txdes); 74462306a36Sopenharmony_ci dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (!skb) 74762306a36Sopenharmony_ci continue; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); 75062306a36Sopenharmony_ci dev_kfree_skb(skb); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci dma_free_coherent(priv->dev, sizeof(struct ftmac100_descs), 75462306a36Sopenharmony_ci priv->descs, priv->descs_dma_addr); 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_cistatic int ftmac100_alloc_buffers(struct ftmac100 *priv) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci int i; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci priv->descs = dma_alloc_coherent(priv->dev, 76262306a36Sopenharmony_ci sizeof(struct ftmac100_descs), 76362306a36Sopenharmony_ci &priv->descs_dma_addr, GFP_KERNEL); 76462306a36Sopenharmony_ci if (!priv->descs) 76562306a36Sopenharmony_ci return -ENOMEM; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* initialize RX ring */ 76862306a36Sopenharmony_ci ftmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci for (i = 0; i < RX_QUEUE_ENTRIES; i++) { 77162306a36Sopenharmony_ci struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (ftmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) 77462306a36Sopenharmony_ci goto err; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* initialize TX ring */ 77862306a36Sopenharmony_ci ftmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]); 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cierr: 78262306a36Sopenharmony_ci ftmac100_free_buffers(priv); 78362306a36Sopenharmony_ci return -ENOMEM; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci/****************************************************************************** 78762306a36Sopenharmony_ci * struct mii_if_info functions 78862306a36Sopenharmony_ci *****************************************************************************/ 78962306a36Sopenharmony_cistatic int ftmac100_mdio_read(struct net_device *netdev, int phy_id, int reg) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 79262306a36Sopenharmony_ci unsigned int phycr; 79362306a36Sopenharmony_ci int i; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci phycr = FTMAC100_PHYCR_PHYAD(phy_id) | 79662306a36Sopenharmony_ci FTMAC100_PHYCR_REGAD(reg) | 79762306a36Sopenharmony_ci FTMAC100_PHYCR_MIIRD; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 80262306a36Sopenharmony_ci phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci if ((phycr & FTMAC100_PHYCR_MIIRD) == 0) 80562306a36Sopenharmony_ci return phycr & FTMAC100_PHYCR_MIIRDATA; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci udelay(100); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci netdev_err(netdev, "mdio read timed out\n"); 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci} 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_cistatic void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg, 81562306a36Sopenharmony_ci int data) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 81862306a36Sopenharmony_ci unsigned int phycr; 81962306a36Sopenharmony_ci int i; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci phycr = FTMAC100_PHYCR_PHYAD(phy_id) | 82262306a36Sopenharmony_ci FTMAC100_PHYCR_REGAD(reg) | 82362306a36Sopenharmony_ci FTMAC100_PHYCR_MIIWR; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci data = FTMAC100_PHYWDATA_MIIWDATA(data); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA); 82862306a36Sopenharmony_ci iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 83162306a36Sopenharmony_ci phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if ((phycr & FTMAC100_PHYCR_MIIWR) == 0) 83462306a36Sopenharmony_ci return; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci udelay(100); 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci netdev_err(netdev, "mdio write timed out\n"); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/****************************************************************************** 84362306a36Sopenharmony_ci * struct ethtool_ops functions 84462306a36Sopenharmony_ci *****************************************************************************/ 84562306a36Sopenharmony_cistatic void ftmac100_get_drvinfo(struct net_device *netdev, 84662306a36Sopenharmony_ci struct ethtool_drvinfo *info) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 84962306a36Sopenharmony_ci strscpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info)); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic int ftmac100_get_link_ksettings(struct net_device *netdev, 85362306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci mii_ethtool_get_link_ksettings(&priv->mii, cmd); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic int ftmac100_set_link_ksettings(struct net_device *netdev, 86362306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 86662306a36Sopenharmony_ci return mii_ethtool_set_link_ksettings(&priv->mii, cmd); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic int ftmac100_nway_reset(struct net_device *netdev) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 87262306a36Sopenharmony_ci return mii_nway_restart(&priv->mii); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic u32 ftmac100_get_link(struct net_device *netdev) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 87862306a36Sopenharmony_ci return mii_link_ok(&priv->mii); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic const struct ethtool_ops ftmac100_ethtool_ops = { 88262306a36Sopenharmony_ci .get_drvinfo = ftmac100_get_drvinfo, 88362306a36Sopenharmony_ci .nway_reset = ftmac100_nway_reset, 88462306a36Sopenharmony_ci .get_link = ftmac100_get_link, 88562306a36Sopenharmony_ci .get_link_ksettings = ftmac100_get_link_ksettings, 88662306a36Sopenharmony_ci .set_link_ksettings = ftmac100_set_link_ksettings, 88762306a36Sopenharmony_ci}; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/****************************************************************************** 89062306a36Sopenharmony_ci * interrupt handler 89162306a36Sopenharmony_ci *****************************************************************************/ 89262306a36Sopenharmony_cistatic irqreturn_t ftmac100_interrupt(int irq, void *dev_id) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct net_device *netdev = dev_id; 89562306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* Disable interrupts for polling */ 89862306a36Sopenharmony_ci ftmac100_disable_all_int(priv); 89962306a36Sopenharmony_ci if (likely(netif_running(netdev))) 90062306a36Sopenharmony_ci napi_schedule(&priv->napi); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return IRQ_HANDLED; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/****************************************************************************** 90662306a36Sopenharmony_ci * struct napi_struct functions 90762306a36Sopenharmony_ci *****************************************************************************/ 90862306a36Sopenharmony_cistatic int ftmac100_poll(struct napi_struct *napi, int budget) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct ftmac100 *priv = container_of(napi, struct ftmac100, napi); 91162306a36Sopenharmony_ci struct net_device *netdev = priv->netdev; 91262306a36Sopenharmony_ci unsigned int status; 91362306a36Sopenharmony_ci bool completed = true; 91462306a36Sopenharmony_ci int rx = 0; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci status = ioread32(priv->base + FTMAC100_OFFSET_ISR); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) { 91962306a36Sopenharmony_ci /* 92062306a36Sopenharmony_ci * FTMAC100_INT_RPKT_FINISH: 92162306a36Sopenharmony_ci * RX DMA has received packets into RX buffer successfully 92262306a36Sopenharmony_ci * 92362306a36Sopenharmony_ci * FTMAC100_INT_NORXBUF: 92462306a36Sopenharmony_ci * RX buffer unavailable 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci bool retry; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci do { 92962306a36Sopenharmony_ci retry = ftmac100_rx_packet(priv, &rx); 93062306a36Sopenharmony_ci } while (retry && rx < budget); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (retry && rx == budget) 93362306a36Sopenharmony_ci completed = false; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) { 93762306a36Sopenharmony_ci /* 93862306a36Sopenharmony_ci * FTMAC100_INT_XPKT_OK: 93962306a36Sopenharmony_ci * packet transmitted to ethernet successfully 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * FTMAC100_INT_XPKT_LOST: 94262306a36Sopenharmony_ci * packet transmitted to ethernet lost due to late 94362306a36Sopenharmony_ci * collision or excessive collision 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_ci ftmac100_tx_complete(priv); 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (status & (FTMAC100_INT_NORXBUF | FTMAC100_INT_RPKT_LOST | 94962306a36Sopenharmony_ci FTMAC100_INT_AHB_ERR | FTMAC100_INT_PHYSTS_CHG)) { 95062306a36Sopenharmony_ci if (net_ratelimit()) 95162306a36Sopenharmony_ci netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, 95262306a36Sopenharmony_ci status & FTMAC100_INT_NORXBUF ? "NORXBUF " : "", 95362306a36Sopenharmony_ci status & FTMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", 95462306a36Sopenharmony_ci status & FTMAC100_INT_AHB_ERR ? "AHB_ERR " : "", 95562306a36Sopenharmony_ci status & FTMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : ""); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (status & FTMAC100_INT_NORXBUF) { 95862306a36Sopenharmony_ci /* RX buffer unavailable */ 95962306a36Sopenharmony_ci netdev->stats.rx_over_errors++; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (status & FTMAC100_INT_RPKT_LOST) { 96362306a36Sopenharmony_ci /* received packet lost due to RX FIFO full */ 96462306a36Sopenharmony_ci netdev->stats.rx_fifo_errors++; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (status & FTMAC100_INT_PHYSTS_CHG) { 96862306a36Sopenharmony_ci /* PHY link status change */ 96962306a36Sopenharmony_ci mii_check_link(&priv->mii); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (completed) { 97462306a36Sopenharmony_ci /* stop polling */ 97562306a36Sopenharmony_ci napi_complete(napi); 97662306a36Sopenharmony_ci ftmac100_enable_all_int(priv); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return rx; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci/****************************************************************************** 98362306a36Sopenharmony_ci * struct net_device_ops functions 98462306a36Sopenharmony_ci *****************************************************************************/ 98562306a36Sopenharmony_cistatic int ftmac100_open(struct net_device *netdev) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 98862306a36Sopenharmony_ci int err; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci err = ftmac100_alloc_buffers(priv); 99162306a36Sopenharmony_ci if (err) { 99262306a36Sopenharmony_ci netdev_err(netdev, "failed to allocate buffers\n"); 99362306a36Sopenharmony_ci goto err_alloc; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name, netdev); 99762306a36Sopenharmony_ci if (err) { 99862306a36Sopenharmony_ci netdev_err(netdev, "failed to request irq %d\n", priv->irq); 99962306a36Sopenharmony_ci goto err_irq; 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci priv->rx_pointer = 0; 100362306a36Sopenharmony_ci priv->tx_clean_pointer = 0; 100462306a36Sopenharmony_ci priv->tx_pointer = 0; 100562306a36Sopenharmony_ci priv->tx_pending = 0; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci err = ftmac100_start_hw(priv); 100862306a36Sopenharmony_ci if (err) 100962306a36Sopenharmony_ci goto err_hw; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci napi_enable(&priv->napi); 101262306a36Sopenharmony_ci netif_start_queue(netdev); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci ftmac100_enable_all_int(priv); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cierr_hw: 101962306a36Sopenharmony_ci free_irq(priv->irq, netdev); 102062306a36Sopenharmony_cierr_irq: 102162306a36Sopenharmony_ci ftmac100_free_buffers(priv); 102262306a36Sopenharmony_cierr_alloc: 102362306a36Sopenharmony_ci return err; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic int ftmac100_stop(struct net_device *netdev) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci ftmac100_disable_all_int(priv); 103162306a36Sopenharmony_ci netif_stop_queue(netdev); 103262306a36Sopenharmony_ci napi_disable(&priv->napi); 103362306a36Sopenharmony_ci ftmac100_stop_hw(priv); 103462306a36Sopenharmony_ci free_irq(priv->irq, netdev); 103562306a36Sopenharmony_ci ftmac100_free_buffers(priv); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci return 0; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic netdev_tx_t 104162306a36Sopenharmony_ciftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 104462306a36Sopenharmony_ci dma_addr_t map; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (unlikely(skb->len > MAX_PKT_SIZE)) { 104762306a36Sopenharmony_ci if (net_ratelimit()) 104862306a36Sopenharmony_ci netdev_dbg(netdev, "tx packet too big\n"); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci netdev->stats.tx_dropped++; 105162306a36Sopenharmony_ci dev_kfree_skb(skb); 105262306a36Sopenharmony_ci return NETDEV_TX_OK; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); 105662306a36Sopenharmony_ci if (unlikely(dma_mapping_error(priv->dev, map))) { 105762306a36Sopenharmony_ci /* drop packet */ 105862306a36Sopenharmony_ci if (net_ratelimit()) 105962306a36Sopenharmony_ci netdev_err(netdev, "map socket buffer failed\n"); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci netdev->stats.tx_dropped++; 106262306a36Sopenharmony_ci dev_kfree_skb(skb); 106362306a36Sopenharmony_ci return NETDEV_TX_OK; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci return ftmac100_xmit(priv, skb, map); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci/* optional */ 107062306a36Sopenharmony_cistatic int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 107362306a36Sopenharmony_ci struct mii_ioctl_data *data = if_mii(ifr); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci return generic_mii_ioctl(&priv->mii, data, cmd, NULL); 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic int ftmac100_change_mtu(struct net_device *netdev, int mtu) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 108162306a36Sopenharmony_ci unsigned int maccr; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR); 108462306a36Sopenharmony_ci if (mtu > ETH_DATA_LEN) { 108562306a36Sopenharmony_ci /* process long packets in the driver */ 108662306a36Sopenharmony_ci maccr |= FTMAC100_MACCR_RX_FTL; 108762306a36Sopenharmony_ci } else { 108862306a36Sopenharmony_ci /* Let the controller drop incoming packets greater 108962306a36Sopenharmony_ci * than 1518 (that is 1500 + 14 Ethernet + 4 FCS). 109062306a36Sopenharmony_ci */ 109162306a36Sopenharmony_ci maccr &= ~FTMAC100_MACCR_RX_FTL; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci netdev->mtu = mtu; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci return 0; 109862306a36Sopenharmony_ci} 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_cistatic void ftmac100_set_rx_mode(struct net_device *netdev) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct ftmac100 *priv = netdev_priv(netdev); 110362306a36Sopenharmony_ci unsigned int maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci ftmac100_set_rx_bits(priv, &maccr); 110662306a36Sopenharmony_ci iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic const struct net_device_ops ftmac100_netdev_ops = { 111062306a36Sopenharmony_ci .ndo_open = ftmac100_open, 111162306a36Sopenharmony_ci .ndo_stop = ftmac100_stop, 111262306a36Sopenharmony_ci .ndo_start_xmit = ftmac100_hard_start_xmit, 111362306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 111462306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 111562306a36Sopenharmony_ci .ndo_eth_ioctl = ftmac100_do_ioctl, 111662306a36Sopenharmony_ci .ndo_change_mtu = ftmac100_change_mtu, 111762306a36Sopenharmony_ci .ndo_set_rx_mode = ftmac100_set_rx_mode, 111862306a36Sopenharmony_ci}; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci/****************************************************************************** 112162306a36Sopenharmony_ci * struct platform_driver functions 112262306a36Sopenharmony_ci *****************************************************************************/ 112362306a36Sopenharmony_cistatic int ftmac100_probe(struct platform_device *pdev) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci struct resource *res; 112662306a36Sopenharmony_ci int irq; 112762306a36Sopenharmony_ci struct net_device *netdev; 112862306a36Sopenharmony_ci struct ftmac100 *priv; 112962306a36Sopenharmony_ci int err; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 113262306a36Sopenharmony_ci if (!res) 113362306a36Sopenharmony_ci return -ENXIO; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 113662306a36Sopenharmony_ci if (irq < 0) 113762306a36Sopenharmony_ci return irq; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* setup net_device */ 114062306a36Sopenharmony_ci netdev = alloc_etherdev(sizeof(*priv)); 114162306a36Sopenharmony_ci if (!netdev) { 114262306a36Sopenharmony_ci err = -ENOMEM; 114362306a36Sopenharmony_ci goto err_alloc_etherdev; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 114762306a36Sopenharmony_ci netdev->ethtool_ops = &ftmac100_ethtool_ops; 114862306a36Sopenharmony_ci netdev->netdev_ops = &ftmac100_netdev_ops; 114962306a36Sopenharmony_ci netdev->max_mtu = MAX_PKT_SIZE - VLAN_ETH_HLEN; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci err = platform_get_ethdev_address(&pdev->dev, netdev); 115262306a36Sopenharmony_ci if (err == -EPROBE_DEFER) 115362306a36Sopenharmony_ci goto defer_get_mac; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci platform_set_drvdata(pdev, netdev); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci /* setup private data */ 115862306a36Sopenharmony_ci priv = netdev_priv(netdev); 115962306a36Sopenharmony_ci priv->netdev = netdev; 116062306a36Sopenharmony_ci priv->dev = &pdev->dev; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci spin_lock_init(&priv->tx_lock); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci /* initialize NAPI */ 116562306a36Sopenharmony_ci netif_napi_add(netdev, &priv->napi, ftmac100_poll); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* map io memory */ 116862306a36Sopenharmony_ci priv->res = request_mem_region(res->start, resource_size(res), 116962306a36Sopenharmony_ci dev_name(&pdev->dev)); 117062306a36Sopenharmony_ci if (!priv->res) { 117162306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not reserve memory region\n"); 117262306a36Sopenharmony_ci err = -ENOMEM; 117362306a36Sopenharmony_ci goto err_req_mem; 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci priv->base = ioremap(res->start, resource_size(res)); 117762306a36Sopenharmony_ci if (!priv->base) { 117862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); 117962306a36Sopenharmony_ci err = -EIO; 118062306a36Sopenharmony_ci goto err_ioremap; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci priv->irq = irq; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* initialize struct mii_if_info */ 118662306a36Sopenharmony_ci priv->mii.phy_id = 0; 118762306a36Sopenharmony_ci priv->mii.phy_id_mask = 0x1f; 118862306a36Sopenharmony_ci priv->mii.reg_num_mask = 0x1f; 118962306a36Sopenharmony_ci priv->mii.dev = netdev; 119062306a36Sopenharmony_ci priv->mii.mdio_read = ftmac100_mdio_read; 119162306a36Sopenharmony_ci priv->mii.mdio_write = ftmac100_mdio_write; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* register network device */ 119462306a36Sopenharmony_ci err = register_netdev(netdev); 119562306a36Sopenharmony_ci if (err) { 119662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register netdev\n"); 119762306a36Sopenharmony_ci goto err_register_netdev; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base); 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci if (!is_valid_ether_addr(netdev->dev_addr)) { 120362306a36Sopenharmony_ci eth_hw_addr_random(netdev); 120462306a36Sopenharmony_ci netdev_info(netdev, "generated random MAC address %pM\n", 120562306a36Sopenharmony_ci netdev->dev_addr); 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return 0; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cierr_register_netdev: 121162306a36Sopenharmony_ci iounmap(priv->base); 121262306a36Sopenharmony_cierr_ioremap: 121362306a36Sopenharmony_ci release_resource(priv->res); 121462306a36Sopenharmony_cierr_req_mem: 121562306a36Sopenharmony_ci netif_napi_del(&priv->napi); 121662306a36Sopenharmony_cidefer_get_mac: 121762306a36Sopenharmony_ci free_netdev(netdev); 121862306a36Sopenharmony_cierr_alloc_etherdev: 121962306a36Sopenharmony_ci return err; 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic int ftmac100_remove(struct platform_device *pdev) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci struct net_device *netdev; 122562306a36Sopenharmony_ci struct ftmac100 *priv; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci netdev = platform_get_drvdata(pdev); 122862306a36Sopenharmony_ci priv = netdev_priv(netdev); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci unregister_netdev(netdev); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci iounmap(priv->base); 123362306a36Sopenharmony_ci release_resource(priv->res); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci netif_napi_del(&priv->napi); 123662306a36Sopenharmony_ci free_netdev(netdev); 123762306a36Sopenharmony_ci return 0; 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic const struct of_device_id ftmac100_of_ids[] = { 124162306a36Sopenharmony_ci { .compatible = "andestech,atmac100" }, 124262306a36Sopenharmony_ci { } 124362306a36Sopenharmony_ci}; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic struct platform_driver ftmac100_driver = { 124662306a36Sopenharmony_ci .probe = ftmac100_probe, 124762306a36Sopenharmony_ci .remove = ftmac100_remove, 124862306a36Sopenharmony_ci .driver = { 124962306a36Sopenharmony_ci .name = DRV_NAME, 125062306a36Sopenharmony_ci .of_match_table = ftmac100_of_ids 125162306a36Sopenharmony_ci }, 125262306a36Sopenharmony_ci}; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci/****************************************************************************** 125562306a36Sopenharmony_ci * initialization / finalization 125662306a36Sopenharmony_ci *****************************************************************************/ 125762306a36Sopenharmony_cimodule_platform_driver(ftmac100_driver); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ciMODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>"); 126062306a36Sopenharmony_ciMODULE_DESCRIPTION("FTMAC100 driver"); 126162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 126262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ftmac100_of_ids); 1263