/** @file * * Marvell AQtion family network card driver. * * Copyright(C) 2017-2021 Marvell * * SPDX-License-Identifier: BSD-2-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO,THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ FILE_LICENCE ( BSD2 ); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "aqc1xx.h" extern struct atl_hw_ops atl_hw; extern struct atl_hw_ops atl2_hw; /** @file * * Marvell AQC network card driver * */ static int atl_ring_alloc ( const struct atl_nic *nic, struct atl_ring *ring, uint32_t desc_size, uint32_t reg_base ) { physaddr_t phy_addr; /* Allocate ring buffer.*/ ring->length = ATL_RING_SIZE * desc_size; ring->ring = dma_alloc ( nic->dma, &ring->map, ring->length, ring->length ); if ( !ring->ring ) return -ENOMEM; /* Initialize the descriptor ring */ memset ( ring->ring, 0, ring->length ); /* Program ring address */ phy_addr = dma ( &ring->map, ring->ring ); /* Write ring address (hi & low parts).*/ ATL_WRITE_REG ( (uint32_t)phy_addr, reg_base ); ATL_WRITE_REG ( (uint32_t)(((uint64_t)phy_addr) >> 32), reg_base + 4 ); /* Write ring length.*/ ATL_WRITE_REG ( ATL_RING_SIZE, reg_base + 8 ); ring->sw_head = ring->sw_tail = 0; DBGC ( nic, "AQUANTIA: %p ring is at [%08llx,%08llx), reg base %#x\n", nic, ((unsigned long long)phy_addr), ((unsigned long long) phy_addr + ring->length), reg_base ); return 0; } static void atl_ring_free ( struct atl_ring *ring ) { dma_free ( &ring->map, ring->ring, ring->length ); ring->ring = NULL; ring->length = 0; } static void atl_ring_next_dx ( unsigned int *val ) { ++( *val ); if ( *val == ATL_RING_SIZE ) *val = 0; } int atl_ring_full ( const struct atl_ring *ring ) { unsigned int tail = ring->sw_tail; atl_ring_next_dx ( &tail ); return tail == ring->sw_head; } void atl_rx_ring_fill ( struct atl_nic *nic ) { struct atl_desc_rx *rx; struct io_buffer *iobuf; physaddr_t address; unsigned int refilled = 0; /* Refill ring */ while ( !atl_ring_full ( &nic->rx_ring ) ) { /* Allocate I/O buffer */ iobuf = alloc_rx_iob ( ATL_RX_MAX_LEN, nic->dma ); if ( !iobuf ) { /* Wait for next refill */ break; } /* Get next receive descriptor */ rx = ( struct atl_desc_rx * )nic->rx_ring.ring + nic->rx_ring.sw_tail; /* Populate receive descriptor */ address = iob_dma ( iobuf ); rx->data_addr = address; rx->hdr_addr = 0; /* Record I/O buffer */ assert ( nic->iobufs[nic->rx_ring.sw_tail] == NULL ); nic->iobufs[nic->rx_ring.sw_tail] = iobuf; DBGC( nic, "AQUANTIA: RX[%d] is [%llx,%llx)\n", nic->rx_ring.sw_tail, ( (unsigned long long)address), ( (unsigned long long)address + ATL_RX_MAX_LEN) ); atl_ring_next_dx ( &nic->rx_ring.sw_tail ); refilled++; } /* Push descriptors to card, if applicable */ if ( refilled ) { wmb(); ATL_WRITE_REG ( nic->rx_ring.sw_tail, ATL_RING_TAIL_PTR ); } } /** * Open network device * * @v netdev Network device * @ret rc Return status code */ static int atl_open ( struct net_device *netdev ) { struct atl_nic *nic = netdev->priv; uint32_t ctrl = 0; /* Tx ring */ if ( atl_ring_alloc ( nic, &nic->tx_ring, sizeof(struct atl_desc_tx), ATL_TX_DMA_DESC_ADDR ) != 0 ) goto err_tx_alloc; /* Rx ring */ if ( atl_ring_alloc ( nic, &nic->rx_ring, sizeof(struct atl_desc_rx), ATL_RX_DMA_DESC_ADDR ) != 0 ) goto err_rx_alloc; /* Allocate interrupt vectors */ ATL_WRITE_REG ( (ATL_IRQ_CTRL_COR_EN | ATL_IRQ_CTRL_REG_RST_DIS), ATL_IRQ_CTRL ); /*TX & RX Interruprt Mapping*/ ctrl = ATL_IRQ_MAP_REG1_RX0 | ATL_IRQ_MAP_REG1_RX0_EN | ATL_IRQ_MAP_REG1_TX0 | ATL_IRQ_MAP_REG1_TX0_EN; ATL_WRITE_REG ( ctrl, ATL_IRQ_MAP_REG1 ); /*TX interrupt ctrl reg*/ ATL_WRITE_REG ( ATL_TX_IRQ_CTRL_WB_EN, ATL_TX_IRQ_CTRL ); /*RX interrupt ctrl reg*/ ATL_WRITE_REG ( ATL_RX_IRQ_CTRL_WB_EN, ATL_RX_IRQ_CTRL ); /*RX data path*/ ctrl = ATL_IRQ_TX | ATL_IRQ_RX; /* itr mask */ ATL_WRITE_REG ( ctrl, ATL_ITR_MSKS ); ATL_WRITE_REG ( (uint32_t)ATL_RX_MAX_LEN / 1024U, ATL_RX_DMA_DESC_BUF_SIZE ); /*filter global ctrl */ ctrl = ATL_RPF_CTRL1_BRC_EN | ATL_RPF_CTRL1_L2_PROMISC | ATL_RPF_CTRL1_ACTION | ATL_RPF_CTRL1_BRC_TSH; ATL_WRITE_REG ( ctrl, ATL_RPF_CTRL1 ); /* vlan promisc */ ATL_WRITE_REG ( ATL_RPF_CTRL2_VLAN_PROMISC, ATL_RPF_CTRL2 ); /* enable rpf2 */ ATL_WRITE_REG ( ATL_RPF2_CTRL_EN, ATL_RPF2_CTRL ); /* RX Packet Buffer 0 Register 1 */ ATL_WRITE_REG ( ATL_RPB0_CTRL1_SIZE, ATL_RPB0_CTRL1 ); /*RX Packet Buffer 0 Register 2 */ ctrl = ATL_RPB0_CTRL2_LOW_TSH | ATL_RPB0_CTRL2_HIGH_TSH | ATL_RPB0_CTRL2_FC_EN; ATL_WRITE_REG ( ctrl, ATL_RPB0_CTRL2 ); /*RPB global ctrl*/ ctrl = ATL_READ_REG(ATL_RPB_CTRL); ctrl |= (ATL_RPB_CTRL_EN | ATL_RPB_CTRL_FC); ATL_WRITE_REG ( ctrl, ATL_RPB_CTRL ); /*TX data path*/ /* enable tpo2 */ ATL_WRITE_REG ( ATL_TPO2_EN, ATL_TPO2_CTRL ); /* tpb global ctrl *** */ ATL_WRITE_REG ( ATL_TPB0_CTRL1_SIZE, ATL_TPB0_CTRL1 ); ctrl = ATL_TPB0_CTRL2_LOW_TSH | ATL_TPB0_CTRL2_HIGH_TSH; /* tpb global ctrl *** */ ATL_WRITE_REG ( ctrl, ATL_TPB0_CTRL2 ); ctrl = ATL_READ_REG ( ATL_TPB_CTRL ); ctrl |= ( ATL_TPB_CTRL_EN | ATL_TPB_CTRL_PAD_EN ); /* tpb global ctrl */ ATL_WRITE_REG ( ctrl, ATL_TPB_CTRL ); /*Enable rings*/ ATL_WRITE_REG ( ATL_READ_REG ( ATL_RING_TX_CTRL ) | ATL_RING_TX_CTRL_EN, ATL_RING_TX_CTRL ); ATL_WRITE_REG ( ATL_READ_REG ( ATL_RING_RX_CTRL ) | ATL_RING_RX_CTRL_EN, ATL_RING_RX_CTRL ); if ( nic->flags == ATL_FLAG_A2 ) { ATL_WRITE_REG ( ATL2_RPF_NEW_EN_ADR_EN, ATL2_RPF_NEW_EN_ADR ); } atl_rx_ring_fill ( nic ); nic->hw_ops->start ( nic ); return 0; err_rx_alloc: atl_ring_free ( &nic->tx_ring ); err_tx_alloc: return -ENOMEM; } /** * Close network device * * @v netdev Network device */ static void atl_close ( struct net_device *netdev ) { struct atl_nic *nic = netdev->priv; nic->hw_ops->stop ( nic ); /* rpb global ctrl */ ATL_WRITE_REG ( ATL_RPB_CTRL_DIS, ATL_RPB_CTRL ); /* tgb global ctrl */ ATL_WRITE_REG ( ATL_TPB_CTRL_DIS, ATL_TPB_CTRL); ATL_WRITE_REG ( ATL_READ_REG(ATL_RING_TX_CTRL) | (~ATL_RING_TX_CTRL_EN), ATL_RING_TX_CTRL ); ATL_WRITE_REG ( ATL_READ_REG(ATL_RING_RX_CTRL) | (~ATL_RING_RX_CTRL_EN), ATL_RING_RX_CTRL ); /* clear itr mask */ ATL_WRITE_REG ( ATL_ITR_MSKS_DIS, ATL_ITR_MSKS ); /* Reset the NIC */ nic->hw_ops->reset ( nic ); atl_ring_free ( &nic->tx_ring ); atl_ring_free ( &nic->rx_ring ); } /** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ int atl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct atl_nic *nic = netdev->priv; struct atl_desc_tx *tx; physaddr_t address; uint32_t len; /* Get next transmit descriptor */ if ( atl_ring_full ( &nic->tx_ring ) ) { DBGC ( nic, "AQUANTIA: %p out of transmit descriptors\n", nic ); return -ENOBUFS; } tx = (struct atl_desc_tx *)nic->tx_ring.ring + nic->tx_ring.sw_tail; /* Populate transmit descriptor */ memset ( tx, 0, sizeof ( *tx ) ); address = iob_dma ( iobuf ); tx->address = address; len = iob_len ( iobuf ); tx->status = 0x1; tx->status = ( (tx->status) & ~ATL_DESC_TX_BUF_LEN_MASK) | ((len << ATL_DESC_TX_BUF_LEN_OFFSET) & ATL_DESC_TX_BUF_LEN_MASK ); tx->status = ((tx->status) & ~ATL_DESC_TX_EOP_MASK) | ( (ATL_DESC_TX_DX_EOP_VALUE << ATL_DESC_TX_EOP_OFFSET) & ATL_DESC_TX_EOP_MASK ); tx->status = ( (tx->status) & ~ATL_DESC_TX_CMD_MASK) | ((ATL_DESC_TX_CMD_VALUE << ATL_DESC_TX_CMD_OFFSET) & ATL_DESC_TX_CMD_MASK ); tx->flag = ( (tx->flag) & ~ATL_DESC_TX_PAY_LEN_MASK) | ((len << ATL_DESC_TX_PAY_LEN_OFFSET) & ATL_DESC_TX_PAY_LEN_MASK ); wmb(); DBGC2 ( nic, "AQUANTIA: %p TX[%d] is [%llx, %llx]\n", nic, nic->tx_ring.sw_tail, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ) ); atl_ring_next_dx ( &nic->tx_ring.sw_tail ); ATL_WRITE_REG ( nic->tx_ring.sw_tail, ATL_RING_TAIL ); return 0; } void atl_check_link ( struct net_device *netdev ) { struct atl_nic *nic = netdev->priv; uint32_t link_state; /* Read link status */ link_state = nic->hw_ops->get_link ( nic ); DBGC ( nic, "AQUANTIA: %p link status is %08x\n", nic, link_state ); if ( link_state != nic->link_state ) { if ( link_state ) { DBGC ( nic, "AQUANTIA: link up\n"); netdev_link_up ( netdev ); } else { DBGC ( nic, "AQUANTIA: link lost\n"); netdev_link_down ( netdev ); } nic->link_state = link_state; } } /** * Poll for completed packets * * @v netdev Network device */ void atl_poll_tx ( struct net_device *netdev ) { struct atl_nic *nic = netdev->priv; struct atl_desc_tx_wb *tx; /* Check for completed packets */ while ( nic->tx_ring.sw_head != nic->tx_ring.sw_tail ) { /* Get next transmit descriptor */ tx = ( struct atl_desc_tx_wb * )nic->tx_ring.ring + nic->tx_ring.sw_head; /* Stop if descriptor is still in use */ if ( !(tx->status & cpu_to_le32 ( ATL_TX_DESC_STATUS_DD ) ) ) return; DBGC2 ( nic, "AQUANTIA: %p TX[%d] complete\n", nic, nic->tx_ring.sw_head ); /* Complete TX descriptor */ atl_ring_next_dx ( &nic->tx_ring.sw_head ); netdev_tx_complete_next ( netdev ); } } /** * Poll for received packets * * @v netdev Network device */ void atl_poll_rx ( struct net_device *netdev ) { struct atl_nic *nic = netdev->priv; struct atl_desc_rx_wb *rx; struct io_buffer *iobuf; size_t len; /* Check for received packets */ while ( nic->rx_ring.sw_head != nic->rx_ring.sw_tail ) { /* Get next receive descriptor */ rx = (struct atl_desc_rx_wb *)nic->rx_ring.ring + nic->rx_ring.sw_head; /* Stop if descriptor is still in use */ if ( !(rx->status & cpu_to_le16(ATL_RX_DESC_STATUS_DD)) ) return; /* Populate I/O buffer */ iobuf = nic->iobufs[nic->rx_ring.sw_head]; nic->iobufs[nic->rx_ring.sw_head] = NULL; len = le16_to_cpu ( rx->pkt_len ); iob_put ( iobuf, len ); /* Hand off to network stack */ DBGC ( nic, "AQUANTIA: %p RX[%d] complete (length %zd)\n", nic, nic->rx_ring.sw_head, len ); netdev_rx ( netdev, iobuf ); atl_ring_next_dx ( &nic->rx_ring.sw_head ); } } /** * Poll for completed and received packets * * @v netdev Network device */ static void atl_poll ( struct net_device *netdev ) { struct atl_nic *nic = netdev->priv; /* Check link state */ atl_check_link ( netdev ); /* Poll for TX completions */ atl_poll_tx ( netdev ); /* Poll for RX completions */ atl_poll_rx ( netdev ); /* Refill RX ring */ atl_rx_ring_fill ( nic ); } /** * Enable or disable interrupts * * @v netdev Network device * @v enable Interrupts should be enabled */ static void atl_irq ( struct net_device *netdev, int enable ) { struct atl_nic *nic = netdev->priv; uint32_t mask; mask = ( ATL_IRQ_TX | ATL_IRQ_RX ); if ( enable ) ATL_WRITE_REG ( mask, ATL_ITR_MSKS ); else ATL_WRITE_REG ( mask, ATL_ITR_MSKC ); } /** Marvell network device operations */ static struct net_device_operations atl_operations = { .open = atl_open, .close = atl_close, .transmit = atl_transmit, .poll = atl_poll, .irq = atl_irq, }; /****************************************************************************** * * PCI interface * ******************************************************************************* */ /** * Probe PCI device * * @v pci PCI device * @ret rc Return status code */ static int atl_probe ( struct pci_device *pci ) { struct net_device *netdev; struct atl_nic *nic; int rc = ENOERR; uint32_t io_size = 0; /* Allocate and initialise net device */ netdev = alloc_etherdev ( sizeof( *nic ) ); if ( !netdev ) { rc = -ENOMEM; goto err_alloc; } netdev_init ( netdev, &atl_operations ); nic = netdev->priv; pci_set_drvdata ( pci, netdev ); netdev->dev = &pci->dev; memset( nic, 0, sizeof( *nic ) ); nic->flags = pci->id->driver_data; /* Fix up PCI device */ adjust_pci_device ( pci ); switch ( nic->flags ) { case ATL_FLAG_A1: nic->hw_ops = &atl_hw; io_size = ATL_BAR_SIZE; break; case ATL_FLAG_A2: nic->hw_ops = &atl2_hw; io_size = ATL2_BAR_SIZE; break; default: goto err_unsupported; break; } /* Map registers */ nic->regs = pci_ioremap ( pci, pci->membase, io_size ); if ( !nic->regs ) { rc = -ENODEV; goto err_ioremap; } /* Configure DMA */ nic->dma = &pci->dma; /* Reset the NIC */ if ( ( rc = nic->hw_ops->reset ( nic ) ) != 0 ) goto err_reset; /* Get MAC Address */ if ( ( rc = nic->hw_ops->get_mac ( nic, netdev->hw_addr ) ) != 0 ) goto err_mac; /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; /* Set initial link state */ netdev_link_down ( netdev ); return 0; err_register_netdev: err_mac: nic->hw_ops->reset ( nic ); err_reset: iounmap ( nic->regs ); err_ioremap: netdev_nullify ( netdev ); netdev_put ( netdev ); err_unsupported: err_alloc: return rc; } /** * Remove PCI device * * @v pci PCI device */ static void atl_remove ( struct pci_device *pci ) { struct net_device *netdev = pci_get_drvdata ( pci ); struct atl_nic *nic = netdev->priv; /* Unregister network device */ unregister_netdev ( netdev ); /* Reset the NIC */ nic->hw_ops->reset ( nic ); /* Free network device */ iounmap ( nic->regs ); netdev_nullify ( netdev ); netdev_put ( netdev ); } /** Marvell PCI device IDs */ static struct pci_device_id atl_nics[] = { /* Atlantic 1 */ /* 10G */ PCI_ROM(0x1D6A, 0x0001, "AQC07", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0xD107, "AQC07", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x07B1, "AQC07", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x87B1, "AQC07", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A1), /* SFP */ PCI_ROM(0x1D6A, 0xD100, "AQC00", "Felicity Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x00B1, "AQC00", "Felicity Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x80B1, "AQC00", "Felicity Network Adapter", ATL_FLAG_A1), /* 5G */ PCI_ROM(0x1D6A, 0xD108, "AQC08", "Marvell AQtion 5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x08B1, "AQC08", "Marvell AQtion 5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x88B1, "AQC08", "Marvell AQtion 5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x11B1, "AQC11", "Marvell AQtion 5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x91B1, "AQC11", "Marvell AQtion 5Gbit Network Adapter", ATL_FLAG_A1), /* 2.5G */ PCI_ROM(0x1D6A, 0xD109, "AQC09", "Marvell AQtion 2.5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x09B1, "AQC09", "Marvell AQtion 2.5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x89B1, "AQC09", "Marvell AQtion 2.5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x12B1, "AQC12", "Marvell AQtion 2.5Gbit Network Adapter", ATL_FLAG_A1), PCI_ROM(0x1D6A, 0x92B1, "AQC12", "Marvell AQtion 2.5Gbit Network Adapter", ATL_FLAG_A1), /* Atlantic 2 */ PCI_ROM(0x1D6A, 0x00C0, "AQC13", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A2), PCI_ROM(0x1D6A, 0x94C0, "AQC13", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A2), PCI_ROM(0x1D6A, 0x93C0, "AQC13", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A2), PCI_ROM(0x1D6A, 0x04C0, "AQC13", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A2), PCI_ROM(0x1D6A, 0x14C0, "AQC13", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A2), PCI_ROM(0x1D6A, 0x12C0, "AQC13", "Marvell AQtion 10Gbit Network Adapter", ATL_FLAG_A2), }; /** Marvell PCI driver */ struct pci_driver atl_driver __pci_driver = { .ids = atl_nics, .id_count = (sizeof(atl_nics) / sizeof(atl_nics[0])), .probe = atl_probe, .remove = atl_remove, };