18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Mans Rullgard <mans@mansr.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Mostly rewritten, based on driver from Sigma Designs. Original 68c2ecf20Sopenharmony_ci * copyright notice below. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Driver for tangox SMP864x/SMP865x/SMP867x/SMP868x builtin Ethernet Mac. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2005 Maxime Bizon <mbizon@freebox.fr> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 218c2ecf20Sopenharmony_ci#include <linux/of_net.h> 228c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 238c2ecf20Sopenharmony_ci#include <linux/phy.h> 248c2ecf20Sopenharmony_ci#include <linux/cache.h> 258c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 268c2ecf20Sopenharmony_ci#include <linux/io.h> 278c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 288c2ecf20Sopenharmony_ci#include <asm/barrier.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "nb8800.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void nb8800_tx_done(struct net_device *dev); 338c2ecf20Sopenharmony_cistatic int nb8800_dma_stop(struct net_device *dev); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic inline u8 nb8800_readb(struct nb8800_priv *priv, int reg) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return readb_relaxed(priv->base + reg); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic inline u32 nb8800_readl(struct nb8800_priv *priv, int reg) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci return readl_relaxed(priv->base + reg); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic inline void nb8800_writeb(struct nb8800_priv *priv, int reg, u8 val) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci writeb_relaxed(val, priv->base + reg); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline void nb8800_writew(struct nb8800_priv *priv, int reg, u16 val) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci writew_relaxed(val, priv->base + reg); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline void nb8800_writel(struct nb8800_priv *priv, int reg, u32 val) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci writel_relaxed(val, priv->base + reg); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline void nb8800_maskb(struct nb8800_priv *priv, int reg, 618c2ecf20Sopenharmony_ci u32 mask, u32 val) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci u32 old = nb8800_readb(priv, reg); 648c2ecf20Sopenharmony_ci u32 new = (old & ~mask) | (val & mask); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (new != old) 678c2ecf20Sopenharmony_ci nb8800_writeb(priv, reg, new); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic inline void nb8800_maskl(struct nb8800_priv *priv, int reg, 718c2ecf20Sopenharmony_ci u32 mask, u32 val) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci u32 old = nb8800_readl(priv, reg); 748c2ecf20Sopenharmony_ci u32 new = (old & ~mask) | (val & mask); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (new != old) 778c2ecf20Sopenharmony_ci nb8800_writel(priv, reg, new); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic inline void nb8800_modb(struct nb8800_priv *priv, int reg, u8 bits, 818c2ecf20Sopenharmony_ci bool set) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci nb8800_maskb(priv, reg, bits, set ? bits : 0); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic inline void nb8800_setb(struct nb8800_priv *priv, int reg, u8 bits) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci nb8800_maskb(priv, reg, bits, bits); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline void nb8800_clearb(struct nb8800_priv *priv, int reg, u8 bits) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci nb8800_maskb(priv, reg, bits, 0); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic inline void nb8800_modl(struct nb8800_priv *priv, int reg, u32 bits, 978c2ecf20Sopenharmony_ci bool set) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci nb8800_maskl(priv, reg, bits, set ? bits : 0); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline void nb8800_setl(struct nb8800_priv *priv, int reg, u32 bits) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci nb8800_maskl(priv, reg, bits, bits); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic inline void nb8800_clearl(struct nb8800_priv *priv, int reg, u32 bits) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci nb8800_maskl(priv, reg, bits, 0); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int nb8800_mdio_wait(struct mii_bus *bus) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct nb8800_priv *priv = bus->priv; 1158c2ecf20Sopenharmony_ci u32 val; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return readl_poll_timeout_atomic(priv->base + NB8800_MDIO_CMD, 1188c2ecf20Sopenharmony_ci val, !(val & MDIO_CMD_GO), 1, 1000); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int nb8800_mdio_cmd(struct mii_bus *bus, u32 cmd) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct nb8800_priv *priv = bus->priv; 1248c2ecf20Sopenharmony_ci int err; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci err = nb8800_mdio_wait(bus); 1278c2ecf20Sopenharmony_ci if (err) 1288c2ecf20Sopenharmony_ci return err; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_MDIO_CMD, cmd); 1318c2ecf20Sopenharmony_ci udelay(10); 1328c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_MDIO_CMD, cmd | MDIO_CMD_GO); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return nb8800_mdio_wait(bus); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int nb8800_mdio_read(struct mii_bus *bus, int phy_id, int reg) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct nb8800_priv *priv = bus->priv; 1408c2ecf20Sopenharmony_ci u32 val; 1418c2ecf20Sopenharmony_ci int err; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci err = nb8800_mdio_cmd(bus, MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg)); 1448c2ecf20Sopenharmony_ci if (err) 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci val = nb8800_readl(priv, NB8800_MDIO_STS); 1488c2ecf20Sopenharmony_ci if (val & MDIO_STS_ERR) 1498c2ecf20Sopenharmony_ci return 0xffff; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return val & 0xffff; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int nb8800_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci u32 cmd = MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg) | 1578c2ecf20Sopenharmony_ci MDIO_CMD_DATA(val) | MDIO_CMD_WR; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return nb8800_mdio_cmd(bus, cmd); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void nb8800_mac_tx(struct net_device *dev, bool enable) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci while (nb8800_readl(priv, NB8800_TXC_CR) & TCR_EN) 1678c2ecf20Sopenharmony_ci cpu_relax(); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci nb8800_modb(priv, NB8800_TX_CTL1, TX_EN, enable); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void nb8800_mac_rx(struct net_device *dev, bool enable) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_EN, enable); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void nb8800_mac_af(struct net_device *dev, bool enable) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_AF_EN, enable); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void nb8800_start_rx(struct net_device *dev) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci nb8800_setl(netdev_priv(dev), NB8800_RXC_CR, RCR_EN); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int nb8800_alloc_rx(struct net_device *dev, unsigned int i, bool napi) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 1908c2ecf20Sopenharmony_ci struct nb8800_rx_desc *rxd = &priv->rx_descs[i]; 1918c2ecf20Sopenharmony_ci struct nb8800_rx_buf *rxb = &priv->rx_bufs[i]; 1928c2ecf20Sopenharmony_ci int size = L1_CACHE_ALIGN(RX_BUF_SIZE); 1938c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 1948c2ecf20Sopenharmony_ci struct page *page; 1958c2ecf20Sopenharmony_ci unsigned long offset; 1968c2ecf20Sopenharmony_ci void *data; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci data = napi ? napi_alloc_frag(size) : netdev_alloc_frag(size); 1998c2ecf20Sopenharmony_ci if (!data) 2008c2ecf20Sopenharmony_ci return -ENOMEM; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci page = virt_to_head_page(data); 2038c2ecf20Sopenharmony_ci offset = data - page_address(page); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci dma_addr = dma_map_page(&dev->dev, page, offset, RX_BUF_SIZE, 2068c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (dma_mapping_error(&dev->dev, dma_addr)) { 2098c2ecf20Sopenharmony_ci skb_free_frag(data); 2108c2ecf20Sopenharmony_ci return -ENOMEM; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci rxb->page = page; 2148c2ecf20Sopenharmony_ci rxb->offset = offset; 2158c2ecf20Sopenharmony_ci rxd->desc.s_addr = dma_addr; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void nb8800_receive(struct net_device *dev, unsigned int i, 2218c2ecf20Sopenharmony_ci unsigned int len) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 2248c2ecf20Sopenharmony_ci struct nb8800_rx_desc *rxd = &priv->rx_descs[i]; 2258c2ecf20Sopenharmony_ci struct page *page = priv->rx_bufs[i].page; 2268c2ecf20Sopenharmony_ci int offset = priv->rx_bufs[i].offset; 2278c2ecf20Sopenharmony_ci void *data = page_address(page) + offset; 2288c2ecf20Sopenharmony_ci dma_addr_t dma = rxd->desc.s_addr; 2298c2ecf20Sopenharmony_ci struct sk_buff *skb; 2308c2ecf20Sopenharmony_ci unsigned int size; 2318c2ecf20Sopenharmony_ci int err; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci size = len <= RX_COPYBREAK ? len : RX_COPYHDR; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci skb = napi_alloc_skb(&priv->napi, size); 2368c2ecf20Sopenharmony_ci if (!skb) { 2378c2ecf20Sopenharmony_ci netdev_err(dev, "rx skb allocation failed\n"); 2388c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 2398c2ecf20Sopenharmony_ci return; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (len <= RX_COPYBREAK) { 2438c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&dev->dev, dma, len, DMA_FROM_DEVICE); 2448c2ecf20Sopenharmony_ci skb_put_data(skb, data, len); 2458c2ecf20Sopenharmony_ci dma_sync_single_for_device(&dev->dev, dma, len, 2468c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2478c2ecf20Sopenharmony_ci } else { 2488c2ecf20Sopenharmony_ci err = nb8800_alloc_rx(dev, i, true); 2498c2ecf20Sopenharmony_ci if (err) { 2508c2ecf20Sopenharmony_ci netdev_err(dev, "rx buffer allocation failed\n"); 2518c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 2528c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 2538c2ecf20Sopenharmony_ci return; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci dma_unmap_page(&dev->dev, dma, RX_BUF_SIZE, DMA_FROM_DEVICE); 2578c2ecf20Sopenharmony_ci skb_put_data(skb, data, RX_COPYHDR); 2588c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 2598c2ecf20Sopenharmony_ci offset + RX_COPYHDR, len - RX_COPYHDR, 2608c2ecf20Sopenharmony_ci RX_BUF_SIZE); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 2648c2ecf20Sopenharmony_ci napi_gro_receive(&priv->napi, skb); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void nb8800_rx_error(struct net_device *dev, u32 report) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci if (report & RX_LENGTH_ERR) 2708c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (report & RX_FCS_ERR) 2738c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (report & RX_FIFO_OVERRUN) 2768c2ecf20Sopenharmony_ci dev->stats.rx_fifo_errors++; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (report & RX_ALIGNMENT_ERROR) 2798c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int nb8800_poll(struct napi_struct *napi, int budget) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct net_device *dev = napi->dev; 2878c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 2888c2ecf20Sopenharmony_ci struct nb8800_rx_desc *rxd; 2898c2ecf20Sopenharmony_ci unsigned int last = priv->rx_eoc; 2908c2ecf20Sopenharmony_ci unsigned int next; 2918c2ecf20Sopenharmony_ci int work = 0; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci nb8800_tx_done(dev); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ciagain: 2968c2ecf20Sopenharmony_ci do { 2978c2ecf20Sopenharmony_ci unsigned int len; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci next = (last + 1) % RX_DESC_COUNT; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci rxd = &priv->rx_descs[next]; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!rxd->report) 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci len = RX_BYTES_TRANSFERRED(rxd->report); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (IS_RX_ERROR(rxd->report)) 3098c2ecf20Sopenharmony_ci nb8800_rx_error(dev, rxd->report); 3108c2ecf20Sopenharmony_ci else 3118c2ecf20Sopenharmony_ci nb8800_receive(dev, next, len); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 3148c2ecf20Sopenharmony_ci dev->stats.rx_bytes += len; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (rxd->report & RX_MULTICAST_PKT) 3178c2ecf20Sopenharmony_ci dev->stats.multicast++; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci rxd->report = 0; 3208c2ecf20Sopenharmony_ci last = next; 3218c2ecf20Sopenharmony_ci work++; 3228c2ecf20Sopenharmony_ci } while (work < budget); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (work) { 3258c2ecf20Sopenharmony_ci priv->rx_descs[last].desc.config |= DESC_EOC; 3268c2ecf20Sopenharmony_ci wmb(); /* ensure new EOC is written before clearing old */ 3278c2ecf20Sopenharmony_ci priv->rx_descs[priv->rx_eoc].desc.config &= ~DESC_EOC; 3288c2ecf20Sopenharmony_ci priv->rx_eoc = last; 3298c2ecf20Sopenharmony_ci nb8800_start_rx(dev); 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (work < budget) { 3338c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* If a packet arrived after we last checked but 3368c2ecf20Sopenharmony_ci * before writing RX_ITR, the interrupt will be 3378c2ecf20Sopenharmony_ci * delayed, so we retrieve it now. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci if (priv->rx_descs[next].report) 3408c2ecf20Sopenharmony_ci goto again; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci napi_complete_done(napi, work); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return work; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic void __nb8800_tx_dma_start(struct net_device *dev) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 3518c2ecf20Sopenharmony_ci struct nb8800_tx_buf *txb; 3528c2ecf20Sopenharmony_ci u32 txc_cr; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci txb = &priv->tx_bufs[priv->tx_queue]; 3558c2ecf20Sopenharmony_ci if (!txb->ready) 3568c2ecf20Sopenharmony_ci return; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci txc_cr = nb8800_readl(priv, NB8800_TXC_CR); 3598c2ecf20Sopenharmony_ci if (txc_cr & TCR_EN) 3608c2ecf20Sopenharmony_ci return; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc); 3638c2ecf20Sopenharmony_ci wmb(); /* ensure desc addr is written before starting DMA */ 3648c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TXC_CR, txc_cr | TCR_EN); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci priv->tx_queue = (priv->tx_queue + txb->chain_len) % TX_DESC_COUNT; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void nb8800_tx_dma_start(struct net_device *dev) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci spin_lock_irq(&priv->tx_lock); 3748c2ecf20Sopenharmony_ci __nb8800_tx_dma_start(dev); 3758c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->tx_lock); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic void nb8800_tx_dma_start_irq(struct net_device *dev) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci spin_lock(&priv->tx_lock); 3838c2ecf20Sopenharmony_ci __nb8800_tx_dma_start(dev); 3848c2ecf20Sopenharmony_ci spin_unlock(&priv->tx_lock); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic netdev_tx_t nb8800_xmit(struct sk_buff *skb, struct net_device *dev) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 3908c2ecf20Sopenharmony_ci struct nb8800_tx_desc *txd; 3918c2ecf20Sopenharmony_ci struct nb8800_tx_buf *txb; 3928c2ecf20Sopenharmony_ci struct nb8800_dma_desc *desc; 3938c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 3948c2ecf20Sopenharmony_ci unsigned int dma_len; 3958c2ecf20Sopenharmony_ci unsigned int align; 3968c2ecf20Sopenharmony_ci unsigned int next; 3978c2ecf20Sopenharmony_ci bool xmit_more; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (atomic_read(&priv->tx_free) <= NB8800_DESC_LOW) { 4008c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4018c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci align = (8 - (uintptr_t)skb->data) & 7; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci dma_len = skb->len - align; 4078c2ecf20Sopenharmony_ci dma_addr = dma_map_single(&dev->dev, skb->data + align, 4088c2ecf20Sopenharmony_ci dma_len, DMA_TO_DEVICE); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (dma_mapping_error(&dev->dev, dma_addr)) { 4118c2ecf20Sopenharmony_ci netdev_err(dev, "tx dma mapping error\n"); 4128c2ecf20Sopenharmony_ci kfree_skb(skb); 4138c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 4148c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci xmit_more = netdev_xmit_more(); 4188c2ecf20Sopenharmony_ci if (atomic_dec_return(&priv->tx_free) <= NB8800_DESC_LOW) { 4198c2ecf20Sopenharmony_ci netif_stop_queue(dev); 4208c2ecf20Sopenharmony_ci xmit_more = false; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci next = priv->tx_next; 4248c2ecf20Sopenharmony_ci txb = &priv->tx_bufs[next]; 4258c2ecf20Sopenharmony_ci txd = &priv->tx_descs[next]; 4268c2ecf20Sopenharmony_ci desc = &txd->desc[0]; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci next = (next + 1) % TX_DESC_COUNT; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (align) { 4318c2ecf20Sopenharmony_ci memcpy(txd->buf, skb->data, align); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci desc->s_addr = 4348c2ecf20Sopenharmony_ci txb->dma_desc + offsetof(struct nb8800_tx_desc, buf); 4358c2ecf20Sopenharmony_ci desc->n_addr = txb->dma_desc + sizeof(txd->desc[0]); 4368c2ecf20Sopenharmony_ci desc->config = DESC_BTS(2) | DESC_DS | align; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci desc++; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci desc->s_addr = dma_addr; 4428c2ecf20Sopenharmony_ci desc->n_addr = priv->tx_bufs[next].dma_desc; 4438c2ecf20Sopenharmony_ci desc->config = DESC_BTS(2) | DESC_DS | DESC_EOF | dma_len; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (!xmit_more) 4468c2ecf20Sopenharmony_ci desc->config |= DESC_EOC; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci txb->skb = skb; 4498c2ecf20Sopenharmony_ci txb->dma_addr = dma_addr; 4508c2ecf20Sopenharmony_ci txb->dma_len = dma_len; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (!priv->tx_chain) { 4538c2ecf20Sopenharmony_ci txb->chain_len = 1; 4548c2ecf20Sopenharmony_ci priv->tx_chain = txb; 4558c2ecf20Sopenharmony_ci } else { 4568c2ecf20Sopenharmony_ci priv->tx_chain->chain_len++; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci netdev_sent_queue(dev, skb->len); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci priv->tx_next = next; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (!xmit_more) { 4648c2ecf20Sopenharmony_ci smp_wmb(); 4658c2ecf20Sopenharmony_ci priv->tx_chain->ready = true; 4668c2ecf20Sopenharmony_ci priv->tx_chain = NULL; 4678c2ecf20Sopenharmony_ci nb8800_tx_dma_start(dev); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic void nb8800_tx_error(struct net_device *dev, u32 report) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci if (report & TX_LATE_COLLISION) 4768c2ecf20Sopenharmony_ci dev->stats.collisions++; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (report & TX_PACKET_DROPPED) 4798c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (report & TX_FIFO_UNDERRUN) 4828c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void nb8800_tx_done(struct net_device *dev) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 4908c2ecf20Sopenharmony_ci unsigned int limit = priv->tx_next; 4918c2ecf20Sopenharmony_ci unsigned int done = priv->tx_done; 4928c2ecf20Sopenharmony_ci unsigned int packets = 0; 4938c2ecf20Sopenharmony_ci unsigned int len = 0; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci while (done != limit) { 4968c2ecf20Sopenharmony_ci struct nb8800_tx_desc *txd = &priv->tx_descs[done]; 4978c2ecf20Sopenharmony_ci struct nb8800_tx_buf *txb = &priv->tx_bufs[done]; 4988c2ecf20Sopenharmony_ci struct sk_buff *skb; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci if (!txd->report) 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci skb = txb->skb; 5048c2ecf20Sopenharmony_ci len += skb->len; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci dma_unmap_single(&dev->dev, txb->dma_addr, txb->dma_len, 5078c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (IS_TX_ERROR(txd->report)) { 5108c2ecf20Sopenharmony_ci nb8800_tx_error(dev, txd->report); 5118c2ecf20Sopenharmony_ci kfree_skb(skb); 5128c2ecf20Sopenharmony_ci } else { 5138c2ecf20Sopenharmony_ci consume_skb(skb); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 5178c2ecf20Sopenharmony_ci dev->stats.tx_bytes += TX_BYTES_TRANSFERRED(txd->report); 5188c2ecf20Sopenharmony_ci dev->stats.collisions += TX_EARLY_COLLISIONS(txd->report); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci txb->skb = NULL; 5218c2ecf20Sopenharmony_ci txb->ready = false; 5228c2ecf20Sopenharmony_ci txd->report = 0; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci done = (done + 1) % TX_DESC_COUNT; 5258c2ecf20Sopenharmony_ci packets++; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (packets) { 5298c2ecf20Sopenharmony_ci smp_mb__before_atomic(); 5308c2ecf20Sopenharmony_ci atomic_add(packets, &priv->tx_free); 5318c2ecf20Sopenharmony_ci netdev_completed_queue(dev, packets, len); 5328c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5338c2ecf20Sopenharmony_ci priv->tx_done = done; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic irqreturn_t nb8800_irq(int irq, void *dev_id) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 5408c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 5418c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 5428c2ecf20Sopenharmony_ci u32 val; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* tx interrupt */ 5458c2ecf20Sopenharmony_ci val = nb8800_readl(priv, NB8800_TXC_SR); 5468c2ecf20Sopenharmony_ci if (val) { 5478c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TXC_SR, val); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (val & TSR_DI) 5508c2ecf20Sopenharmony_ci nb8800_tx_dma_start_irq(dev); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (val & TSR_TI) 5538c2ecf20Sopenharmony_ci napi_schedule_irqoff(&priv->napi); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (unlikely(val & TSR_DE)) 5568c2ecf20Sopenharmony_ci netdev_err(dev, "TX DMA error\n"); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* should never happen with automatic status retrieval */ 5598c2ecf20Sopenharmony_ci if (unlikely(val & TSR_TO)) 5608c2ecf20Sopenharmony_ci netdev_err(dev, "TX Status FIFO overflow\n"); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* rx interrupt */ 5668c2ecf20Sopenharmony_ci val = nb8800_readl(priv, NB8800_RXC_SR); 5678c2ecf20Sopenharmony_ci if (val) { 5688c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_RXC_SR, val); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (likely(val & (RSR_RI | RSR_DI))) { 5718c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_poll); 5728c2ecf20Sopenharmony_ci napi_schedule_irqoff(&priv->napi); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (unlikely(val & RSR_DE)) 5768c2ecf20Sopenharmony_ci netdev_err(dev, "RX DMA error\n"); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* should never happen with automatic status retrieval */ 5798c2ecf20Sopenharmony_ci if (unlikely(val & RSR_RO)) 5808c2ecf20Sopenharmony_ci netdev_err(dev, "RX Status FIFO overflow\n"); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return ret; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic void nb8800_mac_config(struct net_device *dev) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 5918c2ecf20Sopenharmony_ci bool gigabit = priv->speed == SPEED_1000; 5928c2ecf20Sopenharmony_ci u32 mac_mode_mask = RGMII_MODE | HALF_DUPLEX | GMAC_MODE; 5938c2ecf20Sopenharmony_ci u32 mac_mode = 0; 5948c2ecf20Sopenharmony_ci u32 slot_time; 5958c2ecf20Sopenharmony_ci u32 phy_clk; 5968c2ecf20Sopenharmony_ci u32 ict; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (!priv->duplex) 5998c2ecf20Sopenharmony_ci mac_mode |= HALF_DUPLEX; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (gigabit) { 6028c2ecf20Sopenharmony_ci if (phy_interface_is_rgmii(dev->phydev)) 6038c2ecf20Sopenharmony_ci mac_mode |= RGMII_MODE; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci mac_mode |= GMAC_MODE; 6068c2ecf20Sopenharmony_ci phy_clk = 125000000; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci /* Should be 512 but register is only 8 bits */ 6098c2ecf20Sopenharmony_ci slot_time = 255; 6108c2ecf20Sopenharmony_ci } else { 6118c2ecf20Sopenharmony_ci phy_clk = 25000000; 6128c2ecf20Sopenharmony_ci slot_time = 128; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci ict = DIV_ROUND_UP(phy_clk, clk_get_rate(priv->clk)); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_IC_THRESHOLD, ict); 6188c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_SLOT_TIME, slot_time); 6198c2ecf20Sopenharmony_ci nb8800_maskb(priv, NB8800_MAC_MODE, mac_mode_mask, mac_mode); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void nb8800_pause_config(struct net_device *dev) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 6258c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 6268c2ecf20Sopenharmony_ci u32 rxcr; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (priv->pause_aneg) { 6298c2ecf20Sopenharmony_ci if (!phydev || !phydev->link) 6308c2ecf20Sopenharmony_ci return; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci priv->pause_rx = phydev->pause; 6338c2ecf20Sopenharmony_ci priv->pause_tx = phydev->pause ^ phydev->asym_pause; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci nb8800_modb(priv, NB8800_RX_CTL, RX_PAUSE_EN, priv->pause_rx); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci rxcr = nb8800_readl(priv, NB8800_RXC_CR); 6398c2ecf20Sopenharmony_ci if (!!(rxcr & RCR_FL) == priv->pause_tx) 6408c2ecf20Sopenharmony_ci return; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (netif_running(dev)) { 6438c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 6448c2ecf20Sopenharmony_ci netif_tx_lock_bh(dev); 6458c2ecf20Sopenharmony_ci nb8800_dma_stop(dev); 6468c2ecf20Sopenharmony_ci nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx); 6478c2ecf20Sopenharmony_ci nb8800_start_rx(dev); 6488c2ecf20Sopenharmony_ci netif_tx_unlock_bh(dev); 6498c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 6508c2ecf20Sopenharmony_ci } else { 6518c2ecf20Sopenharmony_ci nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic void nb8800_link_reconfigure(struct net_device *dev) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 6588c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 6598c2ecf20Sopenharmony_ci int change = 0; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (phydev->link) { 6628c2ecf20Sopenharmony_ci if (phydev->speed != priv->speed) { 6638c2ecf20Sopenharmony_ci priv->speed = phydev->speed; 6648c2ecf20Sopenharmony_ci change = 1; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (phydev->duplex != priv->duplex) { 6688c2ecf20Sopenharmony_ci priv->duplex = phydev->duplex; 6698c2ecf20Sopenharmony_ci change = 1; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (change) 6738c2ecf20Sopenharmony_ci nb8800_mac_config(dev); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci nb8800_pause_config(dev); 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (phydev->link != priv->link) { 6798c2ecf20Sopenharmony_ci priv->link = phydev->link; 6808c2ecf20Sopenharmony_ci change = 1; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (change) 6848c2ecf20Sopenharmony_ci phy_print_status(phydev); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic void nb8800_update_mac_addr(struct net_device *dev) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 6908c2ecf20Sopenharmony_ci int i; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 6938c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_SRC_ADDR(i), dev->dev_addr[i]); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 6968c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_UC_ADDR(i), dev->dev_addr[i]); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int nb8800_set_mac_address(struct net_device *dev, void *addr) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct sockaddr *sock = addr; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (netif_running(dev)) 7048c2ecf20Sopenharmony_ci return -EBUSY; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci ether_addr_copy(dev->dev_addr, sock->sa_data); 7078c2ecf20Sopenharmony_ci nb8800_update_mac_addr(dev); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return 0; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic void nb8800_mc_init(struct net_device *dev, int val) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_MC_INIT, val); 7178c2ecf20Sopenharmony_ci readb_poll_timeout_atomic(priv->base + NB8800_MC_INIT, val, !val, 7188c2ecf20Sopenharmony_ci 1, 1000); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic void nb8800_set_rx_mode(struct net_device *dev) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 7248c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 7258c2ecf20Sopenharmony_ci int i; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { 7288c2ecf20Sopenharmony_ci nb8800_mac_af(dev, false); 7298c2ecf20Sopenharmony_ci return; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci nb8800_mac_af(dev, true); 7338c2ecf20Sopenharmony_ci nb8800_mc_init(dev, 0); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 7368c2ecf20Sopenharmony_ci for (i = 0; i < ETH_ALEN; i++) 7378c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_MC_ADDR(i), ha->addr[i]); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci nb8800_mc_init(dev, 0xff); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci#define RX_DESC_SIZE (RX_DESC_COUNT * sizeof(struct nb8800_rx_desc)) 7448c2ecf20Sopenharmony_ci#define TX_DESC_SIZE (TX_DESC_COUNT * sizeof(struct nb8800_tx_desc)) 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic void nb8800_dma_free(struct net_device *dev) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 7498c2ecf20Sopenharmony_ci unsigned int i; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (priv->rx_bufs) { 7528c2ecf20Sopenharmony_ci for (i = 0; i < RX_DESC_COUNT; i++) 7538c2ecf20Sopenharmony_ci if (priv->rx_bufs[i].page) 7548c2ecf20Sopenharmony_ci put_page(priv->rx_bufs[i].page); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci kfree(priv->rx_bufs); 7578c2ecf20Sopenharmony_ci priv->rx_bufs = NULL; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (priv->tx_bufs) { 7618c2ecf20Sopenharmony_ci for (i = 0; i < TX_DESC_COUNT; i++) 7628c2ecf20Sopenharmony_ci kfree_skb(priv->tx_bufs[i].skb); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci kfree(priv->tx_bufs); 7658c2ecf20Sopenharmony_ci priv->tx_bufs = NULL; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (priv->rx_descs) { 7698c2ecf20Sopenharmony_ci dma_free_coherent(dev->dev.parent, RX_DESC_SIZE, priv->rx_descs, 7708c2ecf20Sopenharmony_ci priv->rx_desc_dma); 7718c2ecf20Sopenharmony_ci priv->rx_descs = NULL; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (priv->tx_descs) { 7758c2ecf20Sopenharmony_ci dma_free_coherent(dev->dev.parent, TX_DESC_SIZE, priv->tx_descs, 7768c2ecf20Sopenharmony_ci priv->tx_desc_dma); 7778c2ecf20Sopenharmony_ci priv->tx_descs = NULL; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic void nb8800_dma_reset(struct net_device *dev) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 7848c2ecf20Sopenharmony_ci struct nb8800_rx_desc *rxd; 7858c2ecf20Sopenharmony_ci struct nb8800_tx_desc *txd; 7868c2ecf20Sopenharmony_ci unsigned int i; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci for (i = 0; i < RX_DESC_COUNT; i++) { 7898c2ecf20Sopenharmony_ci dma_addr_t rx_dma = priv->rx_desc_dma + i * sizeof(*rxd); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci rxd = &priv->rx_descs[i]; 7928c2ecf20Sopenharmony_ci rxd->desc.n_addr = rx_dma + sizeof(*rxd); 7938c2ecf20Sopenharmony_ci rxd->desc.r_addr = 7948c2ecf20Sopenharmony_ci rx_dma + offsetof(struct nb8800_rx_desc, report); 7958c2ecf20Sopenharmony_ci rxd->desc.config = priv->rx_dma_config; 7968c2ecf20Sopenharmony_ci rxd->report = 0; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci rxd->desc.n_addr = priv->rx_desc_dma; 8008c2ecf20Sopenharmony_ci rxd->desc.config |= DESC_EOC; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci priv->rx_eoc = RX_DESC_COUNT - 1; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci for (i = 0; i < TX_DESC_COUNT; i++) { 8058c2ecf20Sopenharmony_ci struct nb8800_tx_buf *txb = &priv->tx_bufs[i]; 8068c2ecf20Sopenharmony_ci dma_addr_t r_dma = txb->dma_desc + 8078c2ecf20Sopenharmony_ci offsetof(struct nb8800_tx_desc, report); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci txd = &priv->tx_descs[i]; 8108c2ecf20Sopenharmony_ci txd->desc[0].r_addr = r_dma; 8118c2ecf20Sopenharmony_ci txd->desc[1].r_addr = r_dma; 8128c2ecf20Sopenharmony_ci txd->report = 0; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci priv->tx_next = 0; 8168c2ecf20Sopenharmony_ci priv->tx_queue = 0; 8178c2ecf20Sopenharmony_ci priv->tx_done = 0; 8188c2ecf20Sopenharmony_ci atomic_set(&priv->tx_free, TX_DESC_COUNT); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_RX_DESC_ADDR, priv->rx_desc_dma); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci wmb(); /* ensure all setup is written before starting */ 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic int nb8800_dma_init(struct net_device *dev) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 8288c2ecf20Sopenharmony_ci unsigned int n_rx = RX_DESC_COUNT; 8298c2ecf20Sopenharmony_ci unsigned int n_tx = TX_DESC_COUNT; 8308c2ecf20Sopenharmony_ci unsigned int i; 8318c2ecf20Sopenharmony_ci int err; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci priv->rx_descs = dma_alloc_coherent(dev->dev.parent, RX_DESC_SIZE, 8348c2ecf20Sopenharmony_ci &priv->rx_desc_dma, GFP_KERNEL); 8358c2ecf20Sopenharmony_ci if (!priv->rx_descs) 8368c2ecf20Sopenharmony_ci goto err_out; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci priv->rx_bufs = kcalloc(n_rx, sizeof(*priv->rx_bufs), GFP_KERNEL); 8398c2ecf20Sopenharmony_ci if (!priv->rx_bufs) 8408c2ecf20Sopenharmony_ci goto err_out; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci for (i = 0; i < n_rx; i++) { 8438c2ecf20Sopenharmony_ci err = nb8800_alloc_rx(dev, i, false); 8448c2ecf20Sopenharmony_ci if (err) 8458c2ecf20Sopenharmony_ci goto err_out; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci priv->tx_descs = dma_alloc_coherent(dev->dev.parent, TX_DESC_SIZE, 8498c2ecf20Sopenharmony_ci &priv->tx_desc_dma, GFP_KERNEL); 8508c2ecf20Sopenharmony_ci if (!priv->tx_descs) 8518c2ecf20Sopenharmony_ci goto err_out; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci priv->tx_bufs = kcalloc(n_tx, sizeof(*priv->tx_bufs), GFP_KERNEL); 8548c2ecf20Sopenharmony_ci if (!priv->tx_bufs) 8558c2ecf20Sopenharmony_ci goto err_out; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci for (i = 0; i < n_tx; i++) 8588c2ecf20Sopenharmony_ci priv->tx_bufs[i].dma_desc = 8598c2ecf20Sopenharmony_ci priv->tx_desc_dma + i * sizeof(struct nb8800_tx_desc); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci nb8800_dma_reset(dev); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cierr_out: 8668c2ecf20Sopenharmony_ci nb8800_dma_free(dev); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci return -ENOMEM; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int nb8800_dma_stop(struct net_device *dev) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 8748c2ecf20Sopenharmony_ci struct nb8800_tx_buf *txb = &priv->tx_bufs[0]; 8758c2ecf20Sopenharmony_ci struct nb8800_tx_desc *txd = &priv->tx_descs[0]; 8768c2ecf20Sopenharmony_ci int retry = 5; 8778c2ecf20Sopenharmony_ci u32 txcr; 8788c2ecf20Sopenharmony_ci u32 rxcr; 8798c2ecf20Sopenharmony_ci int err; 8808c2ecf20Sopenharmony_ci unsigned int i; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci /* wait for tx to finish */ 8838c2ecf20Sopenharmony_ci err = readl_poll_timeout_atomic(priv->base + NB8800_TXC_CR, txcr, 8848c2ecf20Sopenharmony_ci !(txcr & TCR_EN) && 8858c2ecf20Sopenharmony_ci priv->tx_done == priv->tx_next, 8868c2ecf20Sopenharmony_ci 1000, 1000000); 8878c2ecf20Sopenharmony_ci if (err) 8888c2ecf20Sopenharmony_ci return err; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci /* The rx DMA only stops if it reaches the end of chain. 8918c2ecf20Sopenharmony_ci * To make this happen, we set the EOC flag on all rx 8928c2ecf20Sopenharmony_ci * descriptors, put the device in loopback mode, and send 8938c2ecf20Sopenharmony_ci * a few dummy frames. The interrupt handler will ignore 8948c2ecf20Sopenharmony_ci * these since NAPI is disabled and no real frames are in 8958c2ecf20Sopenharmony_ci * the tx queue. 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci for (i = 0; i < RX_DESC_COUNT; i++) 8998c2ecf20Sopenharmony_ci priv->rx_descs[i].desc.config |= DESC_EOC; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci txd->desc[0].s_addr = 9028c2ecf20Sopenharmony_ci txb->dma_desc + offsetof(struct nb8800_tx_desc, buf); 9038c2ecf20Sopenharmony_ci txd->desc[0].config = DESC_BTS(2) | DESC_DS | DESC_EOF | DESC_EOC | 8; 9048c2ecf20Sopenharmony_ci memset(txd->buf, 0, sizeof(txd->buf)); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci nb8800_mac_af(dev, false); 9078c2ecf20Sopenharmony_ci nb8800_setb(priv, NB8800_MAC_MODE, LOOPBACK_EN); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci do { 9108c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc); 9118c2ecf20Sopenharmony_ci wmb(); 9128c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TXC_CR, txcr | TCR_EN); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci err = readl_poll_timeout_atomic(priv->base + NB8800_RXC_CR, 9158c2ecf20Sopenharmony_ci rxcr, !(rxcr & RCR_EN), 9168c2ecf20Sopenharmony_ci 1000, 100000); 9178c2ecf20Sopenharmony_ci } while (err && --retry); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci nb8800_mac_af(dev, true); 9208c2ecf20Sopenharmony_ci nb8800_clearb(priv, NB8800_MAC_MODE, LOOPBACK_EN); 9218c2ecf20Sopenharmony_ci nb8800_dma_reset(dev); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci return retry ? 0 : -ETIMEDOUT; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic void nb8800_pause_adv(struct net_device *dev) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 9298c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (!phydev) 9328c2ecf20Sopenharmony_ci return; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci phy_set_asym_pause(phydev, priv->pause_rx, priv->pause_tx); 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int nb8800_open(struct net_device *dev) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 9408c2ecf20Sopenharmony_ci struct phy_device *phydev; 9418c2ecf20Sopenharmony_ci int err; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* clear any pending interrupts */ 9448c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_RXC_SR, 0xf); 9458c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TXC_SR, 0xf); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci err = nb8800_dma_init(dev); 9488c2ecf20Sopenharmony_ci if (err) 9498c2ecf20Sopenharmony_ci return err; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci err = request_irq(dev->irq, nb8800_irq, 0, dev_name(&dev->dev), dev); 9528c2ecf20Sopenharmony_ci if (err) 9538c2ecf20Sopenharmony_ci goto err_free_dma; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci nb8800_mac_rx(dev, true); 9568c2ecf20Sopenharmony_ci nb8800_mac_tx(dev, true); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci phydev = of_phy_connect(dev, priv->phy_node, 9598c2ecf20Sopenharmony_ci nb8800_link_reconfigure, 0, 9608c2ecf20Sopenharmony_ci priv->phy_mode); 9618c2ecf20Sopenharmony_ci if (!phydev) { 9628c2ecf20Sopenharmony_ci err = -ENODEV; 9638c2ecf20Sopenharmony_ci goto err_free_irq; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci nb8800_pause_adv(dev); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci netdev_reset_queue(dev); 9698c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 9708c2ecf20Sopenharmony_ci netif_start_queue(dev); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci nb8800_start_rx(dev); 9738c2ecf20Sopenharmony_ci phy_start(phydev); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return 0; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_cierr_free_irq: 9788c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 9798c2ecf20Sopenharmony_cierr_free_dma: 9808c2ecf20Sopenharmony_ci nb8800_dma_free(dev); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci return err; 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int nb8800_stop(struct net_device *dev) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 9888c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci phy_stop(phydev); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9938c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci nb8800_dma_stop(dev); 9968c2ecf20Sopenharmony_ci nb8800_mac_rx(dev, false); 9978c2ecf20Sopenharmony_ci nb8800_mac_tx(dev, false); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci phy_disconnect(phydev); 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci nb8800_dma_free(dev); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci return 0; 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic const struct net_device_ops nb8800_netdev_ops = { 10098c2ecf20Sopenharmony_ci .ndo_open = nb8800_open, 10108c2ecf20Sopenharmony_ci .ndo_stop = nb8800_stop, 10118c2ecf20Sopenharmony_ci .ndo_start_xmit = nb8800_xmit, 10128c2ecf20Sopenharmony_ci .ndo_set_mac_address = nb8800_set_mac_address, 10138c2ecf20Sopenharmony_ci .ndo_set_rx_mode = nb8800_set_rx_mode, 10148c2ecf20Sopenharmony_ci .ndo_do_ioctl = phy_do_ioctl, 10158c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 10168c2ecf20Sopenharmony_ci}; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_cistatic void nb8800_get_pauseparam(struct net_device *dev, 10198c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pp) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci pp->autoneg = priv->pause_aneg; 10248c2ecf20Sopenharmony_ci pp->rx_pause = priv->pause_rx; 10258c2ecf20Sopenharmony_ci pp->tx_pause = priv->pause_tx; 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic int nb8800_set_pauseparam(struct net_device *dev, 10298c2ecf20Sopenharmony_ci struct ethtool_pauseparam *pp) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 10328c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci priv->pause_aneg = pp->autoneg; 10358c2ecf20Sopenharmony_ci priv->pause_rx = pp->rx_pause; 10368c2ecf20Sopenharmony_ci priv->pause_tx = pp->tx_pause; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci nb8800_pause_adv(dev); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (!priv->pause_aneg) 10418c2ecf20Sopenharmony_ci nb8800_pause_config(dev); 10428c2ecf20Sopenharmony_ci else if (phydev) 10438c2ecf20Sopenharmony_ci phy_start_aneg(phydev); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci return 0; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic const char nb8800_stats_names[][ETH_GSTRING_LEN] = { 10498c2ecf20Sopenharmony_ci "rx_bytes_ok", 10508c2ecf20Sopenharmony_ci "rx_frames_ok", 10518c2ecf20Sopenharmony_ci "rx_undersize_frames", 10528c2ecf20Sopenharmony_ci "rx_fragment_frames", 10538c2ecf20Sopenharmony_ci "rx_64_byte_frames", 10548c2ecf20Sopenharmony_ci "rx_127_byte_frames", 10558c2ecf20Sopenharmony_ci "rx_255_byte_frames", 10568c2ecf20Sopenharmony_ci "rx_511_byte_frames", 10578c2ecf20Sopenharmony_ci "rx_1023_byte_frames", 10588c2ecf20Sopenharmony_ci "rx_max_size_frames", 10598c2ecf20Sopenharmony_ci "rx_oversize_frames", 10608c2ecf20Sopenharmony_ci "rx_bad_fcs_frames", 10618c2ecf20Sopenharmony_ci "rx_broadcast_frames", 10628c2ecf20Sopenharmony_ci "rx_multicast_frames", 10638c2ecf20Sopenharmony_ci "rx_control_frames", 10648c2ecf20Sopenharmony_ci "rx_pause_frames", 10658c2ecf20Sopenharmony_ci "rx_unsup_control_frames", 10668c2ecf20Sopenharmony_ci "rx_align_error_frames", 10678c2ecf20Sopenharmony_ci "rx_overrun_frames", 10688c2ecf20Sopenharmony_ci "rx_jabber_frames", 10698c2ecf20Sopenharmony_ci "rx_bytes", 10708c2ecf20Sopenharmony_ci "rx_frames", 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci "tx_bytes_ok", 10738c2ecf20Sopenharmony_ci "tx_frames_ok", 10748c2ecf20Sopenharmony_ci "tx_64_byte_frames", 10758c2ecf20Sopenharmony_ci "tx_127_byte_frames", 10768c2ecf20Sopenharmony_ci "tx_255_byte_frames", 10778c2ecf20Sopenharmony_ci "tx_511_byte_frames", 10788c2ecf20Sopenharmony_ci "tx_1023_byte_frames", 10798c2ecf20Sopenharmony_ci "tx_max_size_frames", 10808c2ecf20Sopenharmony_ci "tx_oversize_frames", 10818c2ecf20Sopenharmony_ci "tx_broadcast_frames", 10828c2ecf20Sopenharmony_ci "tx_multicast_frames", 10838c2ecf20Sopenharmony_ci "tx_control_frames", 10848c2ecf20Sopenharmony_ci "tx_pause_frames", 10858c2ecf20Sopenharmony_ci "tx_underrun_frames", 10868c2ecf20Sopenharmony_ci "tx_single_collision_frames", 10878c2ecf20Sopenharmony_ci "tx_multi_collision_frames", 10888c2ecf20Sopenharmony_ci "tx_deferred_collision_frames", 10898c2ecf20Sopenharmony_ci "tx_late_collision_frames", 10908c2ecf20Sopenharmony_ci "tx_excessive_collision_frames", 10918c2ecf20Sopenharmony_ci "tx_bytes", 10928c2ecf20Sopenharmony_ci "tx_frames", 10938c2ecf20Sopenharmony_ci "tx_collisions", 10948c2ecf20Sopenharmony_ci}; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci#define NB8800_NUM_STATS ARRAY_SIZE(nb8800_stats_names) 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic int nb8800_get_sset_count(struct net_device *dev, int sset) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) 11018c2ecf20Sopenharmony_ci return NB8800_NUM_STATS; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic void nb8800_get_strings(struct net_device *dev, u32 sset, u8 *buf) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) 11098c2ecf20Sopenharmony_ci memcpy(buf, &nb8800_stats_names, sizeof(nb8800_stats_names)); 11108c2ecf20Sopenharmony_ci} 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic u32 nb8800_read_stat(struct net_device *dev, int index) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_STAT_INDEX, index); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci return nb8800_readl(priv, NB8800_STAT_DATA); 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic void nb8800_get_ethtool_stats(struct net_device *dev, 11228c2ecf20Sopenharmony_ci struct ethtool_stats *estats, u64 *st) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci unsigned int i; 11258c2ecf20Sopenharmony_ci u32 rx, tx; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci for (i = 0; i < NB8800_NUM_STATS / 2; i++) { 11288c2ecf20Sopenharmony_ci rx = nb8800_read_stat(dev, i); 11298c2ecf20Sopenharmony_ci tx = nb8800_read_stat(dev, i | 0x80); 11308c2ecf20Sopenharmony_ci st[i] = rx; 11318c2ecf20Sopenharmony_ci st[i + NB8800_NUM_STATS / 2] = tx; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cistatic const struct ethtool_ops nb8800_ethtool_ops = { 11368c2ecf20Sopenharmony_ci .nway_reset = phy_ethtool_nway_reset, 11378c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 11388c2ecf20Sopenharmony_ci .get_pauseparam = nb8800_get_pauseparam, 11398c2ecf20Sopenharmony_ci .set_pauseparam = nb8800_set_pauseparam, 11408c2ecf20Sopenharmony_ci .get_sset_count = nb8800_get_sset_count, 11418c2ecf20Sopenharmony_ci .get_strings = nb8800_get_strings, 11428c2ecf20Sopenharmony_ci .get_ethtool_stats = nb8800_get_ethtool_stats, 11438c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 11448c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 11458c2ecf20Sopenharmony_ci}; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int nb8800_hw_init(struct net_device *dev) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 11508c2ecf20Sopenharmony_ci u32 val; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci val = TX_RETRY_EN | TX_PAD_EN | TX_APPEND_FCS; 11538c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_TX_CTL1, val); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* Collision retry count */ 11568c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_TX_CTL2, 5); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci val = RX_PAD_STRIP | RX_AF_EN; 11598c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_RX_CTL, val); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* Chosen by fair dice roll */ 11628c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_RANDOM_SEED, 4); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* TX cycles per deferral period */ 11658c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_TX_SDP, 12); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci /* The following three threshold values have been 11688c2ecf20Sopenharmony_ci * experimentally determined for good results. 11698c2ecf20Sopenharmony_ci */ 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci /* RX/TX FIFO threshold for partial empty (64-bit entries) */ 11728c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_PE_THRESHOLD, 0); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* RX/TX FIFO threshold for partial full (64-bit entries) */ 11758c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_PF_THRESHOLD, 255); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* Buffer size for transmit (64-bit entries) */ 11788c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_TX_BUFSIZE, 64); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Configure tx DMA */ 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci val = nb8800_readl(priv, NB8800_TXC_CR); 11838c2ecf20Sopenharmony_ci val &= TCR_LE; /* keep endian setting */ 11848c2ecf20Sopenharmony_ci val |= TCR_DM; /* DMA descriptor mode */ 11858c2ecf20Sopenharmony_ci val |= TCR_RS; /* automatically store tx status */ 11868c2ecf20Sopenharmony_ci val |= TCR_DIE; /* interrupt on DMA chain completion */ 11878c2ecf20Sopenharmony_ci val |= TCR_TFI(7); /* interrupt after 7 frames transmitted */ 11888c2ecf20Sopenharmony_ci val |= TCR_BTS(2); /* 32-byte bus transaction size */ 11898c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TXC_CR, val); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci /* TX complete interrupt after 10 ms or 7 frames (see above) */ 11928c2ecf20Sopenharmony_ci val = clk_get_rate(priv->clk) / 100; 11938c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_TX_ITR, val); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci /* Configure rx DMA */ 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci val = nb8800_readl(priv, NB8800_RXC_CR); 11988c2ecf20Sopenharmony_ci val &= RCR_LE; /* keep endian setting */ 11998c2ecf20Sopenharmony_ci val |= RCR_DM; /* DMA descriptor mode */ 12008c2ecf20Sopenharmony_ci val |= RCR_RS; /* automatically store rx status */ 12018c2ecf20Sopenharmony_ci val |= RCR_DIE; /* interrupt at end of DMA chain */ 12028c2ecf20Sopenharmony_ci val |= RCR_RFI(7); /* interrupt after 7 frames received */ 12038c2ecf20Sopenharmony_ci val |= RCR_BTS(2); /* 32-byte bus transaction size */ 12048c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_RXC_CR, val); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* The rx interrupt can fire before the DMA has completed 12078c2ecf20Sopenharmony_ci * unless a small delay is added. 50 us is hopefully enough. 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci priv->rx_itr_irq = clk_get_rate(priv->clk) / 20000; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci /* In NAPI poll mode we want to disable interrupts, but the 12128c2ecf20Sopenharmony_ci * hardware does not permit this. Delay 10 ms instead. 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_ci priv->rx_itr_poll = clk_get_rate(priv->clk) / 100; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci priv->rx_dma_config = RX_BUF_SIZE | DESC_BTS(2) | DESC_DS | DESC_EOF; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* Flow control settings */ 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* Pause time of 0.1 ms */ 12238c2ecf20Sopenharmony_ci val = 100000 / 512; 12248c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_PQ1, val >> 8); 12258c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_PQ2, val & 0xff); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* Auto-negotiate by default */ 12288c2ecf20Sopenharmony_ci priv->pause_aneg = true; 12298c2ecf20Sopenharmony_ci priv->pause_rx = true; 12308c2ecf20Sopenharmony_ci priv->pause_tx = true; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci nb8800_mc_init(dev, 0); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci return 0; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int nb8800_tangox_init(struct net_device *dev) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 12408c2ecf20Sopenharmony_ci u32 pad_mode = PAD_MODE_MII; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci switch (priv->phy_mode) { 12438c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 12448c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_GMII: 12458c2ecf20Sopenharmony_ci pad_mode = PAD_MODE_MII; 12468c2ecf20Sopenharmony_ci break; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 12498c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 12508c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 12518c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 12528c2ecf20Sopenharmony_ci pad_mode = PAD_MODE_RGMII; 12538c2ecf20Sopenharmony_ci break; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci default: 12568c2ecf20Sopenharmony_ci dev_err(dev->dev.parent, "unsupported phy mode %s\n", 12578c2ecf20Sopenharmony_ci phy_modes(priv->phy_mode)); 12588c2ecf20Sopenharmony_ci return -EINVAL; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_TANGOX_PAD_MODE, pad_mode); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic int nb8800_tangox_reset(struct net_device *dev) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 12698c2ecf20Sopenharmony_ci int clk_div; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_TANGOX_RESET, 0); 12728c2ecf20Sopenharmony_ci usleep_range(1000, 10000); 12738c2ecf20Sopenharmony_ci nb8800_writeb(priv, NB8800_TANGOX_RESET, 1); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci wmb(); /* ensure reset is cleared before proceeding */ 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci clk_div = DIV_ROUND_UP(clk_get_rate(priv->clk), 2 * MAX_MDC_CLOCK); 12788c2ecf20Sopenharmony_ci nb8800_writew(priv, NB8800_TANGOX_MDIO_CLKDIV, clk_div); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_cistatic const struct nb8800_ops nb8800_tangox_ops = { 12848c2ecf20Sopenharmony_ci .init = nb8800_tangox_init, 12858c2ecf20Sopenharmony_ci .reset = nb8800_tangox_reset, 12868c2ecf20Sopenharmony_ci}; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_cistatic int nb8800_tango4_init(struct net_device *dev) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(dev); 12918c2ecf20Sopenharmony_ci int err; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci err = nb8800_tangox_init(dev); 12948c2ecf20Sopenharmony_ci if (err) 12958c2ecf20Sopenharmony_ci return err; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci /* On tango4 interrupt on DMA completion per frame works and gives 12988c2ecf20Sopenharmony_ci * better performance despite generating more rx interrupts. 12998c2ecf20Sopenharmony_ci */ 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* Disable unnecessary interrupt on rx completion */ 13028c2ecf20Sopenharmony_ci nb8800_clearl(priv, NB8800_RXC_CR, RCR_RFI(7)); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci /* Request interrupt on descriptor DMA completion */ 13058c2ecf20Sopenharmony_ci priv->rx_dma_config |= DESC_ID; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return 0; 13088c2ecf20Sopenharmony_ci} 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistatic const struct nb8800_ops nb8800_tango4_ops = { 13118c2ecf20Sopenharmony_ci .init = nb8800_tango4_init, 13128c2ecf20Sopenharmony_ci .reset = nb8800_tangox_reset, 13138c2ecf20Sopenharmony_ci}; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_cistatic const struct of_device_id nb8800_dt_ids[] = { 13168c2ecf20Sopenharmony_ci { 13178c2ecf20Sopenharmony_ci .compatible = "aurora,nb8800", 13188c2ecf20Sopenharmony_ci }, 13198c2ecf20Sopenharmony_ci { 13208c2ecf20Sopenharmony_ci .compatible = "sigma,smp8642-ethernet", 13218c2ecf20Sopenharmony_ci .data = &nb8800_tangox_ops, 13228c2ecf20Sopenharmony_ci }, 13238c2ecf20Sopenharmony_ci { 13248c2ecf20Sopenharmony_ci .compatible = "sigma,smp8734-ethernet", 13258c2ecf20Sopenharmony_ci .data = &nb8800_tango4_ops, 13268c2ecf20Sopenharmony_ci }, 13278c2ecf20Sopenharmony_ci { } 13288c2ecf20Sopenharmony_ci}; 13298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, nb8800_dt_ids); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_cistatic int nb8800_probe(struct platform_device *pdev) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci const struct of_device_id *match; 13348c2ecf20Sopenharmony_ci const struct nb8800_ops *ops = NULL; 13358c2ecf20Sopenharmony_ci struct nb8800_priv *priv; 13368c2ecf20Sopenharmony_ci struct resource *res; 13378c2ecf20Sopenharmony_ci struct net_device *dev; 13388c2ecf20Sopenharmony_ci struct mii_bus *bus; 13398c2ecf20Sopenharmony_ci const unsigned char *mac; 13408c2ecf20Sopenharmony_ci void __iomem *base; 13418c2ecf20Sopenharmony_ci int irq; 13428c2ecf20Sopenharmony_ci int ret; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci match = of_match_device(nb8800_dt_ids, &pdev->dev); 13458c2ecf20Sopenharmony_ci if (match) 13468c2ecf20Sopenharmony_ci ops = match->data; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 13498c2ecf20Sopenharmony_ci if (irq <= 0) 13508c2ecf20Sopenharmony_ci return -EINVAL; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 13538c2ecf20Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, res); 13548c2ecf20Sopenharmony_ci if (IS_ERR(base)) 13558c2ecf20Sopenharmony_ci return PTR_ERR(base); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "AU-NB8800 Ethernet at %pa\n", &res->start); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*priv)); 13608c2ecf20Sopenharmony_ci if (!dev) 13618c2ecf20Sopenharmony_ci return -ENOMEM; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 13648c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 13678c2ecf20Sopenharmony_ci priv->base = base; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci ret = of_get_phy_mode(pdev->dev.of_node, &priv->phy_mode); 13708c2ecf20Sopenharmony_ci if (ret) 13718c2ecf20Sopenharmony_ci priv->phy_mode = PHY_INTERFACE_MODE_RGMII; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(&pdev->dev, NULL); 13748c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 13758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get clock\n"); 13768c2ecf20Sopenharmony_ci ret = PTR_ERR(priv->clk); 13778c2ecf20Sopenharmony_ci goto err_free_dev; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clk); 13818c2ecf20Sopenharmony_ci if (ret) 13828c2ecf20Sopenharmony_ci goto err_free_dev; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci spin_lock_init(&priv->tx_lock); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (ops && ops->reset) { 13878c2ecf20Sopenharmony_ci ret = ops->reset(dev); 13888c2ecf20Sopenharmony_ci if (ret) 13898c2ecf20Sopenharmony_ci goto err_disable_clk; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci bus = devm_mdiobus_alloc(&pdev->dev); 13938c2ecf20Sopenharmony_ci if (!bus) { 13948c2ecf20Sopenharmony_ci ret = -ENOMEM; 13958c2ecf20Sopenharmony_ci goto err_disable_clk; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci bus->name = "nb8800-mii"; 13998c2ecf20Sopenharmony_ci bus->read = nb8800_mdio_read; 14008c2ecf20Sopenharmony_ci bus->write = nb8800_mdio_write; 14018c2ecf20Sopenharmony_ci bus->parent = &pdev->dev; 14028c2ecf20Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%lx.nb8800-mii", 14038c2ecf20Sopenharmony_ci (unsigned long)res->start); 14048c2ecf20Sopenharmony_ci bus->priv = priv; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci ret = of_mdiobus_register(bus, pdev->dev.of_node); 14078c2ecf20Sopenharmony_ci if (ret) { 14088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register MII bus\n"); 14098c2ecf20Sopenharmony_ci goto err_disable_clk; 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (of_phy_is_fixed_link(pdev->dev.of_node)) { 14138c2ecf20Sopenharmony_ci ret = of_phy_register_fixed_link(pdev->dev.of_node); 14148c2ecf20Sopenharmony_ci if (ret < 0) { 14158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "bad fixed-link spec\n"); 14168c2ecf20Sopenharmony_ci goto err_free_bus; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci priv->phy_node = of_node_get(pdev->dev.of_node); 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (!priv->phy_node) 14228c2ecf20Sopenharmony_ci priv->phy_node = of_parse_phandle(pdev->dev.of_node, 14238c2ecf20Sopenharmony_ci "phy-handle", 0); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (!priv->phy_node) { 14268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no PHY specified\n"); 14278c2ecf20Sopenharmony_ci ret = -ENODEV; 14288c2ecf20Sopenharmony_ci goto err_free_bus; 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci priv->mii_bus = bus; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci ret = nb8800_hw_init(dev); 14348c2ecf20Sopenharmony_ci if (ret) 14358c2ecf20Sopenharmony_ci goto err_deregister_fixed_link; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (ops && ops->init) { 14388c2ecf20Sopenharmony_ci ret = ops->init(dev); 14398c2ecf20Sopenharmony_ci if (ret) 14408c2ecf20Sopenharmony_ci goto err_deregister_fixed_link; 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci dev->netdev_ops = &nb8800_netdev_ops; 14448c2ecf20Sopenharmony_ci dev->ethtool_ops = &nb8800_ethtool_ops; 14458c2ecf20Sopenharmony_ci dev->flags |= IFF_MULTICAST; 14468c2ecf20Sopenharmony_ci dev->irq = irq; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci mac = of_get_mac_address(pdev->dev.of_node); 14498c2ecf20Sopenharmony_ci if (!IS_ERR(mac)) 14508c2ecf20Sopenharmony_ci ether_addr_copy(dev->dev_addr, mac); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(dev->dev_addr)) 14538c2ecf20Sopenharmony_ci eth_hw_addr_random(dev); 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci nb8800_update_mac_addr(dev); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci netif_carrier_off(dev); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci ret = register_netdev(dev); 14608c2ecf20Sopenharmony_ci if (ret) { 14618c2ecf20Sopenharmony_ci netdev_err(dev, "failed to register netdev\n"); 14628c2ecf20Sopenharmony_ci goto err_free_dma; 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci netif_napi_add(dev, &priv->napi, nb8800_poll, NAPI_POLL_WEIGHT); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci netdev_info(dev, "MAC address %pM\n", dev->dev_addr); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci return 0; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_cierr_free_dma: 14728c2ecf20Sopenharmony_ci nb8800_dma_free(dev); 14738c2ecf20Sopenharmony_cierr_deregister_fixed_link: 14748c2ecf20Sopenharmony_ci if (of_phy_is_fixed_link(pdev->dev.of_node)) 14758c2ecf20Sopenharmony_ci of_phy_deregister_fixed_link(pdev->dev.of_node); 14768c2ecf20Sopenharmony_cierr_free_bus: 14778c2ecf20Sopenharmony_ci of_node_put(priv->phy_node); 14788c2ecf20Sopenharmony_ci mdiobus_unregister(bus); 14798c2ecf20Sopenharmony_cierr_disable_clk: 14808c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 14818c2ecf20Sopenharmony_cierr_free_dev: 14828c2ecf20Sopenharmony_ci free_netdev(dev); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci return ret; 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic int nb8800_remove(struct platform_device *pdev) 14888c2ecf20Sopenharmony_ci{ 14898c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 14908c2ecf20Sopenharmony_ci struct nb8800_priv *priv = netdev_priv(ndev); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci unregister_netdev(ndev); 14938c2ecf20Sopenharmony_ci if (of_phy_is_fixed_link(pdev->dev.of_node)) 14948c2ecf20Sopenharmony_ci of_phy_deregister_fixed_link(pdev->dev.of_node); 14958c2ecf20Sopenharmony_ci of_node_put(priv->phy_node); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci mdiobus_unregister(priv->mii_bus); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci nb8800_dma_free(ndev); 15028c2ecf20Sopenharmony_ci free_netdev(ndev); 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci return 0; 15058c2ecf20Sopenharmony_ci} 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_cistatic struct platform_driver nb8800_driver = { 15088c2ecf20Sopenharmony_ci .driver = { 15098c2ecf20Sopenharmony_ci .name = "nb8800", 15108c2ecf20Sopenharmony_ci .of_match_table = nb8800_dt_ids, 15118c2ecf20Sopenharmony_ci }, 15128c2ecf20Sopenharmony_ci .probe = nb8800_probe, 15138c2ecf20Sopenharmony_ci .remove = nb8800_remove, 15148c2ecf20Sopenharmony_ci}; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cimodule_platform_driver(nb8800_driver); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Aurora AU-NB8800 Ethernet driver"); 15198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mans Rullgard <mans@mansr.com>"); 15208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1521