162306a36Sopenharmony_ci/* MOXA ART Ethernet (RTL8201CP) driver. 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright (C) 2013 Jonas Jensen 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Jonas Jensen <jonas.jensen@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on code from 862306a36Sopenharmony_ci * Moxa Technology Co., Ltd. <www.moxa.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 1162306a36Sopenharmony_ci * License version 2. This program is licensed "as is" without any 1262306a36Sopenharmony_ci * warranty of any kind, whether express or implied. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/etherdevice.h> 1862306a36Sopenharmony_ci#include <linux/skbuff.h> 1962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2062306a36Sopenharmony_ci#include <linux/ethtool.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/interrupt.h> 2362306a36Sopenharmony_ci#include <linux/irq.h> 2462306a36Sopenharmony_ci#include <linux/of_address.h> 2562306a36Sopenharmony_ci#include <linux/of_irq.h> 2662306a36Sopenharmony_ci#include <linux/crc32.h> 2762306a36Sopenharmony_ci#include <linux/crc32c.h> 2862306a36Sopenharmony_ci#include <linux/circ_buf.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "moxart_ether.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic inline void moxart_desc_write(u32 data, __le32 *desc) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci *desc = cpu_to_le32(data); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline u32 moxart_desc_read(__le32 *desc) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return le32_to_cpu(*desc); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline void moxart_emac_write(struct net_device *ndev, 4362306a36Sopenharmony_ci unsigned int reg, unsigned long value) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci writel(value, priv->base + reg); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void moxart_update_mac_address(struct net_device *ndev) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci moxart_emac_write(ndev, REG_MAC_MS_ADDRESS, 5362306a36Sopenharmony_ci ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1]))); 5462306a36Sopenharmony_ci moxart_emac_write(ndev, REG_MAC_MS_ADDRESS + 4, 5562306a36Sopenharmony_ci ((ndev->dev_addr[2] << 24) | 5662306a36Sopenharmony_ci (ndev->dev_addr[3] << 16) | 5762306a36Sopenharmony_ci (ndev->dev_addr[4] << 8) | 5862306a36Sopenharmony_ci (ndev->dev_addr[5]))); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int moxart_set_mac_address(struct net_device *ndev, void *addr) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct sockaddr *address = addr; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci eth_hw_addr_set(ndev, address->sa_data); 6662306a36Sopenharmony_ci moxart_update_mac_address(ndev); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void moxart_mac_free_memory(struct net_device *ndev) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (priv->tx_desc_base) 7662306a36Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, 7762306a36Sopenharmony_ci TX_REG_DESC_SIZE * TX_DESC_NUM, 7862306a36Sopenharmony_ci priv->tx_desc_base, priv->tx_base); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (priv->rx_desc_base) 8162306a36Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, 8262306a36Sopenharmony_ci RX_REG_DESC_SIZE * RX_DESC_NUM, 8362306a36Sopenharmony_ci priv->rx_desc_base, priv->rx_base); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci kfree(priv->tx_buf_base); 8662306a36Sopenharmony_ci kfree(priv->rx_buf_base); 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void moxart_mac_reset(struct net_device *ndev) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci writel(SW_RST, priv->base + REG_MAC_CTRL); 9462306a36Sopenharmony_ci while (readl(priv->base + REG_MAC_CTRL) & SW_RST) 9562306a36Sopenharmony_ci mdelay(10); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci writel(0, priv->base + REG_INTERRUPT_MASK); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci priv->reg_maccr = RX_BROADPKT | FULLDUP | CRC_APD | RX_FTL; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void moxart_mac_enable(struct net_device *ndev) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci writel(0x00001010, priv->base + REG_INT_TIMER_CTRL); 10762306a36Sopenharmony_ci writel(0x00000001, priv->base + REG_APOLL_TIMER_CTRL); 10862306a36Sopenharmony_ci writel(0x00000390, priv->base + REG_DMA_BLEN_CTRL); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci priv->reg_imr |= (RPKT_FINISH_M | XPKT_FINISH_M); 11162306a36Sopenharmony_ci writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci priv->reg_maccr |= (RCV_EN | XMT_EN | RDMA_EN | XDMA_EN); 11462306a36Sopenharmony_ci writel(priv->reg_maccr, priv->base + REG_MAC_CTRL); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void moxart_mac_setup_desc_ring(struct net_device *ndev) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 12062306a36Sopenharmony_ci void *desc; 12162306a36Sopenharmony_ci int i; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 0; i < TX_DESC_NUM; i++) { 12462306a36Sopenharmony_ci desc = priv->tx_desc_base + i * TX_REG_DESC_SIZE; 12562306a36Sopenharmony_ci memset(desc, 0, TX_REG_DESC_SIZE); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci priv->tx_buf[i] = priv->tx_buf_base + priv->tx_buf_size * i; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci moxart_desc_write(TX_DESC1_END, desc + TX_REG_OFFSET_DESC1); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci priv->tx_head = 0; 13262306a36Sopenharmony_ci priv->tx_tail = 0; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci for (i = 0; i < RX_DESC_NUM; i++) { 13562306a36Sopenharmony_ci desc = priv->rx_desc_base + i * RX_REG_DESC_SIZE; 13662306a36Sopenharmony_ci memset(desc, 0, RX_REG_DESC_SIZE); 13762306a36Sopenharmony_ci moxart_desc_write(RX_DESC0_DMA_OWN, desc + RX_REG_OFFSET_DESC0); 13862306a36Sopenharmony_ci moxart_desc_write(RX_BUF_SIZE & RX_DESC1_BUF_SIZE_MASK, 13962306a36Sopenharmony_ci desc + RX_REG_OFFSET_DESC1); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci priv->rx_buf[i] = priv->rx_buf_base + priv->rx_buf_size * i; 14262306a36Sopenharmony_ci priv->rx_mapping[i] = dma_map_single(&priv->pdev->dev, 14362306a36Sopenharmony_ci priv->rx_buf[i], 14462306a36Sopenharmony_ci priv->rx_buf_size, 14562306a36Sopenharmony_ci DMA_FROM_DEVICE); 14662306a36Sopenharmony_ci if (dma_mapping_error(&priv->pdev->dev, priv->rx_mapping[i])) 14762306a36Sopenharmony_ci netdev_err(ndev, "DMA mapping error\n"); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci moxart_desc_write(priv->rx_mapping[i], 15062306a36Sopenharmony_ci desc + RX_REG_OFFSET_DESC2 + RX_DESC2_ADDRESS_PHYS); 15162306a36Sopenharmony_ci moxart_desc_write((uintptr_t)priv->rx_buf[i], 15262306a36Sopenharmony_ci desc + RX_REG_OFFSET_DESC2 + RX_DESC2_ADDRESS_VIRT); 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci moxart_desc_write(RX_DESC1_END, desc + RX_REG_OFFSET_DESC1); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci priv->rx_head = 0; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* reset the MAC controller TX/RX descriptor base address */ 15962306a36Sopenharmony_ci writel(priv->tx_base, priv->base + REG_TXR_BASE_ADDRESS); 16062306a36Sopenharmony_ci writel(priv->rx_base, priv->base + REG_RXR_BASE_ADDRESS); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int moxart_mac_open(struct net_device *ndev) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci napi_enable(&priv->napi); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci moxart_mac_reset(ndev); 17062306a36Sopenharmony_ci moxart_update_mac_address(ndev); 17162306a36Sopenharmony_ci moxart_mac_setup_desc_ring(ndev); 17262306a36Sopenharmony_ci moxart_mac_enable(ndev); 17362306a36Sopenharmony_ci netif_start_queue(ndev); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n", 17662306a36Sopenharmony_ci __func__, readl(priv->base + REG_INTERRUPT_MASK), 17762306a36Sopenharmony_ci readl(priv->base + REG_MAC_CTRL)); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int moxart_mac_stop(struct net_device *ndev) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 18562306a36Sopenharmony_ci int i; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci napi_disable(&priv->napi); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci netif_stop_queue(ndev); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* disable all interrupts */ 19262306a36Sopenharmony_ci writel(0, priv->base + REG_INTERRUPT_MASK); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* disable all functions */ 19562306a36Sopenharmony_ci writel(0, priv->base + REG_MAC_CTRL); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* unmap areas mapped in moxart_mac_setup_desc_ring() */ 19862306a36Sopenharmony_ci for (i = 0; i < RX_DESC_NUM; i++) 19962306a36Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, priv->rx_mapping[i], 20062306a36Sopenharmony_ci priv->rx_buf_size, DMA_FROM_DEVICE); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int moxart_rx_poll(struct napi_struct *napi, int budget) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = container_of(napi, 20862306a36Sopenharmony_ci struct moxart_mac_priv_t, 20962306a36Sopenharmony_ci napi); 21062306a36Sopenharmony_ci struct net_device *ndev = priv->ndev; 21162306a36Sopenharmony_ci struct sk_buff *skb; 21262306a36Sopenharmony_ci void *desc; 21362306a36Sopenharmony_ci unsigned int desc0, len; 21462306a36Sopenharmony_ci int rx_head = priv->rx_head; 21562306a36Sopenharmony_ci int rx = 0; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci while (rx < budget) { 21862306a36Sopenharmony_ci desc = priv->rx_desc_base + (RX_REG_DESC_SIZE * rx_head); 21962306a36Sopenharmony_ci desc0 = moxart_desc_read(desc + RX_REG_OFFSET_DESC0); 22062306a36Sopenharmony_ci rmb(); /* ensure desc0 is up to date */ 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (desc0 & RX_DESC0_DMA_OWN) 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (desc0 & (RX_DESC0_ERR | RX_DESC0_CRC_ERR | RX_DESC0_FTL | 22662306a36Sopenharmony_ci RX_DESC0_RUNT | RX_DESC0_ODD_NB)) { 22762306a36Sopenharmony_ci net_dbg_ratelimited("packet error\n"); 22862306a36Sopenharmony_ci ndev->stats.rx_dropped++; 22962306a36Sopenharmony_ci ndev->stats.rx_errors++; 23062306a36Sopenharmony_ci goto rx_next; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci len = desc0 & RX_DESC0_FRAME_LEN_MASK; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (len > RX_BUF_SIZE) 23662306a36Sopenharmony_ci len = RX_BUF_SIZE; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dma_sync_single_for_cpu(&priv->pdev->dev, 23962306a36Sopenharmony_ci priv->rx_mapping[rx_head], 24062306a36Sopenharmony_ci priv->rx_buf_size, DMA_FROM_DEVICE); 24162306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(ndev, len); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (unlikely(!skb)) { 24462306a36Sopenharmony_ci net_dbg_ratelimited("netdev_alloc_skb_ip_align failed\n"); 24562306a36Sopenharmony_ci ndev->stats.rx_dropped++; 24662306a36Sopenharmony_ci ndev->stats.rx_errors++; 24762306a36Sopenharmony_ci goto rx_next; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci memcpy(skb->data, priv->rx_buf[rx_head], len); 25162306a36Sopenharmony_ci skb_put(skb, len); 25262306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, ndev); 25362306a36Sopenharmony_ci napi_gro_receive(&priv->napi, skb); 25462306a36Sopenharmony_ci rx++; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci ndev->stats.rx_packets++; 25762306a36Sopenharmony_ci ndev->stats.rx_bytes += len; 25862306a36Sopenharmony_ci if (desc0 & RX_DESC0_MULTICAST) 25962306a36Sopenharmony_ci ndev->stats.multicast++; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cirx_next: 26262306a36Sopenharmony_ci wmb(); /* prevent setting ownership back too early */ 26362306a36Sopenharmony_ci moxart_desc_write(RX_DESC0_DMA_OWN, desc + RX_REG_OFFSET_DESC0); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci rx_head = RX_NEXT(rx_head); 26662306a36Sopenharmony_ci priv->rx_head = rx_head; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (rx < budget) 27062306a36Sopenharmony_ci napi_complete_done(napi, rx); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci priv->reg_imr |= RPKT_FINISH_M; 27362306a36Sopenharmony_ci writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return rx; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int moxart_tx_queue_space(struct net_device *ndev) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return CIRC_SPACE(priv->tx_head, priv->tx_tail, TX_DESC_NUM); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic void moxart_tx_finished(struct net_device *ndev) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 28862306a36Sopenharmony_ci unsigned int tx_head = priv->tx_head; 28962306a36Sopenharmony_ci unsigned int tx_tail = priv->tx_tail; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci while (tx_tail != tx_head) { 29262306a36Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, priv->tx_mapping[tx_tail], 29362306a36Sopenharmony_ci priv->tx_len[tx_tail], DMA_TO_DEVICE); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ndev->stats.tx_packets++; 29662306a36Sopenharmony_ci ndev->stats.tx_bytes += priv->tx_skb[tx_tail]->len; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci dev_consume_skb_irq(priv->tx_skb[tx_tail]); 29962306a36Sopenharmony_ci priv->tx_skb[tx_tail] = NULL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci tx_tail = TX_NEXT(tx_tail); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci priv->tx_tail = tx_tail; 30462306a36Sopenharmony_ci if (netif_queue_stopped(ndev) && 30562306a36Sopenharmony_ci moxart_tx_queue_space(ndev) >= TX_WAKE_THRESHOLD) 30662306a36Sopenharmony_ci netif_wake_queue(ndev); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic irqreturn_t moxart_mac_interrupt(int irq, void *dev_id) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct net_device *ndev = (struct net_device *)dev_id; 31262306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 31362306a36Sopenharmony_ci unsigned int ists = readl(priv->base + REG_INTERRUPT_STATUS); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (ists & XPKT_OK_INT_STS) 31662306a36Sopenharmony_ci moxart_tx_finished(ndev); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (ists & RPKT_FINISH) { 31962306a36Sopenharmony_ci if (napi_schedule_prep(&priv->napi)) { 32062306a36Sopenharmony_ci priv->reg_imr &= ~RPKT_FINISH_M; 32162306a36Sopenharmony_ci writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); 32262306a36Sopenharmony_ci __napi_schedule(&priv->napi); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return IRQ_HANDLED; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic netdev_tx_t moxart_mac_start_xmit(struct sk_buff *skb, 33062306a36Sopenharmony_ci struct net_device *ndev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 33362306a36Sopenharmony_ci void *desc; 33462306a36Sopenharmony_ci unsigned int len; 33562306a36Sopenharmony_ci unsigned int tx_head; 33662306a36Sopenharmony_ci u32 txdes1; 33762306a36Sopenharmony_ci netdev_tx_t ret = NETDEV_TX_BUSY; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci spin_lock_irq(&priv->txlock); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci tx_head = priv->tx_head; 34262306a36Sopenharmony_ci desc = priv->tx_desc_base + (TX_REG_DESC_SIZE * tx_head); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (moxart_tx_queue_space(ndev) == 1) 34562306a36Sopenharmony_ci netif_stop_queue(ndev); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (moxart_desc_read(desc + TX_REG_OFFSET_DESC0) & TX_DESC0_DMA_OWN) { 34862306a36Sopenharmony_ci net_dbg_ratelimited("no TX space for packet\n"); 34962306a36Sopenharmony_ci ndev->stats.tx_dropped++; 35062306a36Sopenharmony_ci goto out_unlock; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci rmb(); /* ensure data is only read that had TX_DESC0_DMA_OWN cleared */ 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci priv->tx_mapping[tx_head] = dma_map_single(&priv->pdev->dev, skb->data, 35762306a36Sopenharmony_ci len, DMA_TO_DEVICE); 35862306a36Sopenharmony_ci if (dma_mapping_error(&priv->pdev->dev, priv->tx_mapping[tx_head])) { 35962306a36Sopenharmony_ci netdev_err(ndev, "DMA mapping error\n"); 36062306a36Sopenharmony_ci goto out_unlock; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci priv->tx_len[tx_head] = len; 36462306a36Sopenharmony_ci priv->tx_skb[tx_head] = skb; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci moxart_desc_write(priv->tx_mapping[tx_head], 36762306a36Sopenharmony_ci desc + TX_REG_OFFSET_DESC2 + TX_DESC2_ADDRESS_PHYS); 36862306a36Sopenharmony_ci moxart_desc_write((uintptr_t)skb->data, 36962306a36Sopenharmony_ci desc + TX_REG_OFFSET_DESC2 + TX_DESC2_ADDRESS_VIRT); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (skb->len < ETH_ZLEN) { 37262306a36Sopenharmony_ci memset(&skb->data[skb->len], 37362306a36Sopenharmony_ci 0, ETH_ZLEN - skb->len); 37462306a36Sopenharmony_ci len = ETH_ZLEN; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci dma_sync_single_for_device(&priv->pdev->dev, priv->tx_mapping[tx_head], 37862306a36Sopenharmony_ci priv->tx_buf_size, DMA_TO_DEVICE); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci txdes1 = TX_DESC1_LTS | TX_DESC1_FTS | (len & TX_DESC1_BUF_SIZE_MASK); 38162306a36Sopenharmony_ci if (tx_head == TX_DESC_NUM_MASK) 38262306a36Sopenharmony_ci txdes1 |= TX_DESC1_END; 38362306a36Sopenharmony_ci moxart_desc_write(txdes1, desc + TX_REG_OFFSET_DESC1); 38462306a36Sopenharmony_ci wmb(); /* flush descriptor before transferring ownership */ 38562306a36Sopenharmony_ci moxart_desc_write(TX_DESC0_DMA_OWN, desc + TX_REG_OFFSET_DESC0); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* start to send packet */ 38862306a36Sopenharmony_ci writel(0xffffffff, priv->base + REG_TX_POLL_DEMAND); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci priv->tx_head = TX_NEXT(tx_head); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci netif_trans_update(ndev); 39362306a36Sopenharmony_ci ret = NETDEV_TX_OK; 39462306a36Sopenharmony_ciout_unlock: 39562306a36Sopenharmony_ci spin_unlock_irq(&priv->txlock); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return ret; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void moxart_mac_setmulticast(struct net_device *ndev) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 40362306a36Sopenharmony_ci struct netdev_hw_addr *ha; 40462306a36Sopenharmony_ci int crc_val; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, ndev) { 40762306a36Sopenharmony_ci crc_val = crc32_le(~0, ha->addr, ETH_ALEN); 40862306a36Sopenharmony_ci crc_val = (crc_val >> 26) & 0x3f; 40962306a36Sopenharmony_ci if (crc_val >= 32) { 41062306a36Sopenharmony_ci writel(readl(priv->base + REG_MCAST_HASH_TABLE1) | 41162306a36Sopenharmony_ci (1UL << (crc_val - 32)), 41262306a36Sopenharmony_ci priv->base + REG_MCAST_HASH_TABLE1); 41362306a36Sopenharmony_ci } else { 41462306a36Sopenharmony_ci writel(readl(priv->base + REG_MCAST_HASH_TABLE0) | 41562306a36Sopenharmony_ci (1UL << crc_val), 41662306a36Sopenharmony_ci priv->base + REG_MCAST_HASH_TABLE0); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void moxart_mac_set_rx_mode(struct net_device *ndev) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct moxart_mac_priv_t *priv = netdev_priv(ndev); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci spin_lock_irq(&priv->txlock); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci (ndev->flags & IFF_PROMISC) ? (priv->reg_maccr |= RCV_ALL) : 42862306a36Sopenharmony_ci (priv->reg_maccr &= ~RCV_ALL); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci (ndev->flags & IFF_ALLMULTI) ? (priv->reg_maccr |= RX_MULTIPKT) : 43162306a36Sopenharmony_ci (priv->reg_maccr &= ~RX_MULTIPKT); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if ((ndev->flags & IFF_MULTICAST) && netdev_mc_count(ndev)) { 43462306a36Sopenharmony_ci priv->reg_maccr |= HT_MULTI_EN; 43562306a36Sopenharmony_ci moxart_mac_setmulticast(ndev); 43662306a36Sopenharmony_ci } else { 43762306a36Sopenharmony_ci priv->reg_maccr &= ~HT_MULTI_EN; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci writel(priv->reg_maccr, priv->base + REG_MAC_CTRL); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci spin_unlock_irq(&priv->txlock); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const struct net_device_ops moxart_netdev_ops = { 44662306a36Sopenharmony_ci .ndo_open = moxart_mac_open, 44762306a36Sopenharmony_ci .ndo_stop = moxart_mac_stop, 44862306a36Sopenharmony_ci .ndo_start_xmit = moxart_mac_start_xmit, 44962306a36Sopenharmony_ci .ndo_set_rx_mode = moxart_mac_set_rx_mode, 45062306a36Sopenharmony_ci .ndo_set_mac_address = moxart_set_mac_address, 45162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 45262306a36Sopenharmony_ci}; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int moxart_mac_probe(struct platform_device *pdev) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct device *p_dev = &pdev->dev; 45762306a36Sopenharmony_ci struct device_node *node = p_dev->of_node; 45862306a36Sopenharmony_ci struct net_device *ndev; 45962306a36Sopenharmony_ci struct moxart_mac_priv_t *priv; 46062306a36Sopenharmony_ci struct resource *res; 46162306a36Sopenharmony_ci unsigned int irq; 46262306a36Sopenharmony_ci int ret; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t)); 46562306a36Sopenharmony_ci if (!ndev) 46662306a36Sopenharmony_ci return -ENOMEM; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 46962306a36Sopenharmony_ci if (irq <= 0) { 47062306a36Sopenharmony_ci netdev_err(ndev, "irq_of_parse_and_map failed\n"); 47162306a36Sopenharmony_ci ret = -EINVAL; 47262306a36Sopenharmony_ci goto irq_map_fail; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci priv = netdev_priv(ndev); 47662306a36Sopenharmony_ci priv->ndev = ndev; 47762306a36Sopenharmony_ci priv->pdev = pdev; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 48062306a36Sopenharmony_ci if (IS_ERR(priv->base)) { 48162306a36Sopenharmony_ci ret = PTR_ERR(priv->base); 48262306a36Sopenharmony_ci goto init_fail; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci ndev->base_addr = res->start; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ret = platform_get_ethdev_address(p_dev, ndev); 48762306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 48862306a36Sopenharmony_ci goto init_fail; 48962306a36Sopenharmony_ci if (ret) 49062306a36Sopenharmony_ci eth_hw_addr_random(ndev); 49162306a36Sopenharmony_ci moxart_update_mac_address(ndev); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci spin_lock_init(&priv->txlock); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci priv->tx_buf_size = TX_BUF_SIZE; 49662306a36Sopenharmony_ci priv->rx_buf_size = RX_BUF_SIZE; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci priv->tx_desc_base = dma_alloc_coherent(p_dev, TX_REG_DESC_SIZE * 49962306a36Sopenharmony_ci TX_DESC_NUM, &priv->tx_base, 50062306a36Sopenharmony_ci GFP_DMA | GFP_KERNEL); 50162306a36Sopenharmony_ci if (!priv->tx_desc_base) { 50262306a36Sopenharmony_ci ret = -ENOMEM; 50362306a36Sopenharmony_ci goto init_fail; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci priv->rx_desc_base = dma_alloc_coherent(p_dev, RX_REG_DESC_SIZE * 50762306a36Sopenharmony_ci RX_DESC_NUM, &priv->rx_base, 50862306a36Sopenharmony_ci GFP_DMA | GFP_KERNEL); 50962306a36Sopenharmony_ci if (!priv->rx_desc_base) { 51062306a36Sopenharmony_ci ret = -ENOMEM; 51162306a36Sopenharmony_ci goto init_fail; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci priv->tx_buf_base = kmalloc_array(priv->tx_buf_size, TX_DESC_NUM, 51562306a36Sopenharmony_ci GFP_KERNEL); 51662306a36Sopenharmony_ci if (!priv->tx_buf_base) { 51762306a36Sopenharmony_ci ret = -ENOMEM; 51862306a36Sopenharmony_ci goto init_fail; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci priv->rx_buf_base = kmalloc_array(priv->rx_buf_size, RX_DESC_NUM, 52262306a36Sopenharmony_ci GFP_KERNEL); 52362306a36Sopenharmony_ci if (!priv->rx_buf_base) { 52462306a36Sopenharmony_ci ret = -ENOMEM; 52562306a36Sopenharmony_ci goto init_fail; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci platform_set_drvdata(pdev, ndev); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ret = devm_request_irq(p_dev, irq, moxart_mac_interrupt, 0, 53162306a36Sopenharmony_ci pdev->name, ndev); 53262306a36Sopenharmony_ci if (ret) { 53362306a36Sopenharmony_ci netdev_err(ndev, "devm_request_irq failed\n"); 53462306a36Sopenharmony_ci goto init_fail; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci ndev->netdev_ops = &moxart_netdev_ops; 53862306a36Sopenharmony_ci netif_napi_add_weight(ndev, &priv->napi, moxart_rx_poll, RX_DESC_NUM); 53962306a36Sopenharmony_ci ndev->priv_flags |= IFF_UNICAST_FLT; 54062306a36Sopenharmony_ci ndev->irq = irq; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = register_netdev(ndev); 54562306a36Sopenharmony_ci if (ret) 54662306a36Sopenharmony_ci goto init_fail; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci netdev_dbg(ndev, "%s: IRQ=%d address=%pM\n", 54962306a36Sopenharmony_ci __func__, ndev->irq, ndev->dev_addr); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ciinit_fail: 55462306a36Sopenharmony_ci netdev_err(ndev, "init failed\n"); 55562306a36Sopenharmony_ci moxart_mac_free_memory(ndev); 55662306a36Sopenharmony_ciirq_map_fail: 55762306a36Sopenharmony_ci free_netdev(ndev); 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic int moxart_remove(struct platform_device *pdev) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci unregister_netdev(ndev); 56662306a36Sopenharmony_ci devm_free_irq(&pdev->dev, ndev->irq, ndev); 56762306a36Sopenharmony_ci moxart_mac_free_memory(ndev); 56862306a36Sopenharmony_ci free_netdev(ndev); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic const struct of_device_id moxart_mac_match[] = { 57462306a36Sopenharmony_ci { .compatible = "moxa,moxart-mac" }, 57562306a36Sopenharmony_ci { } 57662306a36Sopenharmony_ci}; 57762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, moxart_mac_match); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic struct platform_driver moxart_mac_driver = { 58062306a36Sopenharmony_ci .probe = moxart_mac_probe, 58162306a36Sopenharmony_ci .remove = moxart_remove, 58262306a36Sopenharmony_ci .driver = { 58362306a36Sopenharmony_ci .name = "moxart-ethernet", 58462306a36Sopenharmony_ci .of_match_table = moxart_mac_match, 58562306a36Sopenharmony_ci }, 58662306a36Sopenharmony_ci}; 58762306a36Sopenharmony_cimodule_platform_driver(moxart_mac_driver); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciMODULE_DESCRIPTION("MOXART RTL8201CP Ethernet driver"); 59062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 59162306a36Sopenharmony_ciMODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>"); 592