18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Gigabit Ethernet adapters based on the Session Layer 48c2ecf20Sopenharmony_ci * Interface (SLIC) technology by Alacritech. The driver does not 58c2ecf20Sopenharmony_ci * support the hardware acceleration features provided by these cards. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2016 Lino Sanfilippo <LinoSanfilippo@gmx.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 168c2ecf20Sopenharmony_ci#include <linux/crc32.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 198c2ecf20Sopenharmony_ci#include <linux/mii.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/firmware.h> 238c2ecf20Sopenharmony_ci#include <linux/list.h> 248c2ecf20Sopenharmony_ci#include <linux/u64_stats_sync.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "slic.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define DRV_NAME "slicoss" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic const struct pci_device_id slic_id_tbl[] = { 318c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH, 328c2ecf20Sopenharmony_ci PCI_DEVICE_ID_ALACRITECH_MOJAVE) }, 338c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_ALACRITECH, 348c2ecf20Sopenharmony_ci PCI_DEVICE_ID_ALACRITECH_OASIS) }, 358c2ecf20Sopenharmony_ci { 0 } 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic const char slic_stats_strings[][ETH_GSTRING_LEN] = { 398c2ecf20Sopenharmony_ci "rx_packets", 408c2ecf20Sopenharmony_ci "rx_bytes", 418c2ecf20Sopenharmony_ci "rx_multicasts", 428c2ecf20Sopenharmony_ci "rx_errors", 438c2ecf20Sopenharmony_ci "rx_buff_miss", 448c2ecf20Sopenharmony_ci "rx_tp_csum", 458c2ecf20Sopenharmony_ci "rx_tp_oflow", 468c2ecf20Sopenharmony_ci "rx_tp_hlen", 478c2ecf20Sopenharmony_ci "rx_ip_csum", 488c2ecf20Sopenharmony_ci "rx_ip_len", 498c2ecf20Sopenharmony_ci "rx_ip_hdr_len", 508c2ecf20Sopenharmony_ci "rx_early", 518c2ecf20Sopenharmony_ci "rx_buff_oflow", 528c2ecf20Sopenharmony_ci "rx_lcode", 538c2ecf20Sopenharmony_ci "rx_drbl", 548c2ecf20Sopenharmony_ci "rx_crc", 558c2ecf20Sopenharmony_ci "rx_oflow_802", 568c2ecf20Sopenharmony_ci "rx_uflow_802", 578c2ecf20Sopenharmony_ci "tx_packets", 588c2ecf20Sopenharmony_ci "tx_bytes", 598c2ecf20Sopenharmony_ci "tx_carrier", 608c2ecf20Sopenharmony_ci "tx_dropped", 618c2ecf20Sopenharmony_ci "irq_errs", 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic inline int slic_next_queue_idx(unsigned int idx, unsigned int qlen) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci return (idx + 1) & (qlen - 1); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline int slic_get_free_queue_descs(unsigned int put_idx, 708c2ecf20Sopenharmony_ci unsigned int done_idx, 718c2ecf20Sopenharmony_ci unsigned int qlen) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci if (put_idx >= done_idx) 748c2ecf20Sopenharmony_ci return (qlen - (put_idx - done_idx) - 1); 758c2ecf20Sopenharmony_ci return (done_idx - put_idx - 1); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic unsigned int slic_next_compl_idx(struct slic_device *sdev) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct slic_stat_queue *stq = &sdev->stq; 818c2ecf20Sopenharmony_ci unsigned int active = stq->active_array; 828c2ecf20Sopenharmony_ci struct slic_stat_desc *descs; 838c2ecf20Sopenharmony_ci struct slic_stat_desc *stat; 848c2ecf20Sopenharmony_ci unsigned int idx; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci descs = stq->descs[active]; 878c2ecf20Sopenharmony_ci stat = &descs[stq->done_idx]; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!stat->status) 908c2ecf20Sopenharmony_ci return SLIC_INVALID_STAT_DESC_IDX; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci idx = (le32_to_cpu(stat->hnd) & 0xffff) - 1; 938c2ecf20Sopenharmony_ci /* reset desc */ 948c2ecf20Sopenharmony_ci stat->hnd = 0; 958c2ecf20Sopenharmony_ci stat->status = 0; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci stq->done_idx = slic_next_queue_idx(stq->done_idx, stq->len); 988c2ecf20Sopenharmony_ci /* check for wraparound */ 998c2ecf20Sopenharmony_ci if (!stq->done_idx) { 1008c2ecf20Sopenharmony_ci dma_addr_t paddr = stq->paddr[active]; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) | 1038c2ecf20Sopenharmony_ci stq->len); 1048c2ecf20Sopenharmony_ci /* make sure new status descriptors are immediately available */ 1058c2ecf20Sopenharmony_ci slic_flush_write(sdev); 1068c2ecf20Sopenharmony_ci active++; 1078c2ecf20Sopenharmony_ci active &= (SLIC_NUM_STAT_DESC_ARRAYS - 1); 1088c2ecf20Sopenharmony_ci stq->active_array = active; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci return idx; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic unsigned int slic_get_free_tx_descs(struct slic_tx_queue *txq) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci /* ensure tail idx is updated */ 1168c2ecf20Sopenharmony_ci smp_mb(); 1178c2ecf20Sopenharmony_ci return slic_get_free_queue_descs(txq->put_idx, txq->done_idx, txq->len); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic unsigned int slic_get_free_rx_descs(struct slic_rx_queue *rxq) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return slic_get_free_queue_descs(rxq->put_idx, rxq->done_idx, rxq->len); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void slic_clear_upr_list(struct slic_upr_list *upr_list) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct slic_upr *upr; 1288c2ecf20Sopenharmony_ci struct slic_upr *tmp; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci spin_lock_bh(&upr_list->lock); 1318c2ecf20Sopenharmony_ci list_for_each_entry_safe(upr, tmp, &upr_list->list, list) { 1328c2ecf20Sopenharmony_ci list_del(&upr->list); 1338c2ecf20Sopenharmony_ci kfree(upr); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci upr_list->pending = false; 1368c2ecf20Sopenharmony_ci spin_unlock_bh(&upr_list->lock); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic void slic_start_upr(struct slic_device *sdev, struct slic_upr *upr) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci u32 reg; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci reg = (upr->type == SLIC_UPR_CONFIG) ? SLIC_REG_RCONFIG : 1448c2ecf20Sopenharmony_ci SLIC_REG_LSTAT; 1458c2ecf20Sopenharmony_ci slic_write(sdev, reg, lower_32_bits(upr->paddr)); 1468c2ecf20Sopenharmony_ci slic_flush_write(sdev); 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void slic_queue_upr(struct slic_device *sdev, struct slic_upr *upr) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct slic_upr_list *upr_list = &sdev->upr_list; 1528c2ecf20Sopenharmony_ci bool pending; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci spin_lock_bh(&upr_list->lock); 1558c2ecf20Sopenharmony_ci pending = upr_list->pending; 1568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&upr->list); 1578c2ecf20Sopenharmony_ci list_add_tail(&upr->list, &upr_list->list); 1588c2ecf20Sopenharmony_ci upr_list->pending = true; 1598c2ecf20Sopenharmony_ci spin_unlock_bh(&upr_list->lock); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!pending) 1628c2ecf20Sopenharmony_ci slic_start_upr(sdev, upr); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct slic_upr *slic_dequeue_upr(struct slic_device *sdev) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct slic_upr_list *upr_list = &sdev->upr_list; 1688c2ecf20Sopenharmony_ci struct slic_upr *next_upr = NULL; 1698c2ecf20Sopenharmony_ci struct slic_upr *upr = NULL; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci spin_lock_bh(&upr_list->lock); 1728c2ecf20Sopenharmony_ci if (!list_empty(&upr_list->list)) { 1738c2ecf20Sopenharmony_ci upr = list_first_entry(&upr_list->list, struct slic_upr, list); 1748c2ecf20Sopenharmony_ci list_del(&upr->list); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (list_empty(&upr_list->list)) 1778c2ecf20Sopenharmony_ci upr_list->pending = false; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci next_upr = list_first_entry(&upr_list->list, 1808c2ecf20Sopenharmony_ci struct slic_upr, list); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci spin_unlock_bh(&upr_list->lock); 1838c2ecf20Sopenharmony_ci /* trigger processing of the next upr in list */ 1848c2ecf20Sopenharmony_ci if (next_upr) 1858c2ecf20Sopenharmony_ci slic_start_upr(sdev, next_upr); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return upr; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int slic_new_upr(struct slic_device *sdev, unsigned int type, 1918c2ecf20Sopenharmony_ci dma_addr_t paddr) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct slic_upr *upr; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci upr = kmalloc(sizeof(*upr), GFP_ATOMIC); 1968c2ecf20Sopenharmony_ci if (!upr) 1978c2ecf20Sopenharmony_ci return -ENOMEM; 1988c2ecf20Sopenharmony_ci upr->type = type; 1998c2ecf20Sopenharmony_ci upr->paddr = paddr; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci slic_queue_upr(sdev, upr); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void slic_set_mcast_bit(u64 *mcmask, unsigned char const *addr) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci u64 mask = *mcmask; 2098c2ecf20Sopenharmony_ci u8 crc; 2108c2ecf20Sopenharmony_ci /* Get the CRC polynomial for the mac address: we use bits 1-8 (lsb), 2118c2ecf20Sopenharmony_ci * bitwise reversed, msb (= lsb bit 0 before bitrev) is automatically 2128c2ecf20Sopenharmony_ci * discarded. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci crc = ether_crc(ETH_ALEN, addr) >> 23; 2158c2ecf20Sopenharmony_ci /* we only have space on the SLIC for 64 entries */ 2168c2ecf20Sopenharmony_ci crc &= 0x3F; 2178c2ecf20Sopenharmony_ci mask |= (u64)1 << crc; 2188c2ecf20Sopenharmony_ci *mcmask = mask; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* must be called with link_lock held */ 2228c2ecf20Sopenharmony_cistatic void slic_configure_rcv(struct slic_device *sdev) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci u32 val; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci val = SLIC_GRCR_RESET | SLIC_GRCR_ADDRAEN | SLIC_GRCR_RCVEN | 2278c2ecf20Sopenharmony_ci SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT | SLIC_GRCR_RCVBAD; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (sdev->duplex == DUPLEX_FULL) 2308c2ecf20Sopenharmony_ci val |= SLIC_GRCR_CTLEN; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (sdev->promisc) 2338c2ecf20Sopenharmony_ci val |= SLIC_GRCR_RCVALL; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WRCFG, val); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* must be called with link_lock held */ 2398c2ecf20Sopenharmony_cistatic void slic_configure_xmt(struct slic_device *sdev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci u32 val; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci val = SLIC_GXCR_RESET | SLIC_GXCR_XMTEN; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (sdev->duplex == DUPLEX_FULL) 2468c2ecf20Sopenharmony_ci val |= SLIC_GXCR_PAUSEEN; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WXCFG, val); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/* must be called with link_lock held */ 2528c2ecf20Sopenharmony_cistatic void slic_configure_mac(struct slic_device *sdev) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci u32 val; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (sdev->speed == SPEED_1000) { 2578c2ecf20Sopenharmony_ci val = SLIC_GMCR_GAPBB_1000 << SLIC_GMCR_GAPBB_SHIFT | 2588c2ecf20Sopenharmony_ci SLIC_GMCR_GAPR1_1000 << SLIC_GMCR_GAPR1_SHIFT | 2598c2ecf20Sopenharmony_ci SLIC_GMCR_GAPR2_1000 << SLIC_GMCR_GAPR2_SHIFT | 2608c2ecf20Sopenharmony_ci SLIC_GMCR_GBIT; /* enable GMII */ 2618c2ecf20Sopenharmony_ci } else { 2628c2ecf20Sopenharmony_ci val = SLIC_GMCR_GAPBB_100 << SLIC_GMCR_GAPBB_SHIFT | 2638c2ecf20Sopenharmony_ci SLIC_GMCR_GAPR1_100 << SLIC_GMCR_GAPR1_SHIFT | 2648c2ecf20Sopenharmony_ci SLIC_GMCR_GAPR2_100 << SLIC_GMCR_GAPR2_SHIFT; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (sdev->duplex == DUPLEX_FULL) 2688c2ecf20Sopenharmony_ci val |= SLIC_GMCR_FULLD; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WMCFG, val); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void slic_configure_link_locked(struct slic_device *sdev, int speed, 2748c2ecf20Sopenharmony_ci unsigned int duplex) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct net_device *dev = sdev->netdev; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (sdev->speed == speed && sdev->duplex == duplex) 2798c2ecf20Sopenharmony_ci return; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci sdev->speed = speed; 2828c2ecf20Sopenharmony_ci sdev->duplex = duplex; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (sdev->speed == SPEED_UNKNOWN) { 2858c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev)) 2868c2ecf20Sopenharmony_ci netif_carrier_off(dev); 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci /* (re)configure link settings */ 2898c2ecf20Sopenharmony_ci slic_configure_mac(sdev); 2908c2ecf20Sopenharmony_ci slic_configure_xmt(sdev); 2918c2ecf20Sopenharmony_ci slic_configure_rcv(sdev); 2928c2ecf20Sopenharmony_ci slic_flush_write(sdev); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (!netif_carrier_ok(dev)) 2958c2ecf20Sopenharmony_ci netif_carrier_on(dev); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic void slic_configure_link(struct slic_device *sdev, int speed, 3008c2ecf20Sopenharmony_ci unsigned int duplex) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci spin_lock_bh(&sdev->link_lock); 3038c2ecf20Sopenharmony_ci slic_configure_link_locked(sdev, speed, duplex); 3048c2ecf20Sopenharmony_ci spin_unlock_bh(&sdev->link_lock); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic void slic_set_rx_mode(struct net_device *dev) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 3108c2ecf20Sopenharmony_ci struct netdev_hw_addr *hwaddr; 3118c2ecf20Sopenharmony_ci bool set_promisc; 3128c2ecf20Sopenharmony_ci u64 mcmask; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { 3158c2ecf20Sopenharmony_ci /* Turn on all multicast addresses. We have to do this for 3168c2ecf20Sopenharmony_ci * promiscuous mode as well as ALLMCAST mode (it saves the 3178c2ecf20Sopenharmony_ci * microcode from having to keep state about the MAC 3188c2ecf20Sopenharmony_ci * configuration). 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_ci mcmask = ~(u64)0; 3218c2ecf20Sopenharmony_ci } else { 3228c2ecf20Sopenharmony_ci mcmask = 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(hwaddr, dev) { 3258c2ecf20Sopenharmony_ci slic_set_mcast_bit(&mcmask, hwaddr->addr); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_MCASTLOW, lower_32_bits(mcmask)); 3308c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_MCASTHIGH, upper_32_bits(mcmask)); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci set_promisc = !!(dev->flags & IFF_PROMISC); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci spin_lock_bh(&sdev->link_lock); 3358c2ecf20Sopenharmony_ci if (sdev->promisc != set_promisc) { 3368c2ecf20Sopenharmony_ci sdev->promisc = set_promisc; 3378c2ecf20Sopenharmony_ci slic_configure_rcv(sdev); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci spin_unlock_bh(&sdev->link_lock); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void slic_xmit_complete(struct slic_device *sdev) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct slic_tx_queue *txq = &sdev->txq; 3458c2ecf20Sopenharmony_ci struct net_device *dev = sdev->netdev; 3468c2ecf20Sopenharmony_ci struct slic_tx_buffer *buff; 3478c2ecf20Sopenharmony_ci unsigned int frames = 0; 3488c2ecf20Sopenharmony_ci unsigned int bytes = 0; 3498c2ecf20Sopenharmony_ci unsigned int idx; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Limit processing to SLIC_MAX_TX_COMPLETIONS frames to avoid that new 3528c2ecf20Sopenharmony_ci * completions during processing keeps the loop running endlessly. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci do { 3558c2ecf20Sopenharmony_ci idx = slic_next_compl_idx(sdev); 3568c2ecf20Sopenharmony_ci if (idx == SLIC_INVALID_STAT_DESC_IDX) 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci txq->done_idx = idx; 3608c2ecf20Sopenharmony_ci buff = &txq->txbuffs[idx]; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (unlikely(!buff->skb)) { 3638c2ecf20Sopenharmony_ci netdev_warn(dev, 3648c2ecf20Sopenharmony_ci "no skb found for desc idx %i\n", idx); 3658c2ecf20Sopenharmony_ci continue; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci dma_unmap_single(&sdev->pdev->dev, 3688c2ecf20Sopenharmony_ci dma_unmap_addr(buff, map_addr), 3698c2ecf20Sopenharmony_ci dma_unmap_len(buff, map_len), DMA_TO_DEVICE); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci bytes += buff->skb->len; 3728c2ecf20Sopenharmony_ci frames++; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci dev_kfree_skb_any(buff->skb); 3758c2ecf20Sopenharmony_ci buff->skb = NULL; 3768c2ecf20Sopenharmony_ci } while (frames < SLIC_MAX_TX_COMPLETIONS); 3778c2ecf20Sopenharmony_ci /* make sure xmit sees the new value for done_idx */ 3788c2ecf20Sopenharmony_ci smp_wmb(); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci u64_stats_update_begin(&sdev->stats.syncp); 3818c2ecf20Sopenharmony_ci sdev->stats.tx_bytes += bytes; 3828c2ecf20Sopenharmony_ci sdev->stats.tx_packets += frames; 3838c2ecf20Sopenharmony_ci u64_stats_update_end(&sdev->stats.syncp); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci netif_tx_lock(dev); 3868c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev) && 3878c2ecf20Sopenharmony_ci (slic_get_free_tx_descs(txq) >= SLIC_MIN_TX_WAKEUP_DESCS)) 3888c2ecf20Sopenharmony_ci netif_wake_queue(dev); 3898c2ecf20Sopenharmony_ci netif_tx_unlock(dev); 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic void slic_refill_rx_queue(struct slic_device *sdev, gfp_t gfp) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci const unsigned int ALIGN_MASK = SLIC_RX_BUFF_ALIGN - 1; 3958c2ecf20Sopenharmony_ci unsigned int maplen = SLIC_RX_BUFF_SIZE; 3968c2ecf20Sopenharmony_ci struct slic_rx_queue *rxq = &sdev->rxq; 3978c2ecf20Sopenharmony_ci struct net_device *dev = sdev->netdev; 3988c2ecf20Sopenharmony_ci struct slic_rx_buffer *buff; 3998c2ecf20Sopenharmony_ci struct slic_rx_desc *desc; 4008c2ecf20Sopenharmony_ci unsigned int misalign; 4018c2ecf20Sopenharmony_ci unsigned int offset; 4028c2ecf20Sopenharmony_ci struct sk_buff *skb; 4038c2ecf20Sopenharmony_ci dma_addr_t paddr; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci while (slic_get_free_rx_descs(rxq) > SLIC_MAX_REQ_RX_DESCS) { 4068c2ecf20Sopenharmony_ci skb = alloc_skb(maplen + ALIGN_MASK, gfp); 4078c2ecf20Sopenharmony_ci if (!skb) 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen, 4118c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4128c2ecf20Sopenharmony_ci if (dma_mapping_error(&sdev->pdev->dev, paddr)) { 4138c2ecf20Sopenharmony_ci netdev_err(dev, "mapping rx packet failed\n"); 4148c2ecf20Sopenharmony_ci /* drop skb */ 4158c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4168c2ecf20Sopenharmony_ci break; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci /* ensure head buffer descriptors are 256 byte aligned */ 4198c2ecf20Sopenharmony_ci offset = 0; 4208c2ecf20Sopenharmony_ci misalign = paddr & ALIGN_MASK; 4218c2ecf20Sopenharmony_ci if (misalign) { 4228c2ecf20Sopenharmony_ci offset = SLIC_RX_BUFF_ALIGN - misalign; 4238c2ecf20Sopenharmony_ci skb_reserve(skb, offset); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci /* the HW expects dma chunks for descriptor + frame data */ 4268c2ecf20Sopenharmony_ci desc = (struct slic_rx_desc *)skb->data; 4278c2ecf20Sopenharmony_ci /* temporarily sync descriptor for CPU to clear status */ 4288c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&sdev->pdev->dev, paddr, 4298c2ecf20Sopenharmony_ci offset + sizeof(*desc), 4308c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4318c2ecf20Sopenharmony_ci desc->status = 0; 4328c2ecf20Sopenharmony_ci /* return it to HW again */ 4338c2ecf20Sopenharmony_ci dma_sync_single_for_device(&sdev->pdev->dev, paddr, 4348c2ecf20Sopenharmony_ci offset + sizeof(*desc), 4358c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci buff = &rxq->rxbuffs[rxq->put_idx]; 4388c2ecf20Sopenharmony_ci buff->skb = skb; 4398c2ecf20Sopenharmony_ci dma_unmap_addr_set(buff, map_addr, paddr); 4408c2ecf20Sopenharmony_ci dma_unmap_len_set(buff, map_len, maplen); 4418c2ecf20Sopenharmony_ci buff->addr_offset = offset; 4428c2ecf20Sopenharmony_ci /* complete write to descriptor before it is handed to HW */ 4438c2ecf20Sopenharmony_ci wmb(); 4448c2ecf20Sopenharmony_ci /* head buffer descriptors are placed immediately before skb */ 4458c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_HBAR, lower_32_bits(paddr) + offset); 4468c2ecf20Sopenharmony_ci rxq->put_idx = slic_next_queue_idx(rxq->put_idx, rxq->len); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void slic_handle_frame_error(struct slic_device *sdev, 4518c2ecf20Sopenharmony_ci struct sk_buff *skb) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct slic_stats *stats = &sdev->stats; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (sdev->model == SLIC_MODEL_OASIS) { 4568c2ecf20Sopenharmony_ci struct slic_rx_info_oasis *info; 4578c2ecf20Sopenharmony_ci u32 status_b; 4588c2ecf20Sopenharmony_ci u32 status; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci info = (struct slic_rx_info_oasis *)skb->data; 4618c2ecf20Sopenharmony_ci status = le32_to_cpu(info->frame_status); 4628c2ecf20Sopenharmony_ci status_b = le32_to_cpu(info->frame_status_b); 4638c2ecf20Sopenharmony_ci /* transport layer */ 4648c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_TPCSUM) 4658c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_tpcsum); 4668c2ecf20Sopenharmony_ci if (status & SLIC_VRHSTAT_TPOFLO) 4678c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_tpoflow); 4688c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_TPHLEN) 4698c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_tphlen); 4708c2ecf20Sopenharmony_ci /* ip layer */ 4718c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_IPCSUM) 4728c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_ipcsum); 4738c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_IPLERR) 4748c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_iplen); 4758c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_IPHERR) 4768c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_iphlen); 4778c2ecf20Sopenharmony_ci /* link layer */ 4788c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_RCVE) 4798c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_early); 4808c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_BUFF) 4818c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_buffoflow); 4828c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_CODE) 4838c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_lcode); 4848c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_DRBL) 4858c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_drbl); 4868c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_CRC) 4878c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_crc); 4888c2ecf20Sopenharmony_ci if (status & SLIC_VRHSTAT_802OE) 4898c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_oflow802); 4908c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_802UE) 4918c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_uflow802); 4928c2ecf20Sopenharmony_ci if (status_b & SLIC_VRHSTATB_CARRE) 4938c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, tx_carrier); 4948c2ecf20Sopenharmony_ci } else { /* mojave */ 4958c2ecf20Sopenharmony_ci struct slic_rx_info_mojave *info; 4968c2ecf20Sopenharmony_ci u32 status; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci info = (struct slic_rx_info_mojave *)skb->data; 4998c2ecf20Sopenharmony_ci status = le32_to_cpu(info->frame_status); 5008c2ecf20Sopenharmony_ci /* transport layer */ 5018c2ecf20Sopenharmony_ci if (status & SLIC_VGBSTAT_XPERR) { 5028c2ecf20Sopenharmony_ci u32 xerr = status >> SLIC_VGBSTAT_XERRSHFT; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (xerr == SLIC_VGBSTAT_XCSERR) 5058c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_tpcsum); 5068c2ecf20Sopenharmony_ci if (xerr == SLIC_VGBSTAT_XUFLOW) 5078c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_tpoflow); 5088c2ecf20Sopenharmony_ci if (xerr == SLIC_VGBSTAT_XHLEN) 5098c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_tphlen); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci /* ip layer */ 5128c2ecf20Sopenharmony_ci if (status & SLIC_VGBSTAT_NETERR) { 5138c2ecf20Sopenharmony_ci u32 nerr = status >> SLIC_VGBSTAT_NERRSHFT & 5148c2ecf20Sopenharmony_ci SLIC_VGBSTAT_NERRMSK; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (nerr == SLIC_VGBSTAT_NCSERR) 5178c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_ipcsum); 5188c2ecf20Sopenharmony_ci if (nerr == SLIC_VGBSTAT_NUFLOW) 5198c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_iplen); 5208c2ecf20Sopenharmony_ci if (nerr == SLIC_VGBSTAT_NHLEN) 5218c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_iphlen); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci /* link layer */ 5248c2ecf20Sopenharmony_ci if (status & SLIC_VGBSTAT_LNKERR) { 5258c2ecf20Sopenharmony_ci u32 lerr = status & SLIC_VGBSTAT_LERRMSK; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (lerr == SLIC_VGBSTAT_LDEARLY) 5288c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_early); 5298c2ecf20Sopenharmony_ci if (lerr == SLIC_VGBSTAT_LBOFLO) 5308c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_buffoflow); 5318c2ecf20Sopenharmony_ci if (lerr == SLIC_VGBSTAT_LCODERR) 5328c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_lcode); 5338c2ecf20Sopenharmony_ci if (lerr == SLIC_VGBSTAT_LDBLNBL) 5348c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_drbl); 5358c2ecf20Sopenharmony_ci if (lerr == SLIC_VGBSTAT_LCRCERR) 5368c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_crc); 5378c2ecf20Sopenharmony_ci if (lerr == SLIC_VGBSTAT_LOFLO) 5388c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_oflow802); 5398c2ecf20Sopenharmony_ci if (lerr == SLIC_VGBSTAT_LUFLO) 5408c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_uflow802); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_errors); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic void slic_handle_receive(struct slic_device *sdev, unsigned int todo, 5478c2ecf20Sopenharmony_ci unsigned int *done) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct slic_rx_queue *rxq = &sdev->rxq; 5508c2ecf20Sopenharmony_ci struct net_device *dev = sdev->netdev; 5518c2ecf20Sopenharmony_ci struct slic_rx_buffer *buff; 5528c2ecf20Sopenharmony_ci struct slic_rx_desc *desc; 5538c2ecf20Sopenharmony_ci unsigned int frames = 0; 5548c2ecf20Sopenharmony_ci unsigned int bytes = 0; 5558c2ecf20Sopenharmony_ci struct sk_buff *skb; 5568c2ecf20Sopenharmony_ci u32 status; 5578c2ecf20Sopenharmony_ci u32 len; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci while (todo && (rxq->done_idx != rxq->put_idx)) { 5608c2ecf20Sopenharmony_ci buff = &rxq->rxbuffs[rxq->done_idx]; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci skb = buff->skb; 5638c2ecf20Sopenharmony_ci if (!skb) 5648c2ecf20Sopenharmony_ci break; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci desc = (struct slic_rx_desc *)skb->data; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&sdev->pdev->dev, 5698c2ecf20Sopenharmony_ci dma_unmap_addr(buff, map_addr), 5708c2ecf20Sopenharmony_ci buff->addr_offset + sizeof(*desc), 5718c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci status = le32_to_cpu(desc->status); 5748c2ecf20Sopenharmony_ci if (!(status & SLIC_IRHDDR_SVALID)) { 5758c2ecf20Sopenharmony_ci dma_sync_single_for_device(&sdev->pdev->dev, 5768c2ecf20Sopenharmony_ci dma_unmap_addr(buff, 5778c2ecf20Sopenharmony_ci map_addr), 5788c2ecf20Sopenharmony_ci buff->addr_offset + 5798c2ecf20Sopenharmony_ci sizeof(*desc), 5808c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5818c2ecf20Sopenharmony_ci break; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci buff->skb = NULL; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci dma_unmap_single(&sdev->pdev->dev, 5878c2ecf20Sopenharmony_ci dma_unmap_addr(buff, map_addr), 5888c2ecf20Sopenharmony_ci dma_unmap_len(buff, map_len), 5898c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* skip rx descriptor that is placed before the frame data */ 5928c2ecf20Sopenharmony_ci skb_reserve(skb, SLIC_RX_BUFF_HDR_SIZE); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (unlikely(status & SLIC_IRHDDR_ERR)) { 5958c2ecf20Sopenharmony_ci slic_handle_frame_error(sdev, skb); 5968c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 5978c2ecf20Sopenharmony_ci } else { 5988c2ecf20Sopenharmony_ci struct ethhdr *eh = (struct ethhdr *)skb->data; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (is_multicast_ether_addr(eh->h_dest)) 6018c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(&sdev->stats, rx_mcasts); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci len = le32_to_cpu(desc->length) & SLIC_IRHDDR_FLEN_MSK; 6048c2ecf20Sopenharmony_ci skb_put(skb, len); 6058c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 6068c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci napi_gro_receive(&sdev->napi, skb); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci bytes += len; 6118c2ecf20Sopenharmony_ci frames++; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci rxq->done_idx = slic_next_queue_idx(rxq->done_idx, rxq->len); 6148c2ecf20Sopenharmony_ci todo--; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci u64_stats_update_begin(&sdev->stats.syncp); 6188c2ecf20Sopenharmony_ci sdev->stats.rx_bytes += bytes; 6198c2ecf20Sopenharmony_ci sdev->stats.rx_packets += frames; 6208c2ecf20Sopenharmony_ci u64_stats_update_end(&sdev->stats.syncp); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci slic_refill_rx_queue(sdev, GFP_ATOMIC); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic void slic_handle_link_irq(struct slic_device *sdev) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct slic_shmem *sm = &sdev->shmem; 6288c2ecf20Sopenharmony_ci struct slic_shmem_data *sm_data = sm->shmem_data; 6298c2ecf20Sopenharmony_ci unsigned int duplex; 6308c2ecf20Sopenharmony_ci int speed; 6318c2ecf20Sopenharmony_ci u32 link; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci link = le32_to_cpu(sm_data->link); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (link & SLIC_GIG_LINKUP) { 6368c2ecf20Sopenharmony_ci if (link & SLIC_GIG_SPEED_1000) 6378c2ecf20Sopenharmony_ci speed = SPEED_1000; 6388c2ecf20Sopenharmony_ci else if (link & SLIC_GIG_SPEED_100) 6398c2ecf20Sopenharmony_ci speed = SPEED_100; 6408c2ecf20Sopenharmony_ci else 6418c2ecf20Sopenharmony_ci speed = SPEED_10; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci duplex = (link & SLIC_GIG_FULLDUPLEX) ? DUPLEX_FULL : 6448c2ecf20Sopenharmony_ci DUPLEX_HALF; 6458c2ecf20Sopenharmony_ci } else { 6468c2ecf20Sopenharmony_ci duplex = DUPLEX_UNKNOWN; 6478c2ecf20Sopenharmony_ci speed = SPEED_UNKNOWN; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci slic_configure_link(sdev, speed, duplex); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic void slic_handle_upr_irq(struct slic_device *sdev, u32 irqs) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct slic_upr *upr; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* remove upr that caused this irq (always the first entry in list) */ 6578c2ecf20Sopenharmony_ci upr = slic_dequeue_upr(sdev); 6588c2ecf20Sopenharmony_ci if (!upr) { 6598c2ecf20Sopenharmony_ci netdev_warn(sdev->netdev, "no upr found on list\n"); 6608c2ecf20Sopenharmony_ci return; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (upr->type == SLIC_UPR_LSTAT) { 6648c2ecf20Sopenharmony_ci if (unlikely(irqs & SLIC_ISR_UPCERR_MASK)) { 6658c2ecf20Sopenharmony_ci /* try again */ 6668c2ecf20Sopenharmony_ci slic_queue_upr(sdev, upr); 6678c2ecf20Sopenharmony_ci return; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci slic_handle_link_irq(sdev); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci kfree(upr); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic int slic_handle_link_change(struct slic_device *sdev) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci return slic_new_upr(sdev, SLIC_UPR_LSTAT, sdev->shmem.link_paddr); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void slic_handle_err_irq(struct slic_device *sdev, u32 isr) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct slic_stats *stats = &sdev->stats; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (isr & SLIC_ISR_RMISS) 6848c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, rx_buff_miss); 6858c2ecf20Sopenharmony_ci if (isr & SLIC_ISR_XDROP) 6868c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, tx_dropped); 6878c2ecf20Sopenharmony_ci if (!(isr & (SLIC_ISR_RMISS | SLIC_ISR_XDROP))) 6888c2ecf20Sopenharmony_ci SLIC_INC_STATS_COUNTER(stats, irq_errs); 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic void slic_handle_irq(struct slic_device *sdev, u32 isr, 6928c2ecf20Sopenharmony_ci unsigned int todo, unsigned int *done) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci if (isr & SLIC_ISR_ERR) 6958c2ecf20Sopenharmony_ci slic_handle_err_irq(sdev, isr); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (isr & SLIC_ISR_LEVENT) 6988c2ecf20Sopenharmony_ci slic_handle_link_change(sdev); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (isr & SLIC_ISR_UPC_MASK) 7018c2ecf20Sopenharmony_ci slic_handle_upr_irq(sdev, isr); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (isr & SLIC_ISR_RCV) 7048c2ecf20Sopenharmony_ci slic_handle_receive(sdev, todo, done); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (isr & SLIC_ISR_CMD) 7078c2ecf20Sopenharmony_ci slic_xmit_complete(sdev); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic int slic_poll(struct napi_struct *napi, int todo) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct slic_device *sdev = container_of(napi, struct slic_device, napi); 7138c2ecf20Sopenharmony_ci struct slic_shmem *sm = &sdev->shmem; 7148c2ecf20Sopenharmony_ci struct slic_shmem_data *sm_data = sm->shmem_data; 7158c2ecf20Sopenharmony_ci u32 isr = le32_to_cpu(sm_data->isr); 7168c2ecf20Sopenharmony_ci int done = 0; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci slic_handle_irq(sdev, isr, todo, &done); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (done < todo) { 7218c2ecf20Sopenharmony_ci napi_complete_done(napi, done); 7228c2ecf20Sopenharmony_ci /* reenable irqs */ 7238c2ecf20Sopenharmony_ci sm_data->isr = 0; 7248c2ecf20Sopenharmony_ci /* make sure sm_data->isr is cleard before irqs are reenabled */ 7258c2ecf20Sopenharmony_ci wmb(); 7268c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISR, 0); 7278c2ecf20Sopenharmony_ci slic_flush_write(sdev); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return done; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic irqreturn_t slic_irq(int irq, void *dev_id) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct slic_device *sdev = dev_id; 7368c2ecf20Sopenharmony_ci struct slic_shmem *sm = &sdev->shmem; 7378c2ecf20Sopenharmony_ci struct slic_shmem_data *sm_data = sm->shmem_data; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_MASK); 7408c2ecf20Sopenharmony_ci slic_flush_write(sdev); 7418c2ecf20Sopenharmony_ci /* make sure sm_data->isr is read after ICR_INT_MASK is set */ 7428c2ecf20Sopenharmony_ci wmb(); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (!sm_data->isr) { 7458c2ecf20Sopenharmony_ci dma_rmb(); 7468c2ecf20Sopenharmony_ci /* spurious interrupt */ 7478c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISR, 0); 7488c2ecf20Sopenharmony_ci slic_flush_write(sdev); 7498c2ecf20Sopenharmony_ci return IRQ_NONE; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci napi_schedule_irqoff(&sdev->napi); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7558c2ecf20Sopenharmony_ci} 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic void slic_card_reset(struct slic_device *sdev) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci u16 cmd; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RESET, SLIC_RESET_MAGIC); 7628c2ecf20Sopenharmony_ci /* flush write by means of config space */ 7638c2ecf20Sopenharmony_ci pci_read_config_word(sdev->pdev, PCI_COMMAND, &cmd); 7648c2ecf20Sopenharmony_ci mdelay(1); 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int slic_init_stat_queue(struct slic_device *sdev) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci const unsigned int DESC_ALIGN_MASK = SLIC_STATS_DESC_ALIGN - 1; 7708c2ecf20Sopenharmony_ci struct slic_stat_queue *stq = &sdev->stq; 7718c2ecf20Sopenharmony_ci struct slic_stat_desc *descs; 7728c2ecf20Sopenharmony_ci unsigned int misalign; 7738c2ecf20Sopenharmony_ci unsigned int offset; 7748c2ecf20Sopenharmony_ci dma_addr_t paddr; 7758c2ecf20Sopenharmony_ci size_t size; 7768c2ecf20Sopenharmony_ci int err; 7778c2ecf20Sopenharmony_ci int i; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci stq->len = SLIC_NUM_STAT_DESCS; 7808c2ecf20Sopenharmony_ci stq->active_array = 0; 7818c2ecf20Sopenharmony_ci stq->done_idx = 0; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci size = stq->len * sizeof(*descs) + DESC_ALIGN_MASK; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) { 7868c2ecf20Sopenharmony_ci descs = dma_alloc_coherent(&sdev->pdev->dev, size, &paddr, 7878c2ecf20Sopenharmony_ci GFP_KERNEL); 7888c2ecf20Sopenharmony_ci if (!descs) { 7898c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, 7908c2ecf20Sopenharmony_ci "failed to allocate status descriptors\n"); 7918c2ecf20Sopenharmony_ci err = -ENOMEM; 7928c2ecf20Sopenharmony_ci goto free_descs; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci /* ensure correct alignment */ 7958c2ecf20Sopenharmony_ci offset = 0; 7968c2ecf20Sopenharmony_ci misalign = paddr & DESC_ALIGN_MASK; 7978c2ecf20Sopenharmony_ci if (misalign) { 7988c2ecf20Sopenharmony_ci offset = SLIC_STATS_DESC_ALIGN - misalign; 7998c2ecf20Sopenharmony_ci descs += offset; 8008c2ecf20Sopenharmony_ci paddr += offset; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RBAR, lower_32_bits(paddr) | 8048c2ecf20Sopenharmony_ci stq->len); 8058c2ecf20Sopenharmony_ci stq->descs[i] = descs; 8068c2ecf20Sopenharmony_ci stq->paddr[i] = paddr; 8078c2ecf20Sopenharmony_ci stq->addr_offset[i] = offset; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci stq->mem_size = size; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci return 0; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cifree_descs: 8158c2ecf20Sopenharmony_ci while (i--) { 8168c2ecf20Sopenharmony_ci dma_free_coherent(&sdev->pdev->dev, stq->mem_size, 8178c2ecf20Sopenharmony_ci stq->descs[i] - stq->addr_offset[i], 8188c2ecf20Sopenharmony_ci stq->paddr[i] - stq->addr_offset[i]); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return err; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic void slic_free_stat_queue(struct slic_device *sdev) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct slic_stat_queue *stq = &sdev->stq; 8278c2ecf20Sopenharmony_ci int i; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci for (i = 0; i < SLIC_NUM_STAT_DESC_ARRAYS; i++) { 8308c2ecf20Sopenharmony_ci dma_free_coherent(&sdev->pdev->dev, stq->mem_size, 8318c2ecf20Sopenharmony_ci stq->descs[i] - stq->addr_offset[i], 8328c2ecf20Sopenharmony_ci stq->paddr[i] - stq->addr_offset[i]); 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_cistatic int slic_init_tx_queue(struct slic_device *sdev) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct slic_tx_queue *txq = &sdev->txq; 8398c2ecf20Sopenharmony_ci struct slic_tx_buffer *buff; 8408c2ecf20Sopenharmony_ci struct slic_tx_desc *desc; 8418c2ecf20Sopenharmony_ci unsigned int i; 8428c2ecf20Sopenharmony_ci int err; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci txq->len = SLIC_NUM_TX_DESCS; 8458c2ecf20Sopenharmony_ci txq->put_idx = 0; 8468c2ecf20Sopenharmony_ci txq->done_idx = 0; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci txq->txbuffs = kcalloc(txq->len, sizeof(*buff), GFP_KERNEL); 8498c2ecf20Sopenharmony_ci if (!txq->txbuffs) 8508c2ecf20Sopenharmony_ci return -ENOMEM; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci txq->dma_pool = dma_pool_create("slic_pool", &sdev->pdev->dev, 8538c2ecf20Sopenharmony_ci sizeof(*desc), SLIC_TX_DESC_ALIGN, 8548c2ecf20Sopenharmony_ci 4096); 8558c2ecf20Sopenharmony_ci if (!txq->dma_pool) { 8568c2ecf20Sopenharmony_ci err = -ENOMEM; 8578c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, "failed to create dma pool\n"); 8588c2ecf20Sopenharmony_ci goto free_buffs; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci for (i = 0; i < txq->len; i++) { 8628c2ecf20Sopenharmony_ci buff = &txq->txbuffs[i]; 8638c2ecf20Sopenharmony_ci desc = dma_pool_zalloc(txq->dma_pool, GFP_KERNEL, 8648c2ecf20Sopenharmony_ci &buff->desc_paddr); 8658c2ecf20Sopenharmony_ci if (!desc) { 8668c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, 8678c2ecf20Sopenharmony_ci "failed to alloc pool chunk (%i)\n", i); 8688c2ecf20Sopenharmony_ci err = -ENOMEM; 8698c2ecf20Sopenharmony_ci goto free_descs; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci desc->hnd = cpu_to_le32((u32)(i + 1)); 8738c2ecf20Sopenharmony_ci desc->cmd = SLIC_CMD_XMT_REQ; 8748c2ecf20Sopenharmony_ci desc->flags = 0; 8758c2ecf20Sopenharmony_ci desc->type = cpu_to_le32(SLIC_CMD_TYPE_DUMB); 8768c2ecf20Sopenharmony_ci buff->desc = desc; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cifree_descs: 8828c2ecf20Sopenharmony_ci while (i--) { 8838c2ecf20Sopenharmony_ci buff = &txq->txbuffs[i]; 8848c2ecf20Sopenharmony_ci dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr); 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci dma_pool_destroy(txq->dma_pool); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cifree_buffs: 8898c2ecf20Sopenharmony_ci kfree(txq->txbuffs); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci return err; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_cistatic void slic_free_tx_queue(struct slic_device *sdev) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci struct slic_tx_queue *txq = &sdev->txq; 8978c2ecf20Sopenharmony_ci struct slic_tx_buffer *buff; 8988c2ecf20Sopenharmony_ci unsigned int i; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci for (i = 0; i < txq->len; i++) { 9018c2ecf20Sopenharmony_ci buff = &txq->txbuffs[i]; 9028c2ecf20Sopenharmony_ci dma_pool_free(txq->dma_pool, buff->desc, buff->desc_paddr); 9038c2ecf20Sopenharmony_ci if (!buff->skb) 9048c2ecf20Sopenharmony_ci continue; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci dma_unmap_single(&sdev->pdev->dev, 9078c2ecf20Sopenharmony_ci dma_unmap_addr(buff, map_addr), 9088c2ecf20Sopenharmony_ci dma_unmap_len(buff, map_len), DMA_TO_DEVICE); 9098c2ecf20Sopenharmony_ci consume_skb(buff->skb); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci dma_pool_destroy(txq->dma_pool); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci kfree(txq->txbuffs); 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic int slic_init_rx_queue(struct slic_device *sdev) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci struct slic_rx_queue *rxq = &sdev->rxq; 9198c2ecf20Sopenharmony_ci struct slic_rx_buffer *buff; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci rxq->len = SLIC_NUM_RX_LES; 9228c2ecf20Sopenharmony_ci rxq->done_idx = 0; 9238c2ecf20Sopenharmony_ci rxq->put_idx = 0; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci buff = kcalloc(rxq->len, sizeof(*buff), GFP_KERNEL); 9268c2ecf20Sopenharmony_ci if (!buff) 9278c2ecf20Sopenharmony_ci return -ENOMEM; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci rxq->rxbuffs = buff; 9308c2ecf20Sopenharmony_ci slic_refill_rx_queue(sdev, GFP_KERNEL); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return 0; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic void slic_free_rx_queue(struct slic_device *sdev) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct slic_rx_queue *rxq = &sdev->rxq; 9388c2ecf20Sopenharmony_ci struct slic_rx_buffer *buff; 9398c2ecf20Sopenharmony_ci unsigned int i; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* free rx buffers */ 9428c2ecf20Sopenharmony_ci for (i = 0; i < rxq->len; i++) { 9438c2ecf20Sopenharmony_ci buff = &rxq->rxbuffs[i]; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (!buff->skb) 9468c2ecf20Sopenharmony_ci continue; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci dma_unmap_single(&sdev->pdev->dev, 9498c2ecf20Sopenharmony_ci dma_unmap_addr(buff, map_addr), 9508c2ecf20Sopenharmony_ci dma_unmap_len(buff, map_len), 9518c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 9528c2ecf20Sopenharmony_ci consume_skb(buff->skb); 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci kfree(rxq->rxbuffs); 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic void slic_set_link_autoneg(struct slic_device *sdev) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci unsigned int subid = sdev->pdev->subsystem_device; 9608c2ecf20Sopenharmony_ci u32 val; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (sdev->is_fiber) { 9638c2ecf20Sopenharmony_ci /* We've got a fiber gigabit interface, and register 4 is 9648c2ecf20Sopenharmony_ci * different in fiber mode than in copper mode. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci /* advertise FD only @1000 Mb */ 9678c2ecf20Sopenharmony_ci val = MII_ADVERTISE << 16 | ADVERTISE_1000XFULL | 9688c2ecf20Sopenharmony_ci ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; 9698c2ecf20Sopenharmony_ci /* enable PAUSE frames */ 9708c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 9718c2ecf20Sopenharmony_ci /* reset phy, enable auto-neg */ 9728c2ecf20Sopenharmony_ci val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE | 9738c2ecf20Sopenharmony_ci BMCR_ANRESTART; 9748c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 9758c2ecf20Sopenharmony_ci } else { /* copper gigabit */ 9768c2ecf20Sopenharmony_ci /* We've got a copper gigabit interface, and register 4 is 9778c2ecf20Sopenharmony_ci * different in copper mode than in fiber mode. 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci /* advertise 10/100 Mb modes */ 9808c2ecf20Sopenharmony_ci val = MII_ADVERTISE << 16 | ADVERTISE_100FULL | 9818c2ecf20Sopenharmony_ci ADVERTISE_100HALF | ADVERTISE_10FULL | ADVERTISE_10HALF; 9828c2ecf20Sopenharmony_ci /* enable PAUSE frames */ 9838c2ecf20Sopenharmony_ci val |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; 9848c2ecf20Sopenharmony_ci /* required by the Cicada PHY */ 9858c2ecf20Sopenharmony_ci val |= ADVERTISE_CSMA; 9868c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* advertise FD only @1000 Mb */ 9898c2ecf20Sopenharmony_ci val = MII_CTRL1000 << 16 | ADVERTISE_1000FULL; 9908c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (subid != PCI_SUBDEVICE_ID_ALACRITECH_CICADA) { 9938c2ecf20Sopenharmony_ci /* if a Marvell PHY enable auto crossover */ 9948c2ecf20Sopenharmony_ci val = SLIC_MIICR_REG_16 | SLIC_MRV_REG16_XOVERON; 9958c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* reset phy, enable auto-neg */ 9988c2ecf20Sopenharmony_ci val = MII_BMCR << 16 | BMCR_RESET | BMCR_ANENABLE | 9998c2ecf20Sopenharmony_ci BMCR_ANRESTART; 10008c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 10018c2ecf20Sopenharmony_ci } else { 10028c2ecf20Sopenharmony_ci /* enable and restart auto-neg (don't reset) */ 10038c2ecf20Sopenharmony_ci val = MII_BMCR << 16 | BMCR_ANENABLE | BMCR_ANRESTART; 10048c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic void slic_set_mac_address(struct slic_device *sdev) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci u8 *addr = sdev->netdev->dev_addr; 10128c2ecf20Sopenharmony_ci u32 val; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci val = addr[5] | addr[4] << 8 | addr[3] << 16 | addr[2] << 24; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WRADDRAL, val); 10178c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WRADDRBL, val); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci val = addr[0] << 8 | addr[1]; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WRADDRAH, val); 10228c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WRADDRBH, val); 10238c2ecf20Sopenharmony_ci slic_flush_write(sdev); 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic u32 slic_read_dword_from_firmware(const struct firmware *fw, int *offset) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci int idx = *offset; 10298c2ecf20Sopenharmony_ci __le32 val; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci memcpy(&val, fw->data + *offset, sizeof(val)); 10328c2ecf20Sopenharmony_ci idx += 4; 10338c2ecf20Sopenharmony_ci *offset = idx; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return le32_to_cpu(val); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_RCV_FIRMWARE_MOJAVE); 10398c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_RCV_FIRMWARE_OASIS); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic int slic_load_rcvseq_firmware(struct slic_device *sdev) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci const struct firmware *fw; 10448c2ecf20Sopenharmony_ci const char *file; 10458c2ecf20Sopenharmony_ci u32 codelen; 10468c2ecf20Sopenharmony_ci int idx = 0; 10478c2ecf20Sopenharmony_ci u32 instr; 10488c2ecf20Sopenharmony_ci u32 addr; 10498c2ecf20Sopenharmony_ci int err; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci file = (sdev->model == SLIC_MODEL_OASIS) ? SLIC_RCV_FIRMWARE_OASIS : 10528c2ecf20Sopenharmony_ci SLIC_RCV_FIRMWARE_MOJAVE; 10538c2ecf20Sopenharmony_ci err = request_firmware(&fw, file, &sdev->pdev->dev); 10548c2ecf20Sopenharmony_ci if (err) { 10558c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, 10568c2ecf20Sopenharmony_ci "failed to load receive sequencer firmware %s\n", file); 10578c2ecf20Sopenharmony_ci return err; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci /* Do an initial sanity check concerning firmware size now. A further 10608c2ecf20Sopenharmony_ci * check follows below. 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_ci if (fw->size < SLIC_FIRMWARE_MIN_SIZE) { 10638c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, 10648c2ecf20Sopenharmony_ci "invalid firmware size %zu (min %u expected)\n", 10658c2ecf20Sopenharmony_ci fw->size, SLIC_FIRMWARE_MIN_SIZE); 10668c2ecf20Sopenharmony_ci err = -EINVAL; 10678c2ecf20Sopenharmony_ci goto release; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci codelen = slic_read_dword_from_firmware(fw, &idx); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* do another sanity check against firmware size */ 10738c2ecf20Sopenharmony_ci if ((codelen + 4) > fw->size) { 10748c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, 10758c2ecf20Sopenharmony_ci "invalid rcv-sequencer firmware size %zu\n", fw->size); 10768c2ecf20Sopenharmony_ci err = -EINVAL; 10778c2ecf20Sopenharmony_ci goto release; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* download sequencer code to card */ 10818c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_BEGIN); 10828c2ecf20Sopenharmony_ci for (addr = 0; addr < codelen; addr++) { 10838c2ecf20Sopenharmony_ci __le32 val; 10848c2ecf20Sopenharmony_ci /* write out instruction address */ 10858c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RCV_WCS, addr); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci instr = slic_read_dword_from_firmware(fw, &idx); 10888c2ecf20Sopenharmony_ci /* write out the instruction data low addr */ 10898c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RCV_WCS, instr); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci val = (__le32)fw->data[idx]; 10928c2ecf20Sopenharmony_ci instr = le32_to_cpu(val); 10938c2ecf20Sopenharmony_ci idx++; 10948c2ecf20Sopenharmony_ci /* write out the instruction data high addr */ 10958c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RCV_WCS, instr); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci /* finish download */ 10988c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_RCV_WCS, SLIC_RCVWCS_FINISH); 10998c2ecf20Sopenharmony_ci slic_flush_write(sdev); 11008c2ecf20Sopenharmony_cirelease: 11018c2ecf20Sopenharmony_ci release_firmware(fw); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci return err; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_FIRMWARE_MOJAVE); 11078c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SLIC_FIRMWARE_OASIS); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic int slic_load_firmware(struct slic_device *sdev) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci u32 sectstart[SLIC_FIRMWARE_MAX_SECTIONS]; 11128c2ecf20Sopenharmony_ci u32 sectsize[SLIC_FIRMWARE_MAX_SECTIONS]; 11138c2ecf20Sopenharmony_ci const struct firmware *fw; 11148c2ecf20Sopenharmony_ci unsigned int datalen; 11158c2ecf20Sopenharmony_ci const char *file; 11168c2ecf20Sopenharmony_ci int code_start; 11178c2ecf20Sopenharmony_ci unsigned int i; 11188c2ecf20Sopenharmony_ci u32 numsects; 11198c2ecf20Sopenharmony_ci int idx = 0; 11208c2ecf20Sopenharmony_ci u32 sect; 11218c2ecf20Sopenharmony_ci u32 instr; 11228c2ecf20Sopenharmony_ci u32 addr; 11238c2ecf20Sopenharmony_ci u32 base; 11248c2ecf20Sopenharmony_ci int err; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci file = (sdev->model == SLIC_MODEL_OASIS) ? SLIC_FIRMWARE_OASIS : 11278c2ecf20Sopenharmony_ci SLIC_FIRMWARE_MOJAVE; 11288c2ecf20Sopenharmony_ci err = request_firmware(&fw, file, &sdev->pdev->dev); 11298c2ecf20Sopenharmony_ci if (err) { 11308c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, "failed to load firmware %s\n", file); 11318c2ecf20Sopenharmony_ci return err; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci /* Do an initial sanity check concerning firmware size now. A further 11348c2ecf20Sopenharmony_ci * check follows below. 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci if (fw->size < SLIC_FIRMWARE_MIN_SIZE) { 11378c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, 11388c2ecf20Sopenharmony_ci "invalid firmware size %zu (min is %u)\n", fw->size, 11398c2ecf20Sopenharmony_ci SLIC_FIRMWARE_MIN_SIZE); 11408c2ecf20Sopenharmony_ci err = -EINVAL; 11418c2ecf20Sopenharmony_ci goto release; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci numsects = slic_read_dword_from_firmware(fw, &idx); 11458c2ecf20Sopenharmony_ci if (numsects == 0 || numsects > SLIC_FIRMWARE_MAX_SECTIONS) { 11468c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, 11478c2ecf20Sopenharmony_ci "invalid number of sections in firmware: %u", numsects); 11488c2ecf20Sopenharmony_ci err = -EINVAL; 11498c2ecf20Sopenharmony_ci goto release; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci datalen = numsects * 8 + 4; 11538c2ecf20Sopenharmony_ci for (i = 0; i < numsects; i++) { 11548c2ecf20Sopenharmony_ci sectsize[i] = slic_read_dword_from_firmware(fw, &idx); 11558c2ecf20Sopenharmony_ci datalen += sectsize[i]; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* do another sanity check against firmware size */ 11598c2ecf20Sopenharmony_ci if (datalen > fw->size) { 11608c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, 11618c2ecf20Sopenharmony_ci "invalid firmware size %zu (expected >= %u)\n", 11628c2ecf20Sopenharmony_ci fw->size, datalen); 11638c2ecf20Sopenharmony_ci err = -EINVAL; 11648c2ecf20Sopenharmony_ci goto release; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci /* get sections */ 11678c2ecf20Sopenharmony_ci for (i = 0; i < numsects; i++) 11688c2ecf20Sopenharmony_ci sectstart[i] = slic_read_dword_from_firmware(fw, &idx); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci code_start = idx; 11718c2ecf20Sopenharmony_ci instr = slic_read_dword_from_firmware(fw, &idx); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci for (sect = 0; sect < numsects; sect++) { 11748c2ecf20Sopenharmony_ci unsigned int ssize = sectsize[sect] >> 3; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci base = sectstart[sect]; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci for (addr = 0; addr < ssize; addr++) { 11798c2ecf20Sopenharmony_ci /* write out instruction address */ 11808c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WCS, base + addr); 11818c2ecf20Sopenharmony_ci /* write out instruction to low addr */ 11828c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WCS, instr); 11838c2ecf20Sopenharmony_ci instr = slic_read_dword_from_firmware(fw, &idx); 11848c2ecf20Sopenharmony_ci /* write out instruction to high addr */ 11858c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WCS, instr); 11868c2ecf20Sopenharmony_ci instr = slic_read_dword_from_firmware(fw, &idx); 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci idx = code_start; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci for (sect = 0; sect < numsects; sect++) { 11938c2ecf20Sopenharmony_ci unsigned int ssize = sectsize[sect] >> 3; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci instr = slic_read_dword_from_firmware(fw, &idx); 11968c2ecf20Sopenharmony_ci base = sectstart[sect]; 11978c2ecf20Sopenharmony_ci if (base < 0x8000) 11988c2ecf20Sopenharmony_ci continue; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci for (addr = 0; addr < ssize; addr++) { 12018c2ecf20Sopenharmony_ci /* write out instruction address */ 12028c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WCS, 12038c2ecf20Sopenharmony_ci SLIC_WCS_COMPARE | (base + addr)); 12048c2ecf20Sopenharmony_ci /* write out instruction to low addr */ 12058c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WCS, instr); 12068c2ecf20Sopenharmony_ci instr = slic_read_dword_from_firmware(fw, &idx); 12078c2ecf20Sopenharmony_ci /* write out instruction to high addr */ 12088c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WCS, instr); 12098c2ecf20Sopenharmony_ci instr = slic_read_dword_from_firmware(fw, &idx); 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci slic_flush_write(sdev); 12138c2ecf20Sopenharmony_ci mdelay(10); 12148c2ecf20Sopenharmony_ci /* everything OK, kick off the card */ 12158c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WCS, SLIC_WCS_START); 12168c2ecf20Sopenharmony_ci slic_flush_write(sdev); 12178c2ecf20Sopenharmony_ci /* wait long enough for ucode to init card and reach the mainloop */ 12188c2ecf20Sopenharmony_ci mdelay(20); 12198c2ecf20Sopenharmony_cirelease: 12208c2ecf20Sopenharmony_ci release_firmware(fw); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci return err; 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic int slic_init_shmem(struct slic_device *sdev) 12268c2ecf20Sopenharmony_ci{ 12278c2ecf20Sopenharmony_ci struct slic_shmem *sm = &sdev->shmem; 12288c2ecf20Sopenharmony_ci struct slic_shmem_data *sm_data; 12298c2ecf20Sopenharmony_ci dma_addr_t paddr; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci sm_data = dma_alloc_coherent(&sdev->pdev->dev, sizeof(*sm_data), 12328c2ecf20Sopenharmony_ci &paddr, GFP_KERNEL); 12338c2ecf20Sopenharmony_ci if (!sm_data) { 12348c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, "failed to allocate shared memory\n"); 12358c2ecf20Sopenharmony_ci return -ENOMEM; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci sm->shmem_data = sm_data; 12398c2ecf20Sopenharmony_ci sm->isr_paddr = paddr; 12408c2ecf20Sopenharmony_ci sm->link_paddr = paddr + offsetof(struct slic_shmem_data, link); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci return 0; 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cistatic void slic_free_shmem(struct slic_device *sdev) 12468c2ecf20Sopenharmony_ci{ 12478c2ecf20Sopenharmony_ci struct slic_shmem *sm = &sdev->shmem; 12488c2ecf20Sopenharmony_ci struct slic_shmem_data *sm_data = sm->shmem_data; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci dma_free_coherent(&sdev->pdev->dev, sizeof(*sm_data), sm_data, 12518c2ecf20Sopenharmony_ci sm->isr_paddr); 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_cistatic int slic_init_iface(struct slic_device *sdev) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci struct slic_shmem *sm = &sdev->shmem; 12578c2ecf20Sopenharmony_ci int err; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci sdev->upr_list.pending = false; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci err = slic_init_shmem(sdev); 12628c2ecf20Sopenharmony_ci if (err) { 12638c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, "failed to init shared memory\n"); 12648c2ecf20Sopenharmony_ci return err; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci err = slic_load_firmware(sdev); 12688c2ecf20Sopenharmony_ci if (err) { 12698c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, "failed to load firmware\n"); 12708c2ecf20Sopenharmony_ci goto free_sm; 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci err = slic_load_rcvseq_firmware(sdev); 12748c2ecf20Sopenharmony_ci if (err) { 12758c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, 12768c2ecf20Sopenharmony_ci "failed to load firmware for receive sequencer\n"); 12778c2ecf20Sopenharmony_ci goto free_sm; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF); 12818c2ecf20Sopenharmony_ci slic_flush_write(sdev); 12828c2ecf20Sopenharmony_ci mdelay(1); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci err = slic_init_rx_queue(sdev); 12858c2ecf20Sopenharmony_ci if (err) { 12868c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, "failed to init rx queue: %u\n", err); 12878c2ecf20Sopenharmony_ci goto free_sm; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci err = slic_init_tx_queue(sdev); 12918c2ecf20Sopenharmony_ci if (err) { 12928c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, "failed to init tx queue: %u\n", err); 12938c2ecf20Sopenharmony_ci goto free_rxq; 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci err = slic_init_stat_queue(sdev); 12978c2ecf20Sopenharmony_ci if (err) { 12988c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, "failed to init status queue: %u\n", 12998c2ecf20Sopenharmony_ci err); 13008c2ecf20Sopenharmony_ci goto free_txq; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr)); 13048c2ecf20Sopenharmony_ci napi_enable(&sdev->napi); 13058c2ecf20Sopenharmony_ci /* disable irq mitigation */ 13068c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_INTAGG, 0); 13078c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISR, 0); 13088c2ecf20Sopenharmony_ci slic_flush_write(sdev); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci slic_set_mac_address(sdev); 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci spin_lock_bh(&sdev->link_lock); 13138c2ecf20Sopenharmony_ci sdev->duplex = DUPLEX_UNKNOWN; 13148c2ecf20Sopenharmony_ci sdev->speed = SPEED_UNKNOWN; 13158c2ecf20Sopenharmony_ci spin_unlock_bh(&sdev->link_lock); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci slic_set_link_autoneg(sdev); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci err = request_irq(sdev->pdev->irq, slic_irq, IRQF_SHARED, DRV_NAME, 13208c2ecf20Sopenharmony_ci sdev); 13218c2ecf20Sopenharmony_ci if (err) { 13228c2ecf20Sopenharmony_ci netdev_err(sdev->netdev, "failed to request irq: %u\n", err); 13238c2ecf20Sopenharmony_ci goto disable_napi; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_ON); 13278c2ecf20Sopenharmony_ci slic_flush_write(sdev); 13288c2ecf20Sopenharmony_ci /* request initial link status */ 13298c2ecf20Sopenharmony_ci err = slic_handle_link_change(sdev); 13308c2ecf20Sopenharmony_ci if (err) 13318c2ecf20Sopenharmony_ci netdev_warn(sdev->netdev, 13328c2ecf20Sopenharmony_ci "failed to set initial link state: %u\n", err); 13338c2ecf20Sopenharmony_ci return 0; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cidisable_napi: 13368c2ecf20Sopenharmony_ci napi_disable(&sdev->napi); 13378c2ecf20Sopenharmony_ci slic_free_stat_queue(sdev); 13388c2ecf20Sopenharmony_cifree_txq: 13398c2ecf20Sopenharmony_ci slic_free_tx_queue(sdev); 13408c2ecf20Sopenharmony_cifree_rxq: 13418c2ecf20Sopenharmony_ci slic_free_rx_queue(sdev); 13428c2ecf20Sopenharmony_cifree_sm: 13438c2ecf20Sopenharmony_ci slic_free_shmem(sdev); 13448c2ecf20Sopenharmony_ci slic_card_reset(sdev); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci return err; 13478c2ecf20Sopenharmony_ci} 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_cistatic int slic_open(struct net_device *dev) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 13528c2ecf20Sopenharmony_ci int err; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci netif_carrier_off(dev); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci err = slic_init_iface(sdev); 13578c2ecf20Sopenharmony_ci if (err) { 13588c2ecf20Sopenharmony_ci netdev_err(dev, "failed to initialize interface: %i\n", err); 13598c2ecf20Sopenharmony_ci return err; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci netif_start_queue(dev); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return 0; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic int slic_close(struct net_device *dev) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 13708c2ecf20Sopenharmony_ci u32 val; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci netif_stop_queue(dev); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci /* stop irq handling */ 13758c2ecf20Sopenharmony_ci napi_disable(&sdev->napi); 13768c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF); 13778c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISR, 0); 13788c2ecf20Sopenharmony_ci slic_flush_write(sdev); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci free_irq(sdev->pdev->irq, sdev); 13818c2ecf20Sopenharmony_ci /* turn off RCV and XMT and power down PHY */ 13828c2ecf20Sopenharmony_ci val = SLIC_GXCR_RESET | SLIC_GXCR_PAUSEEN; 13838c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WXCFG, val); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci val = SLIC_GRCR_RESET | SLIC_GRCR_CTLEN | SLIC_GRCR_ADDRAEN | 13868c2ecf20Sopenharmony_ci SLIC_GRCR_HASHSIZE << SLIC_GRCR_HASHSIZE_SHIFT; 13878c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WRCFG, val); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci val = MII_BMCR << 16 | BMCR_PDOWN; 13908c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_WPHY, val); 13918c2ecf20Sopenharmony_ci slic_flush_write(sdev); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci slic_clear_upr_list(&sdev->upr_list); 13948c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_QUIESCE, 0); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci slic_free_stat_queue(sdev); 13978c2ecf20Sopenharmony_ci slic_free_tx_queue(sdev); 13988c2ecf20Sopenharmony_ci slic_free_rx_queue(sdev); 13998c2ecf20Sopenharmony_ci slic_free_shmem(sdev); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci slic_card_reset(sdev); 14028c2ecf20Sopenharmony_ci netif_carrier_off(dev); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci return 0; 14058c2ecf20Sopenharmony_ci} 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_cistatic netdev_tx_t slic_xmit(struct sk_buff *skb, struct net_device *dev) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 14108c2ecf20Sopenharmony_ci struct slic_tx_queue *txq = &sdev->txq; 14118c2ecf20Sopenharmony_ci struct slic_tx_buffer *buff; 14128c2ecf20Sopenharmony_ci struct slic_tx_desc *desc; 14138c2ecf20Sopenharmony_ci dma_addr_t paddr; 14148c2ecf20Sopenharmony_ci u32 cbar_val; 14158c2ecf20Sopenharmony_ci u32 maplen; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (unlikely(slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS)) { 14188c2ecf20Sopenharmony_ci netdev_err(dev, "BUG! not enough tx LEs left: %u\n", 14198c2ecf20Sopenharmony_ci slic_get_free_tx_descs(txq)); 14208c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 14218c2ecf20Sopenharmony_ci } 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci maplen = skb_headlen(skb); 14248c2ecf20Sopenharmony_ci paddr = dma_map_single(&sdev->pdev->dev, skb->data, maplen, 14258c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 14268c2ecf20Sopenharmony_ci if (dma_mapping_error(&sdev->pdev->dev, paddr)) { 14278c2ecf20Sopenharmony_ci netdev_err(dev, "failed to map tx buffer\n"); 14288c2ecf20Sopenharmony_ci goto drop_skb; 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci buff = &txq->txbuffs[txq->put_idx]; 14328c2ecf20Sopenharmony_ci buff->skb = skb; 14338c2ecf20Sopenharmony_ci dma_unmap_addr_set(buff, map_addr, paddr); 14348c2ecf20Sopenharmony_ci dma_unmap_len_set(buff, map_len, maplen); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci desc = buff->desc; 14378c2ecf20Sopenharmony_ci desc->totlen = cpu_to_le32(maplen); 14388c2ecf20Sopenharmony_ci desc->paddrl = cpu_to_le32(lower_32_bits(paddr)); 14398c2ecf20Sopenharmony_ci desc->paddrh = cpu_to_le32(upper_32_bits(paddr)); 14408c2ecf20Sopenharmony_ci desc->len = cpu_to_le32(maplen); 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci txq->put_idx = slic_next_queue_idx(txq->put_idx, txq->len); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci cbar_val = lower_32_bits(buff->desc_paddr) | 1; 14458c2ecf20Sopenharmony_ci /* complete writes to RAM and DMA before hardware is informed */ 14468c2ecf20Sopenharmony_ci wmb(); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_CBAR, cbar_val); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (slic_get_free_tx_descs(txq) < SLIC_MAX_REQ_TX_DESCS) 14518c2ecf20Sopenharmony_ci netif_stop_queue(dev); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 14548c2ecf20Sopenharmony_cidrop_skb: 14558c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_cistatic void slic_get_stats(struct net_device *dev, 14618c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *lst) 14628c2ecf20Sopenharmony_ci{ 14638c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 14648c2ecf20Sopenharmony_ci struct slic_stats *stats = &sdev->stats; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->rx_packets, stats, rx_packets); 14678c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->tx_packets, stats, tx_packets); 14688c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->rx_bytes, stats, rx_bytes); 14698c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->tx_bytes, stats, tx_bytes); 14708c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->rx_errors, stats, rx_errors); 14718c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->rx_dropped, stats, rx_buff_miss); 14728c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->tx_dropped, stats, tx_dropped); 14738c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->multicast, stats, rx_mcasts); 14748c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->rx_over_errors, stats, rx_buffoflow); 14758c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->rx_crc_errors, stats, rx_crc); 14768c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->rx_fifo_errors, stats, rx_oflow802); 14778c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(lst->tx_carrier_errors, stats, tx_carrier); 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic int slic_get_sset_count(struct net_device *dev, int sset) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci switch (sset) { 14838c2ecf20Sopenharmony_ci case ETH_SS_STATS: 14848c2ecf20Sopenharmony_ci return ARRAY_SIZE(slic_stats_strings); 14858c2ecf20Sopenharmony_ci default: 14868c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14878c2ecf20Sopenharmony_ci } 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic void slic_get_ethtool_stats(struct net_device *dev, 14918c2ecf20Sopenharmony_ci struct ethtool_stats *eth_stats, u64 *data) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 14948c2ecf20Sopenharmony_ci struct slic_stats *stats = &sdev->stats; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[0], stats, rx_packets); 14978c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[1], stats, rx_bytes); 14988c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[2], stats, rx_mcasts); 14998c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[3], stats, rx_errors); 15008c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[4], stats, rx_buff_miss); 15018c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[5], stats, rx_tpcsum); 15028c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[6], stats, rx_tpoflow); 15038c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[7], stats, rx_tphlen); 15048c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[8], stats, rx_ipcsum); 15058c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[9], stats, rx_iplen); 15068c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[10], stats, rx_iphlen); 15078c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[11], stats, rx_early); 15088c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[12], stats, rx_buffoflow); 15098c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[13], stats, rx_lcode); 15108c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[14], stats, rx_drbl); 15118c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[15], stats, rx_crc); 15128c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[16], stats, rx_oflow802); 15138c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[17], stats, rx_uflow802); 15148c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[18], stats, tx_packets); 15158c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[19], stats, tx_bytes); 15168c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[20], stats, tx_carrier); 15178c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[21], stats, tx_dropped); 15188c2ecf20Sopenharmony_ci SLIC_GET_STATS_COUNTER(data[22], stats, irq_errs); 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic void slic_get_strings(struct net_device *dev, u32 stringset, u8 *data) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci if (stringset == ETH_SS_STATS) { 15248c2ecf20Sopenharmony_ci memcpy(data, slic_stats_strings, sizeof(slic_stats_strings)); 15258c2ecf20Sopenharmony_ci data += sizeof(slic_stats_strings); 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci} 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_cistatic void slic_get_drvinfo(struct net_device *dev, 15308c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 15318c2ecf20Sopenharmony_ci{ 15328c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 15358c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(sdev->pdev), sizeof(info->bus_info)); 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic const struct ethtool_ops slic_ethtool_ops = { 15398c2ecf20Sopenharmony_ci .get_drvinfo = slic_get_drvinfo, 15408c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 15418c2ecf20Sopenharmony_ci .get_strings = slic_get_strings, 15428c2ecf20Sopenharmony_ci .get_ethtool_stats = slic_get_ethtool_stats, 15438c2ecf20Sopenharmony_ci .get_sset_count = slic_get_sset_count, 15448c2ecf20Sopenharmony_ci}; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_cistatic const struct net_device_ops slic_netdev_ops = { 15478c2ecf20Sopenharmony_ci .ndo_open = slic_open, 15488c2ecf20Sopenharmony_ci .ndo_stop = slic_close, 15498c2ecf20Sopenharmony_ci .ndo_start_xmit = slic_xmit, 15508c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 15518c2ecf20Sopenharmony_ci .ndo_get_stats64 = slic_get_stats, 15528c2ecf20Sopenharmony_ci .ndo_set_rx_mode = slic_set_rx_mode, 15538c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 15548c2ecf20Sopenharmony_ci}; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_cistatic u16 slic_eeprom_csum(unsigned char *eeprom, unsigned int len) 15578c2ecf20Sopenharmony_ci{ 15588c2ecf20Sopenharmony_ci unsigned char *ptr = eeprom; 15598c2ecf20Sopenharmony_ci u32 csum = 0; 15608c2ecf20Sopenharmony_ci __le16 data; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci while (len > 1) { 15638c2ecf20Sopenharmony_ci memcpy(&data, ptr, sizeof(data)); 15648c2ecf20Sopenharmony_ci csum += le16_to_cpu(data); 15658c2ecf20Sopenharmony_ci ptr += 2; 15668c2ecf20Sopenharmony_ci len -= 2; 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci if (len > 0) 15698c2ecf20Sopenharmony_ci csum += *(u8 *)ptr; 15708c2ecf20Sopenharmony_ci while (csum >> 16) 15718c2ecf20Sopenharmony_ci csum = (csum & 0xFFFF) + ((csum >> 16) & 0xFFFF); 15728c2ecf20Sopenharmony_ci return ~csum; 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci/* check eeprom size, magic and checksum */ 15768c2ecf20Sopenharmony_cistatic bool slic_eeprom_valid(unsigned char *eeprom, unsigned int size) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci const unsigned int MAX_SIZE = 128; 15798c2ecf20Sopenharmony_ci const unsigned int MIN_SIZE = 98; 15808c2ecf20Sopenharmony_ci __le16 magic; 15818c2ecf20Sopenharmony_ci __le16 csum; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if (size < MIN_SIZE || size > MAX_SIZE) 15848c2ecf20Sopenharmony_ci return false; 15858c2ecf20Sopenharmony_ci memcpy(&magic, eeprom, sizeof(magic)); 15868c2ecf20Sopenharmony_ci if (le16_to_cpu(magic) != SLIC_EEPROM_MAGIC) 15878c2ecf20Sopenharmony_ci return false; 15888c2ecf20Sopenharmony_ci /* cut checksum bytes */ 15898c2ecf20Sopenharmony_ci size -= 2; 15908c2ecf20Sopenharmony_ci memcpy(&csum, eeprom + size, sizeof(csum)); 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci return (le16_to_cpu(csum) == slic_eeprom_csum(eeprom, size)); 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_cistatic int slic_read_eeprom(struct slic_device *sdev) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci unsigned int devfn = PCI_FUNC(sdev->pdev->devfn); 15988c2ecf20Sopenharmony_ci struct slic_shmem *sm = &sdev->shmem; 15998c2ecf20Sopenharmony_ci struct slic_shmem_data *sm_data = sm->shmem_data; 16008c2ecf20Sopenharmony_ci const unsigned int MAX_LOOPS = 5000; 16018c2ecf20Sopenharmony_ci unsigned int codesize; 16028c2ecf20Sopenharmony_ci unsigned char *eeprom; 16038c2ecf20Sopenharmony_ci struct slic_upr *upr; 16048c2ecf20Sopenharmony_ci unsigned int i = 0; 16058c2ecf20Sopenharmony_ci dma_addr_t paddr; 16068c2ecf20Sopenharmony_ci int err = 0; 16078c2ecf20Sopenharmony_ci u8 *mac[2]; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci eeprom = dma_alloc_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE, 16108c2ecf20Sopenharmony_ci &paddr, GFP_KERNEL); 16118c2ecf20Sopenharmony_ci if (!eeprom) 16128c2ecf20Sopenharmony_ci return -ENOMEM; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ICR, SLIC_ICR_INT_OFF); 16158c2ecf20Sopenharmony_ci /* setup ISP temporarily */ 16168c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISP, lower_32_bits(sm->isr_paddr)); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci err = slic_new_upr(sdev, SLIC_UPR_CONFIG, paddr); 16198c2ecf20Sopenharmony_ci if (!err) { 16208c2ecf20Sopenharmony_ci for (i = 0; i < MAX_LOOPS; i++) { 16218c2ecf20Sopenharmony_ci if (le32_to_cpu(sm_data->isr) & SLIC_ISR_UPC) 16228c2ecf20Sopenharmony_ci break; 16238c2ecf20Sopenharmony_ci mdelay(1); 16248c2ecf20Sopenharmony_ci } 16258c2ecf20Sopenharmony_ci if (i == MAX_LOOPS) { 16268c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, 16278c2ecf20Sopenharmony_ci "timed out while waiting for eeprom data\n"); 16288c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci upr = slic_dequeue_upr(sdev); 16318c2ecf20Sopenharmony_ci kfree(upr); 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISP, 0); 16358c2ecf20Sopenharmony_ci slic_write(sdev, SLIC_REG_ISR, 0); 16368c2ecf20Sopenharmony_ci slic_flush_write(sdev); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if (err) 16398c2ecf20Sopenharmony_ci goto free_eeprom; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (sdev->model == SLIC_MODEL_OASIS) { 16428c2ecf20Sopenharmony_ci struct slic_oasis_eeprom *oee; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci oee = (struct slic_oasis_eeprom *)eeprom; 16458c2ecf20Sopenharmony_ci mac[0] = oee->mac; 16468c2ecf20Sopenharmony_ci mac[1] = oee->mac2; 16478c2ecf20Sopenharmony_ci codesize = le16_to_cpu(oee->eeprom_code_size); 16488c2ecf20Sopenharmony_ci } else { 16498c2ecf20Sopenharmony_ci struct slic_mojave_eeprom *mee; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci mee = (struct slic_mojave_eeprom *)eeprom; 16528c2ecf20Sopenharmony_ci mac[0] = mee->mac; 16538c2ecf20Sopenharmony_ci mac[1] = mee->mac2; 16548c2ecf20Sopenharmony_ci codesize = le16_to_cpu(mee->eeprom_code_size); 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (!slic_eeprom_valid(eeprom, codesize)) { 16588c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, "invalid checksum in eeprom\n"); 16598c2ecf20Sopenharmony_ci err = -EINVAL; 16608c2ecf20Sopenharmony_ci goto free_eeprom; 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci /* set mac address */ 16638c2ecf20Sopenharmony_ci ether_addr_copy(sdev->netdev->dev_addr, mac[devfn]); 16648c2ecf20Sopenharmony_cifree_eeprom: 16658c2ecf20Sopenharmony_ci dma_free_coherent(&sdev->pdev->dev, SLIC_EEPROM_SIZE, eeprom, paddr); 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci return err; 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cistatic int slic_init(struct slic_device *sdev) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci int err; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci spin_lock_init(&sdev->upper_lock); 16758c2ecf20Sopenharmony_ci spin_lock_init(&sdev->link_lock); 16768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdev->upr_list.list); 16778c2ecf20Sopenharmony_ci spin_lock_init(&sdev->upr_list.lock); 16788c2ecf20Sopenharmony_ci u64_stats_init(&sdev->stats.syncp); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci slic_card_reset(sdev); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci err = slic_load_firmware(sdev); 16838c2ecf20Sopenharmony_ci if (err) { 16848c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, "failed to load firmware\n"); 16858c2ecf20Sopenharmony_ci return err; 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* we need the shared memory to read EEPROM so set it up temporarily */ 16898c2ecf20Sopenharmony_ci err = slic_init_shmem(sdev); 16908c2ecf20Sopenharmony_ci if (err) { 16918c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, "failed to init shared memory\n"); 16928c2ecf20Sopenharmony_ci return err; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci err = slic_read_eeprom(sdev); 16968c2ecf20Sopenharmony_ci if (err) { 16978c2ecf20Sopenharmony_ci dev_err(&sdev->pdev->dev, "failed to read eeprom\n"); 16988c2ecf20Sopenharmony_ci goto free_sm; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci slic_card_reset(sdev); 17028c2ecf20Sopenharmony_ci slic_free_shmem(sdev); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci return 0; 17058c2ecf20Sopenharmony_cifree_sm: 17068c2ecf20Sopenharmony_ci slic_free_shmem(sdev); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci return err; 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_cistatic bool slic_is_fiber(unsigned short subdev) 17128c2ecf20Sopenharmony_ci{ 17138c2ecf20Sopenharmony_ci switch (subdev) { 17148c2ecf20Sopenharmony_ci /* Mojave */ 17158c2ecf20Sopenharmony_ci case PCI_SUBDEVICE_ID_ALACRITECH_1000X1F: 17168c2ecf20Sopenharmony_ci case PCI_SUBDEVICE_ID_ALACRITECH_SES1001F: fallthrough; 17178c2ecf20Sopenharmony_ci /* Oasis */ 17188c2ecf20Sopenharmony_ci case PCI_SUBDEVICE_ID_ALACRITECH_SEN2002XF: 17198c2ecf20Sopenharmony_ci case PCI_SUBDEVICE_ID_ALACRITECH_SEN2001XF: 17208c2ecf20Sopenharmony_ci case PCI_SUBDEVICE_ID_ALACRITECH_SEN2104EF: 17218c2ecf20Sopenharmony_ci case PCI_SUBDEVICE_ID_ALACRITECH_SEN2102EF: 17228c2ecf20Sopenharmony_ci return true; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci return false; 17258c2ecf20Sopenharmony_ci} 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_cistatic void slic_configure_pci(struct pci_dev *pdev) 17288c2ecf20Sopenharmony_ci{ 17298c2ecf20Sopenharmony_ci u16 old; 17308c2ecf20Sopenharmony_ci u16 cmd; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &old); 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci cmd = old | PCI_COMMAND_PARITY | PCI_COMMAND_SERR; 17358c2ecf20Sopenharmony_ci if (old != cmd) 17368c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, cmd); 17378c2ecf20Sopenharmony_ci} 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_cistatic int slic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 17408c2ecf20Sopenharmony_ci{ 17418c2ecf20Sopenharmony_ci struct slic_device *sdev; 17428c2ecf20Sopenharmony_ci struct net_device *dev; 17438c2ecf20Sopenharmony_ci int err; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 17468c2ecf20Sopenharmony_ci if (err) { 17478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable PCI device\n"); 17488c2ecf20Sopenharmony_ci return err; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci pci_set_master(pdev); 17528c2ecf20Sopenharmony_ci pci_try_set_mwi(pdev); 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci slic_configure_pci(pdev); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); 17578c2ecf20Sopenharmony_ci if (err) { 17588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to setup DMA\n"); 17598c2ecf20Sopenharmony_ci goto disable; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 17658c2ecf20Sopenharmony_ci if (err) { 17668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to obtain PCI regions\n"); 17678c2ecf20Sopenharmony_ci goto disable; 17688c2ecf20Sopenharmony_ci } 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*sdev)); 17718c2ecf20Sopenharmony_ci if (!dev) { 17728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to alloc ethernet device\n"); 17738c2ecf20Sopenharmony_ci err = -ENOMEM; 17748c2ecf20Sopenharmony_ci goto free_regions; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 17788c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 17798c2ecf20Sopenharmony_ci dev->irq = pdev->irq; 17808c2ecf20Sopenharmony_ci dev->netdev_ops = &slic_netdev_ops; 17818c2ecf20Sopenharmony_ci dev->hw_features = NETIF_F_RXCSUM; 17828c2ecf20Sopenharmony_ci dev->features |= dev->hw_features; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci dev->ethtool_ops = &slic_ethtool_ops; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci sdev = netdev_priv(dev); 17878c2ecf20Sopenharmony_ci sdev->model = (pdev->device == PCI_DEVICE_ID_ALACRITECH_OASIS) ? 17888c2ecf20Sopenharmony_ci SLIC_MODEL_OASIS : SLIC_MODEL_MOJAVE; 17898c2ecf20Sopenharmony_ci sdev->is_fiber = slic_is_fiber(pdev->subsystem_device); 17908c2ecf20Sopenharmony_ci sdev->pdev = pdev; 17918c2ecf20Sopenharmony_ci sdev->netdev = dev; 17928c2ecf20Sopenharmony_ci sdev->regs = ioremap(pci_resource_start(pdev, 0), 17938c2ecf20Sopenharmony_ci pci_resource_len(pdev, 0)); 17948c2ecf20Sopenharmony_ci if (!sdev->regs) { 17958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to map registers\n"); 17968c2ecf20Sopenharmony_ci err = -ENOMEM; 17978c2ecf20Sopenharmony_ci goto free_netdev; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci err = slic_init(sdev); 18018c2ecf20Sopenharmony_ci if (err) { 18028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to initialize driver\n"); 18038c2ecf20Sopenharmony_ci goto unmap; 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci netif_napi_add(dev, &sdev->napi, slic_poll, SLIC_NAPI_WEIGHT); 18078c2ecf20Sopenharmony_ci netif_carrier_off(dev); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci err = register_netdev(dev); 18108c2ecf20Sopenharmony_ci if (err) { 18118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register net device: %i\n", err); 18128c2ecf20Sopenharmony_ci goto unmap; 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci return 0; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ciunmap: 18188c2ecf20Sopenharmony_ci iounmap(sdev->regs); 18198c2ecf20Sopenharmony_cifree_netdev: 18208c2ecf20Sopenharmony_ci free_netdev(dev); 18218c2ecf20Sopenharmony_cifree_regions: 18228c2ecf20Sopenharmony_ci pci_release_regions(pdev); 18238c2ecf20Sopenharmony_cidisable: 18248c2ecf20Sopenharmony_ci pci_disable_device(pdev); 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci return err; 18278c2ecf20Sopenharmony_ci} 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_cistatic void slic_remove(struct pci_dev *pdev) 18308c2ecf20Sopenharmony_ci{ 18318c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 18328c2ecf20Sopenharmony_ci struct slic_device *sdev = netdev_priv(dev); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci unregister_netdev(dev); 18358c2ecf20Sopenharmony_ci iounmap(sdev->regs); 18368c2ecf20Sopenharmony_ci free_netdev(dev); 18378c2ecf20Sopenharmony_ci pci_release_regions(pdev); 18388c2ecf20Sopenharmony_ci pci_disable_device(pdev); 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_cistatic struct pci_driver slic_driver = { 18428c2ecf20Sopenharmony_ci .name = DRV_NAME, 18438c2ecf20Sopenharmony_ci .id_table = slic_id_tbl, 18448c2ecf20Sopenharmony_ci .probe = slic_probe, 18458c2ecf20Sopenharmony_ci .remove = slic_remove, 18468c2ecf20Sopenharmony_ci}; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_cimodule_pci_driver(slic_driver); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Alacritech non-accelerated SLIC driver"); 18518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lino Sanfilippo <LinoSanfilippo@gmx.de>"); 18528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1853