123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716 |
- /*
- * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- #include <stdint.h>
- #include <string.h>
- #include <strings.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <errno.h>
- #include <assert.h>
- #include <byteswap.h>
- #include <ipxe/netdevice.h>
- #include <ipxe/ethernet.h>
- #include <ipxe/if_ether.h>
- #include <ipxe/iobuf.h>
- #include <ipxe/malloc.h>
- #include <ipxe/pci.h>
- #include <ipxe/pciea.h>
- #include <ipxe/umalloc.h>
- #include "thunderx.h"
- #include "thunderxcfg.h"
-
- /** @file
- *
- * Cavium ThunderX Ethernet driver
- *
- */
-
- /** List of BGX Ethernet interfaces */
- static LIST_HEAD ( txnic_bgxs );
-
- /** List of physical functions */
- static LIST_HEAD ( txnic_pfs );
-
- /** Debug colour for physical function and BGX messages */
- #define TXNICCOL(x) ( &txnic_pfs + (x)->node )
-
- /** Board configuration protocol */
- static EFI_THUNDER_CONFIG_PROTOCOL *txcfg;
- EFI_REQUEST_PROTOCOL ( EFI_THUNDER_CONFIG_PROTOCOL, &txcfg );
-
- /******************************************************************************
- *
- * Diagnostics
- *
- ******************************************************************************
- */
-
- /**
- * Show virtual NIC diagnostics (for debugging)
- *
- * @v vnic Virtual NIC
- */
- static __attribute__ (( unused )) void txnic_diag ( struct txnic *vnic ) {
-
- DBGC ( vnic, "TXNIC %s SQ %05zx(%05llx)/%05zx(%05llx) %08llx\n",
- vnic->name,
- ( ( vnic->sq.prod % TXNIC_SQES ) * TXNIC_SQ_STRIDE ),
- readq ( vnic->regs + TXNIC_QS_SQ_TAIL(0) ),
- ( ( vnic->sq.cons % TXNIC_SQES ) * TXNIC_SQ_STRIDE ),
- readq ( vnic->regs + TXNIC_QS_SQ_HEAD(0) ),
- readq ( vnic->regs + TXNIC_QS_SQ_STATUS(0) ) );
- DBGC ( vnic, "TXNIC %s RQ %05zx(%05llx)/%05zx(%05llx) %016llx\n",
- vnic->name,
- ( ( vnic->rq.prod % TXNIC_RQES ) * TXNIC_RQ_STRIDE ),
- readq ( vnic->regs + TXNIC_QS_RBDR_TAIL(0) ),
- ( ( vnic->rq.cons % TXNIC_RQES ) * TXNIC_RQ_STRIDE ),
- readq ( vnic->regs + TXNIC_QS_RBDR_HEAD(0) ),
- readq ( vnic->regs + TXNIC_QS_RBDR_STATUS0(0) ) );
- DBGC ( vnic, "TXNIC %s CQ xxxxx(%05llx)/%05x(%05llx) %08llx:%08llx\n",
- vnic->name, readq ( vnic->regs + TXNIC_QS_CQ_TAIL(0) ),
- ( ( vnic->cq.cons % TXNIC_CQES ) * TXNIC_CQ_STRIDE ),
- readq ( vnic->regs + TXNIC_QS_CQ_HEAD(0) ),
- readq ( vnic->regs + TXNIC_QS_CQ_STATUS(0) ),
- readq ( vnic->regs + TXNIC_QS_CQ_STATUS2(0) ) );
- }
-
- /******************************************************************************
- *
- * Send queue
- *
- ******************************************************************************
- */
-
- /**
- * Create send queue
- *
- * @v vnic Virtual NIC
- * @ret rc Return status code
- */
- static int txnic_create_sq ( struct txnic *vnic ) {
-
- /* Reset send queue */
- vnic->sq.prod = 0;
- vnic->sq.cons = 0;
- writeq ( TXNIC_QS_SQ_CFG_RESET, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) );
-
- /* Configure and enable send queue */
- writeq ( user_to_phys ( vnic->sq.sqe, 0 ),
- ( vnic->regs + TXNIC_QS_SQ_BASE(0) ) );
- writeq ( ( TXNIC_QS_SQ_CFG_ENA | TXNIC_QS_SQ_CFG_QSIZE_1K ),
- ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) );
-
- DBGC ( vnic, "TXNIC %s SQ at [%08lx,%08lx)\n",
- vnic->name, user_to_phys ( vnic->sq.sqe, 0 ),
- user_to_phys ( vnic->sq.sqe, TXNIC_SQ_SIZE ) );
- return 0;
- }
-
- /**
- * Disable send queue
- *
- * @v vnic Virtual NIC
- * @ret rc Return status code
- */
- static int txnic_disable_sq ( struct txnic *vnic ) {
- uint64_t status;
- unsigned int i;
-
- /* Disable send queue */
- writeq ( 0, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) );
-
- /* Wait for send queue to be stopped */
- for ( i = 0 ; i < TXNIC_SQ_STOP_MAX_WAIT_MS ; i++ ) {
-
- /* Check if send queue is stopped */
- status = readq ( vnic->regs + TXNIC_QS_SQ_STATUS(0) );
- if ( status & TXNIC_QS_SQ_STATUS_STOPPED )
- return 0;
-
- /* Delay */
- mdelay ( 1 );
- }
-
- DBGC ( vnic, "TXNIC %s SQ disable timed out\n", vnic->name );
- return -ETIMEDOUT;
- }
-
- /**
- * Destroy send queue
- *
- * @v vnic Virtual NIC
- */
- static void txnic_destroy_sq ( struct txnic *vnic ) {
- int rc;
-
- /* Disable send queue */
- if ( ( rc = txnic_disable_sq ( vnic ) ) != 0 ) {
- /* Nothing else we can do */
- return;
- }
-
- /* Reset send queue */
- writeq ( TXNIC_QS_SQ_CFG_RESET, ( vnic->regs + TXNIC_QS_SQ_CFG(0) ) );
- }
-
- /**
- * Send packet
- *
- * @v vnic Virtual NIC
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
- static int txnic_send ( struct txnic *vnic, struct io_buffer *iobuf ) {
- struct txnic_sqe sqe;
- unsigned int sq_idx;
- size_t offset;
- size_t len;
-
- /* Get next send queue entry */
- if ( ( vnic->sq.prod - vnic->sq.cons ) >= TXNIC_SQ_FILL ) {
- DBGC ( vnic, "TXNIC %s out of send queue entries\n",
- vnic->name );
- return -ENOBUFS;
- }
- sq_idx = ( vnic->sq.prod++ % TXNIC_SQES );
- offset = ( sq_idx * TXNIC_SQ_STRIDE );
-
- /* Populate send descriptor */
- len = iob_len ( iobuf );
- memset ( &sqe, 0, sizeof ( sqe ) );
- sqe.hdr.total = cpu_to_le32 ( ( len >= ETH_ZLEN ) ? len : ETH_ZLEN );
- sqe.hdr.subdcnt = ( TXNIC_SQE_SUBDESCS - 1 );
- sqe.hdr.flags = TXNIC_SEND_HDR_FLAGS;
- sqe.gather.size = cpu_to_le16 ( len );
- sqe.gather.flags = TXNIC_SEND_GATHER_FLAGS;
- sqe.gather.addr = cpu_to_le64 ( virt_to_bus ( iobuf->data ) );
- DBGC2 ( vnic, "TXNIC %s SQE %#03x is [%08lx,%08lx)\n",
- vnic->name, sq_idx, virt_to_bus ( iobuf->data ),
- ( virt_to_bus ( iobuf->data ) + len ) );
-
- /* Copy send descriptor to ring */
- copy_to_user ( vnic->sq.sqe, offset, &sqe, sizeof ( sqe ) );
-
- /* Ring doorbell */
- wmb();
- writeq ( TXNIC_SQE_SUBDESCS, ( vnic->regs + TXNIC_QS_SQ_DOOR(0) ) );
-
- return 0;
- }
-
- /**
- * Complete send queue entry
- *
- * @v vnic Virtual NIC
- * @v cqe Send completion queue entry
- */
- static void txnic_complete_sqe ( struct txnic *vnic,
- struct txnic_cqe_send *cqe ) {
- struct net_device *netdev = vnic->netdev;
- unsigned int sq_idx;
- unsigned int status;
-
- /* Parse completion */
- sq_idx = ( le16_to_cpu ( cqe->sqe_ptr ) / TXNIC_SQE_SUBDESCS );
- status = cqe->send_status;
-
- /* Sanity check */
- assert ( sq_idx == ( vnic->sq.cons % TXNIC_SQES ) );
-
- /* Free send queue entry */
- vnic->sq.cons++;
-
- /* Complete transmission */
- if ( status ) {
- DBGC ( vnic, "TXNIC %s SQE %#03x complete (status %#02x)\n",
- vnic->name, sq_idx, status );
- netdev_tx_complete_next_err ( netdev, -EIO );
- } else {
- DBGC2 ( vnic, "TXNIC %s SQE %#03x complete\n",
- vnic->name, sq_idx );
- netdev_tx_complete_next ( netdev );
- }
- }
-
- /******************************************************************************
- *
- * Receive queue
- *
- ******************************************************************************
- */
-
- /**
- * Create receive queue
- *
- * @v vnic Virtual NIC
- * @ret rc Return status code
- */
- static int txnic_create_rq ( struct txnic *vnic ) {
-
- /* Reset receive buffer descriptor ring */
- vnic->rq.prod = 0;
- vnic->rq.cons = 0;
- writeq ( TXNIC_QS_RBDR_CFG_RESET,
- ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) );
-
- /* Configure and enable receive buffer descriptor ring */
- writeq ( user_to_phys ( vnic->rq.rqe, 0 ),
- ( vnic->regs + TXNIC_QS_RBDR_BASE(0) ) );
- writeq ( ( TXNIC_QS_RBDR_CFG_ENA | TXNIC_QS_RBDR_CFG_QSIZE_8K |
- TXNIC_QS_RBDR_CFG_LINES ( TXNIC_RQE_SIZE /
- TXNIC_LINE_SIZE ) ),
- ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) );
-
- /* Enable receive queue */
- writeq ( TXNIC_QS_RQ_CFG_ENA, ( vnic->regs + TXNIC_QS_RQ_CFG(0) ) );
-
- DBGC ( vnic, "TXNIC %s RQ at [%08lx,%08lx)\n",
- vnic->name, user_to_phys ( vnic->rq.rqe, 0 ),
- user_to_phys ( vnic->rq.rqe, TXNIC_RQ_SIZE ) );
- return 0;
- }
-
- /**
- * Disable receive queue
- *
- * @v vnic Virtual NIC
- * @ret rc Return status code
- */
- static int txnic_disable_rq ( struct txnic *vnic ) {
- uint64_t cfg;
- unsigned int i;
-
- /* Disable receive queue */
- writeq ( 0, ( vnic->regs + TXNIC_QS_RQ_CFG(0) ) );
-
- /* Wait for receive queue to be disabled */
- for ( i = 0 ; i < TXNIC_RQ_DISABLE_MAX_WAIT_MS ; i++ ) {
-
- /* Check if receive queue is disabled */
- cfg = readq ( vnic->regs + TXNIC_QS_RQ_CFG(0) );
- if ( ! ( cfg & TXNIC_QS_RQ_CFG_ENA ) )
- return 0;
-
- /* Delay */
- mdelay ( 1 );
- }
-
- DBGC ( vnic, "TXNIC %s RQ disable timed out\n", vnic->name );
- return -ETIMEDOUT;
- }
-
- /**
- * Destroy receive queue
- *
- * @v vnic Virtual NIC
- */
- static void txnic_destroy_rq ( struct txnic *vnic ) {
- unsigned int i;
- int rc;
-
- /* Disable receive queue */
- if ( ( rc = txnic_disable_rq ( vnic ) ) != 0 ) {
- /* Leak memory; there's nothing else we can do */
- return;
- }
-
- /* Disable receive buffer descriptor ring */
- writeq ( 0, ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) );
-
- /* Reset receive buffer descriptor ring */
- writeq ( TXNIC_QS_RBDR_CFG_RESET,
- ( vnic->regs + TXNIC_QS_RBDR_CFG(0) ) );
-
- /* Free any unused I/O buffers */
- for ( i = 0 ; i < TXNIC_RQ_FILL ; i++ ) {
- if ( vnic->rq.iobuf[i] )
- free_iob ( vnic->rq.iobuf[i] );
- vnic->rq.iobuf[i] = NULL;
- }
- }
-
- /**
- * Refill receive queue
- *
- * @v vnic Virtual NIC
- */
- static void txnic_refill_rq ( struct txnic *vnic ) {
- struct io_buffer *iobuf;
- struct txnic_rqe rqe;
- unsigned int rq_idx;
- unsigned int rq_iobuf_idx;
- unsigned int refilled = 0;
- size_t offset;
-
- /* Refill ring */
- while ( ( vnic->rq.prod - vnic->rq.cons ) < TXNIC_RQ_FILL ) {
-
- /* Allocate I/O buffer */
- iobuf = alloc_iob ( TXNIC_RQE_SIZE );
- if ( ! iobuf ) {
- /* Wait for next refill */
- break;
- }
-
- /* Get next receive descriptor */
- rq_idx = ( vnic->rq.prod++ % TXNIC_RQES );
- offset = ( rq_idx * TXNIC_RQ_STRIDE );
-
- /* Populate receive descriptor */
- rqe.rbdre.addr = cpu_to_le64 ( virt_to_bus ( iobuf->data ) );
- DBGC2 ( vnic, "TXNIC %s RQE %#03x is [%08lx,%08lx)\n",
- vnic->name, rq_idx, virt_to_bus ( iobuf->data ),
- ( virt_to_bus ( iobuf->data ) + TXNIC_RQE_SIZE ) );
-
- /* Copy receive descriptor to ring */
- copy_to_user ( vnic->rq.rqe, offset, &rqe, sizeof ( rqe ) );
- refilled++;
-
- /* Record I/O buffer */
- rq_iobuf_idx = ( rq_idx % TXNIC_RQ_FILL );
- assert ( vnic->rq.iobuf[rq_iobuf_idx] == NULL );
- vnic->rq.iobuf[rq_iobuf_idx] = iobuf;
- }
-
- /* Ring doorbell */
- wmb();
- writeq ( refilled, ( vnic->regs + TXNIC_QS_RBDR_DOOR(0) ) );
- }
-
- /**
- * Complete receive queue entry
- *
- * @v vnic Virtual NIC
- * @v cqe Receive completion queue entry
- */
- static void txnic_complete_rqe ( struct txnic *vnic,
- struct txnic_cqe_rx *cqe ) {
- struct net_device *netdev = vnic->netdev;
- struct io_buffer *iobuf;
- unsigned int errop;
- unsigned int rq_idx;
- unsigned int rq_iobuf_idx;
- size_t apad_len;
- size_t len;
-
- /* Parse completion */
- errop = cqe->errop;
- apad_len = TXNIC_CQE_RX_APAD_LEN ( cqe->apad );
- len = le16_to_cpu ( cqe->len );
-
- /* Get next receive I/O buffer */
- rq_idx = ( vnic->rq.cons++ % TXNIC_RQES );
- rq_iobuf_idx = ( rq_idx % TXNIC_RQ_FILL );
- iobuf = vnic->rq.iobuf[rq_iobuf_idx];
- vnic->rq.iobuf[rq_iobuf_idx] = NULL;
-
- /* Populate I/O buffer */
- iob_reserve ( iobuf, apad_len );
- iob_put ( iobuf, len );
-
- /* Hand off to network stack */
- if ( errop ) {
- DBGC ( vnic, "TXNIC %s RQE %#03x error (length %zd, errop "
- "%#02x)\n", vnic->name, rq_idx, len, errop );
- netdev_rx_err ( netdev, iobuf, -EIO );
- } else {
- DBGC2 ( vnic, "TXNIC %s RQE %#03x complete (length %zd)\n",
- vnic->name, rq_idx, len );
- netdev_rx ( netdev, iobuf );
- }
- }
-
- /******************************************************************************
- *
- * Completion queue
- *
- ******************************************************************************
- */
-
- /**
- * Create completion queue
- *
- * @v vnic Virtual NIC
- * @ret rc Return status code
- */
- static int txnic_create_cq ( struct txnic *vnic ) {
-
- /* Reset completion queue */
- vnic->cq.cons = 0;
- writeq ( TXNIC_QS_CQ_CFG_RESET, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) );
-
- /* Configure and enable completion queue */
- writeq ( user_to_phys ( vnic->cq.cqe, 0 ),
- ( vnic->regs + TXNIC_QS_CQ_BASE(0) ) );
- writeq ( ( TXNIC_QS_CQ_CFG_ENA | TXNIC_QS_CQ_CFG_QSIZE_256 ),
- ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) );
-
- DBGC ( vnic, "TXNIC %s CQ at [%08lx,%08lx)\n",
- vnic->name, user_to_phys ( vnic->cq.cqe, 0 ),
- user_to_phys ( vnic->cq.cqe, TXNIC_CQ_SIZE ) );
- return 0;
- }
-
- /**
- * Disable completion queue
- *
- * @v vnic Virtual NIC
- * @ret rc Return status code
- */
- static int txnic_disable_cq ( struct txnic *vnic ) {
- uint64_t cfg;
- unsigned int i;
-
- /* Disable completion queue */
- writeq ( 0, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) );
-
- /* Wait for completion queue to be disabled */
- for ( i = 0 ; i < TXNIC_CQ_DISABLE_MAX_WAIT_MS ; i++ ) {
-
- /* Check if completion queue is disabled */
- cfg = readq ( vnic->regs + TXNIC_QS_CQ_CFG(0) );
- if ( ! ( cfg & TXNIC_QS_CQ_CFG_ENA ) )
- return 0;
-
- /* Delay */
- mdelay ( 1 );
- }
-
- DBGC ( vnic, "TXNIC %s CQ disable timed out\n", vnic->name );
- return -ETIMEDOUT;
- }
-
- /**
- * Destroy completion queue
- *
- * @v vnic Virtual NIC
- */
- static void txnic_destroy_cq ( struct txnic *vnic ) {
- int rc;
-
- /* Disable completion queue */
- if ( ( rc = txnic_disable_cq ( vnic ) ) != 0 ) {
- /* Leak memory; there's nothing else we can do */
- return;
- }
-
- /* Reset completion queue */
- writeq ( TXNIC_QS_CQ_CFG_RESET, ( vnic->regs + TXNIC_QS_CQ_CFG(0) ) );
- }
-
- /**
- * Poll completion queue
- *
- * @v vnic Virtual NIC
- */
- static void txnic_poll_cq ( struct txnic *vnic ) {
- union txnic_cqe cqe;
- uint64_t status;
- size_t offset;
- unsigned int qcount;
- unsigned int cq_idx;
- unsigned int i;
-
- /* Get number of completions */
- status = readq ( vnic->regs + TXNIC_QS_CQ_STATUS(0) );
- qcount = TXNIC_QS_CQ_STATUS_QCOUNT ( status );
- if ( ! qcount )
- return;
-
- /* Process completion queue entries */
- for ( i = 0 ; i < qcount ; i++ ) {
-
- /* Get completion queue entry */
- cq_idx = ( vnic->cq.cons++ % TXNIC_CQES );
- offset = ( cq_idx * TXNIC_CQ_STRIDE );
- copy_from_user ( &cqe, vnic->cq.cqe, offset, sizeof ( cqe ) );
-
- /* Process completion queue entry */
- switch ( cqe.common.cqe_type ) {
- case TXNIC_CQE_TYPE_SEND:
- txnic_complete_sqe ( vnic, &cqe.send );
- break;
- case TXNIC_CQE_TYPE_RX:
- txnic_complete_rqe ( vnic, &cqe.rx );
- break;
- default:
- DBGC ( vnic, "TXNIC %s unknown completion type %d\n",
- vnic->name, cqe.common.cqe_type );
- DBGC_HDA ( vnic, user_to_phys ( vnic->cq.cqe, offset ),
- &cqe, sizeof ( cqe ) );
- break;
- }
- }
-
- /* Ring doorbell */
- writeq ( qcount, ( vnic->regs + TXNIC_QS_CQ_DOOR(0) ) );
- }
-
- /******************************************************************************
- *
- * Virtual NIC
- *
- ******************************************************************************
- */
-
- /**
- * Open virtual NIC
- *
- * @v vnic Virtual NIC
- * @ret rc Return status code
- */
- static int txnic_open ( struct txnic *vnic ) {
- int rc;
-
- /* Create completion queue */
- if ( ( rc = txnic_create_cq ( vnic ) ) != 0 )
- goto err_create_cq;
-
- /* Create send queue */
- if ( ( rc = txnic_create_sq ( vnic ) ) != 0 )
- goto err_create_sq;
-
- /* Create receive queue */
- if ( ( rc = txnic_create_rq ( vnic ) ) != 0 )
- goto err_create_rq;
-
- /* Refill receive queue */
- txnic_refill_rq ( vnic );
-
- return 0;
-
- txnic_destroy_rq ( vnic );
- err_create_rq:
- txnic_destroy_sq ( vnic );
- err_create_sq:
- txnic_destroy_cq ( vnic );
- err_create_cq:
- return rc;
- }
-
- /**
- * Close virtual NIC
- *
- * @v vnic Virtual NIC
- */
- static void txnic_close ( struct txnic *vnic ) {
-
- /* Destroy receive queue */
- txnic_destroy_rq ( vnic );
-
- /* Destroy send queue */
- txnic_destroy_sq ( vnic );
-
- /* Destroy completion queue */
- txnic_destroy_cq ( vnic );
- }
-
- /**
- * Poll virtual NIC
- *
- * @v vnic Virtual NIC
- */
- static void txnic_poll ( struct txnic *vnic ) {
-
- /* Poll completion queue */
- txnic_poll_cq ( vnic );
-
- /* Refill receive queue */
- txnic_refill_rq ( vnic );
- }
-
- /**
- * Allocate virtual NIC
- *
- * @v dev Underlying device
- * @v membase Register base address
- * @ret vnic Virtual NIC, or NULL on failure
- */
- static struct txnic * txnic_alloc ( struct device *dev,
- unsigned long membase ) {
- struct net_device *netdev;
- struct txnic *vnic;
-
- /* Allocate network device */
- netdev = alloc_etherdev ( sizeof ( *vnic ) );
- if ( ! netdev )
- goto err_alloc_netdev;
- netdev->dev = dev;
- vnic = netdev->priv;
- vnic->netdev = netdev;
- vnic->name = dev->name;
-
- /* Allow caller to reuse netdev->priv. (The generic virtual
- * NIC code never assumes that netdev->priv==vnic.)
- */
- netdev->priv = NULL;
-
- /* Allocate completion queue */
- vnic->cq.cqe = umalloc ( TXNIC_CQ_SIZE );
- if ( ! vnic->cq.cqe )
- goto err_alloc_cq;
-
- /* Allocate send queue */
- vnic->sq.sqe = umalloc ( TXNIC_SQ_SIZE );
- if ( ! vnic->sq.sqe )
- goto err_alloc_sq;
-
- /* Allocate receive queue */
- vnic->rq.rqe = umalloc ( TXNIC_RQ_SIZE );
- if ( ! vnic->rq.rqe )
- goto err_alloc_rq;
-
- /* Map registers */
- vnic->regs = ioremap ( membase, TXNIC_VF_BAR_SIZE );
- if ( ! vnic->regs )
- goto err_ioremap;
-
- return vnic;
-
- iounmap ( vnic->regs );
- err_ioremap:
- ufree ( vnic->rq.rqe );
- err_alloc_rq:
- ufree ( vnic->sq.sqe );
- err_alloc_sq:
- ufree ( vnic->cq.cqe );
- err_alloc_cq:
- netdev_nullify ( netdev );
- netdev_put ( netdev );
- err_alloc_netdev:
- return NULL;
- }
-
- /**
- * Free virtual NIC
- *
- * @v vnic Virtual NIC
- */
- static void txnic_free ( struct txnic *vnic ) {
- struct net_device *netdev = vnic->netdev;
-
- /* Unmap registers */
- iounmap ( vnic->regs );
-
- /* Free receive queue */
- ufree ( vnic->rq.rqe );
-
- /* Free send queue */
- ufree ( vnic->sq.sqe );
-
- /* Free completion queue */
- ufree ( vnic->cq.cqe );
-
- /* Free network device */
- netdev_nullify ( netdev );
- netdev_put ( netdev );
- }
-
- /******************************************************************************
- *
- * Logical MAC virtual NICs
- *
- ******************************************************************************
- */
-
- /**
- * Show LMAC diagnostics (for debugging)
- *
- * @v lmac Logical MAC
- */
- static __attribute__ (( unused )) void
- txnic_lmac_diag ( struct txnic_lmac *lmac ) {
- struct txnic *vnic = lmac->vnic;
- uint64_t status1;
- uint64_t status2;
- uint64_t br_status1;
- uint64_t br_status2;
- uint64_t br_algn_status;
- uint64_t br_pmd_status;
- uint64_t an_status;
-
- /* Read status (clearing latching bits) */
- writeq ( BGX_SPU_STATUS1_RCV_LNK, ( lmac->regs + BGX_SPU_STATUS1 ) );
- writeq ( BGX_SPU_STATUS2_RCVFLT, ( lmac->regs + BGX_SPU_STATUS2 ) );
- status1 = readq ( lmac->regs + BGX_SPU_STATUS1 );
- status2 = readq ( lmac->regs + BGX_SPU_STATUS2 );
- DBGC ( vnic, "TXNIC %s SPU %02llx:%04llx%s%s%s\n",
- vnic->name, status1, status2,
- ( ( status1 & BGX_SPU_STATUS1_FLT ) ? " FLT" : "" ),
- ( ( status1 & BGX_SPU_STATUS1_RCV_LNK ) ? " RCV_LNK" : "" ),
- ( ( status2 & BGX_SPU_STATUS2_RCVFLT ) ? " RCVFLT" : "" ) );
-
- /* Read BASE-R status (clearing latching bits) */
- writeq ( ( BGX_SPU_BR_STATUS2_LATCHED_LOCK |
- BGX_SPU_BR_STATUS2_LATCHED_BER ),
- ( lmac->regs + BGX_SPU_BR_STATUS2 ) );
- br_status1 = readq ( lmac->regs + BGX_SPU_BR_STATUS1 );
- br_status2 = readq ( lmac->regs + BGX_SPU_BR_STATUS2 );
- DBGC ( vnic, "TXNIC %s BR %04llx:%04llx%s%s%s%s%s\n",
- vnic->name, br_status2, br_status2,
- ( ( br_status1 & BGX_SPU_BR_STATUS1_RCV_LNK ) ? " RCV_LNK" : ""),
- ( ( br_status1 & BGX_SPU_BR_STATUS1_HI_BER ) ? " HI_BER" : "" ),
- ( ( br_status1 & BGX_SPU_BR_STATUS1_BLK_LOCK ) ?
- " BLK_LOCK" : "" ),
- ( ( br_status2 & BGX_SPU_BR_STATUS2_LATCHED_LOCK ) ?
- " LATCHED_LOCK" : "" ),
- ( ( br_status2 & BGX_SPU_BR_STATUS2_LATCHED_BER ) ?
- " LATCHED_BER" : "" ) );
-
- /* Read BASE-R alignment status */
- br_algn_status = readq ( lmac->regs + BGX_SPU_BR_ALGN_STATUS );
- DBGC ( vnic, "TXNIC %s BR ALGN %016llx%s\n", vnic->name, br_algn_status,
- ( ( br_algn_status & BGX_SPU_BR_ALGN_STATUS_ALIGND ) ?
- " ALIGND" : "" ) );
-
- /* Read BASE-R link training status */
- br_pmd_status = readq ( lmac->regs + BGX_SPU_BR_PMD_STATUS );
- DBGC ( vnic, "TXNIC %s BR PMD %04llx\n", vnic->name, br_pmd_status );
-
- /* Read autonegotiation status (clearing latching bits) */
- writeq ( ( BGX_SPU_AN_STATUS_PAGE_RX | BGX_SPU_AN_STATUS_LINK_STATUS ),
- ( lmac->regs + BGX_SPU_AN_STATUS ) );
- an_status = readq ( lmac->regs + BGX_SPU_AN_STATUS );
- DBGC ( vnic, "TXNIC %s BR AN %04llx%s%s%s%s%s\n", vnic->name, an_status,
- ( ( an_status & BGX_SPU_AN_STATUS_XNP_STAT ) ? " XNP_STAT" : ""),
- ( ( an_status & BGX_SPU_AN_STATUS_PAGE_RX ) ? " PAGE_RX" : "" ),
- ( ( an_status & BGX_SPU_AN_STATUS_AN_COMPLETE ) ?
- " AN_COMPLETE" : "" ),
- ( ( an_status & BGX_SPU_AN_STATUS_LINK_STATUS ) ?
- " LINK_STATUS" : "" ),
- ( ( an_status & BGX_SPU_AN_STATUS_LP_AN_ABLE ) ?
- " LP_AN_ABLE" : "" ) );
-
- /* Read transmit statistics */
- DBGC ( vnic, "TXNIC %s TXF xc %#llx xd %#llx mc %#llx sc %#llx ok "
- "%#llx bc %#llx mc %#llx un %#llx pa %#llx\n", vnic->name,
- readq ( lmac->regs + BGX_CMR_TX_STAT0 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT1 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT2 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT3 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT5 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT14 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT15 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT16 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT17 ) );
- DBGC ( vnic, "TXNIC %s TXB ok %#llx hist %#llx:%#llx:%#llx:%#llx:"
- "%#llx:%#llx:%#llx:%#llx\n", vnic->name,
- readq ( lmac->regs + BGX_CMR_TX_STAT4 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT6 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT7 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT8 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT9 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT10 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT11 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT12 ),
- readq ( lmac->regs + BGX_CMR_TX_STAT13 ) );
-
- /* Read receive statistics */
- DBGC ( vnic, "TXNIC %s RXF ok %#llx pa %#llx nm %#llx ov %#llx er "
- "%#llx nc %#llx\n", vnic->name,
- readq ( lmac->regs + BGX_CMR_RX_STAT0 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT2 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT4 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT6 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT8 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT9 ) );
- DBGC ( vnic, "TXNIC %s RXB ok %#llx pa %#llx nm %#llx ov %#llx nc "
- "%#llx\n", vnic->name,
- readq ( lmac->regs + BGX_CMR_RX_STAT1 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT3 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT5 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT7 ),
- readq ( lmac->regs + BGX_CMR_RX_STAT10 ) );
- }
-
- /**
- * Update LMAC link state
- *
- * @v lmac Logical MAC
- */
- static void txnic_lmac_update_link ( struct txnic_lmac *lmac ) {
- struct txnic *vnic = lmac->vnic;
- struct net_device *netdev = vnic->netdev;
- uint64_t status1;
-
- /* Read status (clearing latching bits) */
- writeq ( BGX_SPU_STATUS1_RCV_LNK, ( lmac->regs + BGX_SPU_STATUS1 ) );
- status1 = readq ( lmac->regs + BGX_SPU_STATUS1 );
-
- /* Report link status */
- if ( status1 & BGX_SPU_STATUS1_RCV_LNK ) {
- netdev_link_up ( netdev );
- } else {
- netdev_link_down ( netdev );
- }
- }
-
- /**
- * Poll LMAC link state
- *
- * @v lmac Logical MAC
- */
- static void txnic_lmac_poll_link ( struct txnic_lmac *lmac ) {
- struct txnic *vnic = lmac->vnic;
- uint64_t intr;
-
- /* Get interrupt status */
- intr = readq ( lmac->regs + BGX_SPU_INT );
- if ( ! intr )
- return;
- DBGC ( vnic, "TXNIC %s INT %04llx%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
- vnic->name, intr,
- ( ( intr & BGX_SPU_INT_TRAINING_FAIL ) ? " TRAINING_FAIL" : "" ),
- ( ( intr & BGX_SPU_INT_TRAINING_DONE ) ? " TRAINING_DONE" : "" ),
- ( ( intr & BGX_SPU_INT_AN_COMPLETE ) ? " AN_COMPLETE" : "" ),
- ( ( intr & BGX_SPU_INT_AN_LINK_GOOD ) ? " AN_LINK_GOOD" : "" ),
- ( ( intr & BGX_SPU_INT_AN_PAGE_RX ) ? " AN_PAGE_RX" : "" ),
- ( ( intr & BGX_SPU_INT_FEC_UNCORR ) ? " FEC_UNCORR" : "" ),
- ( ( intr & BGX_SPU_INT_FEC_CORR ) ? " FEC_CORR" : "" ),
- ( ( intr & BGX_SPU_INT_BIP_ERR ) ? " BIP_ERR" : "" ),
- ( ( intr & BGX_SPU_INT_DBG_SYNC ) ? " DBG_SYNC" : "" ),
- ( ( intr & BGX_SPU_INT_ALGNLOS ) ? " ALGNLOS" : "" ),
- ( ( intr & BGX_SPU_INT_SYNLOS ) ? " SYNLOS" : "" ),
- ( ( intr & BGX_SPU_INT_BITLCKLS ) ? " BITLCKLS" : "" ),
- ( ( intr & BGX_SPU_INT_ERR_BLK ) ? " ERR_BLK" : "" ),
- ( ( intr & BGX_SPU_INT_RX_LINK_DOWN ) ? " RX_LINK_DOWN" : "" ),
- ( ( intr & BGX_SPU_INT_RX_LINK_UP ) ? " RX_LINK_UP" : "" ) );
-
- /* Clear interrupt status */
- writeq ( intr, ( lmac->regs + BGX_SPU_INT ) );
-
- /* Update link state */
- txnic_lmac_update_link ( lmac );
- }
-
- /**
- * Reset LMAC
- *
- * @v lmac Logical MAC
- */
- static void txnic_lmac_reset ( struct txnic_lmac *lmac ) {
- struct txnic_bgx *bgx = lmac->bgx;
- struct txnic_pf *pf = bgx->pf;
- void *qsregs = ( pf->regs + TXNIC_PF_QS ( lmac->idx ) );
-
- /* There is no reset available for the physical function
- * aspects of a virtual NIC; we have to explicitly reload a
- * sensible set of default values.
- */
- writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) );
- writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_CFG(0) ) );
- writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_DROP_CFG(0) ) );
- writeq ( 0, ( qsregs + TXNIC_PF_QS_RQ_BP_CFG(0) ) );
- writeq ( 0, ( qsregs + TXNIC_PF_QS_SQ_CFG(0) ) );
- }
-
- /**
- * Open network device
- *
- * @v netdev Network device
- * @ret rc Return status code
- */
- static int txnic_lmac_open ( struct net_device *netdev ) {
- struct txnic_lmac *lmac = netdev->priv;
- struct txnic_bgx *bgx = lmac->bgx;
- struct txnic_pf *pf = bgx->pf;
- struct txnic *vnic = lmac->vnic;
- unsigned int vnic_idx = lmac->idx;
- unsigned int chan_idx = TXNIC_CHAN_IDX ( vnic_idx );
- unsigned int tl4_idx = TXNIC_TL4_IDX ( vnic_idx );
- unsigned int tl3_idx = TXNIC_TL3_IDX ( vnic_idx );
- unsigned int tl2_idx = TXNIC_TL2_IDX ( vnic_idx );
- void *lmregs = ( pf->regs + TXNIC_PF_LMAC ( vnic_idx ) );
- void *chregs = ( pf->regs + TXNIC_PF_CHAN ( chan_idx ) );
- void *qsregs = ( pf->regs + TXNIC_PF_QS ( vnic_idx ) );
- size_t max_pkt_size;
- int rc;
-
- /* Configure channel/match parse indices */
- writeq ( ( TXNIC_PF_MPI_CFG_VNIC ( vnic_idx ) |
- TXNIC_PF_MPI_CFG_RSSI_BASE ( vnic_idx ) ),
- ( TXNIC_PF_MPI_CFG ( vnic_idx ) + pf->regs ) );
- writeq ( ( TXNIC_PF_RSSI_RQ_RQ_QS ( vnic_idx ) ),
- ( TXNIC_PF_RSSI_RQ ( vnic_idx ) + pf->regs ) );
-
- /* Configure LMAC */
- max_pkt_size = ( netdev->max_pkt_len + 4 /* possible VLAN */ );
- writeq ( ( TXNIC_PF_LMAC_CFG_ADJUST_DEFAULT |
- TXNIC_PF_LMAC_CFG_MIN_PKT_SIZE ( ETH_ZLEN ) ),
- ( TXNIC_PF_LMAC_CFG + lmregs ) );
- writeq ( ( TXNIC_PF_LMAC_CFG2_MAX_PKT_SIZE ( max_pkt_size ) ),
- ( TXNIC_PF_LMAC_CFG2 + lmregs ) );
- writeq ( ( TXNIC_PF_LMAC_CREDIT_CC_UNIT_CNT_DEFAULT |
- TXNIC_PF_LMAC_CREDIT_CC_PACKET_CNT_DEFAULT |
- TXNIC_PF_LMAC_CREDIT_CC_ENABLE ),
- ( TXNIC_PF_LMAC_CREDIT + lmregs ) );
-
- /* Configure channels */
- writeq ( ( TXNIC_PF_CHAN_TX_CFG_BP_ENA ),
- ( TXNIC_PF_CHAN_TX_CFG + chregs ) );
- writeq ( ( TXNIC_PF_CHAN_RX_CFG_CPI_BASE ( vnic_idx ) ),
- ( TXNIC_PF_CHAN_RX_CFG + chregs ) );
- writeq ( ( TXNIC_PF_CHAN_RX_BP_CFG_ENA |
- TXNIC_PF_CHAN_RX_BP_CFG_BPID ( vnic_idx ) ),
- ( TXNIC_PF_CHAN_RX_BP_CFG + chregs ) );
-
- /* Configure traffic limiters */
- writeq ( ( TXNIC_PF_TL2_CFG_RR_QUANTUM_DEFAULT ),
- ( TXNIC_PF_TL2_CFG ( tl2_idx ) + pf->regs ) );
- writeq ( ( TXNIC_PF_TL3_CFG_RR_QUANTUM_DEFAULT ),
- ( TXNIC_PF_TL3_CFG ( tl3_idx ) + pf->regs ) );
- writeq ( ( TXNIC_PF_TL3_CHAN_CHAN ( chan_idx ) ),
- ( TXNIC_PF_TL3_CHAN ( tl3_idx ) + pf->regs ) );
- writeq ( ( TXNIC_PF_TL4_CFG_SQ_QS ( vnic_idx ) |
- TXNIC_PF_TL4_CFG_RR_QUANTUM_DEFAULT ),
- ( TXNIC_PF_TL4_CFG ( tl4_idx ) + pf->regs ) );
-
- /* Configure send queue */
- writeq ( ( TXNIC_PF_QS_SQ_CFG_CQ_QS ( vnic_idx ) ),
- ( TXNIC_PF_QS_SQ_CFG(0) + qsregs ) );
- writeq ( ( TXNIC_PF_QS_SQ_CFG2_TL4 ( tl4_idx ) ),
- ( TXNIC_PF_QS_SQ_CFG2(0) + qsregs ) );
-
- /* Configure receive queue */
- writeq ( ( TXNIC_PF_QS_RQ_CFG_CACHING_ALL |
- TXNIC_PF_QS_RQ_CFG_CQ_QS ( vnic_idx ) |
- TXNIC_PF_QS_RQ_CFG_RBDR_CONT_QS ( vnic_idx ) |
- TXNIC_PF_QS_RQ_CFG_RBDR_STRT_QS ( vnic_idx ) ),
- ( TXNIC_PF_QS_RQ_CFG(0) + qsregs ) );
- writeq ( ( TXNIC_PF_QS_RQ_BP_CFG_RBDR_BP_ENA |
- TXNIC_PF_QS_RQ_BP_CFG_CQ_BP_ENA |
- TXNIC_PF_QS_RQ_BP_CFG_BPID ( vnic_idx ) ),
- ( TXNIC_PF_QS_RQ_BP_CFG(0) + qsregs ) );
-
- /* Enable queue set */
- writeq ( ( TXNIC_PF_QS_CFG_ENA | TXNIC_PF_QS_CFG_VNIC ( vnic_idx ) ),
- ( TXNIC_PF_QS_CFG + qsregs ) );
-
- /* Open virtual NIC */
- if ( ( rc = txnic_open ( vnic ) ) != 0 )
- goto err_open;
-
- /* Update link state */
- txnic_lmac_update_link ( lmac );
-
- return 0;
-
- txnic_close ( vnic );
- err_open:
- writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) );
- return rc;
- }
-
- /**
- * Close network device
- *
- * @v netdev Network device
- */
- static void txnic_lmac_close ( struct net_device *netdev ) {
- struct txnic_lmac *lmac = netdev->priv;
- struct txnic_bgx *bgx = lmac->bgx;
- struct txnic_pf *pf = bgx->pf;
- struct txnic *vnic = lmac->vnic;
- void *qsregs = ( pf->regs + TXNIC_PF_QS ( lmac->idx ) );
-
- /* Close virtual NIC */
- txnic_close ( vnic );
-
- /* Disable queue set */
- writeq ( 0, ( qsregs + TXNIC_PF_QS_CFG ) );
- }
-
- /**
- * Transmit packet
- *
- * @v netdev Network device
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
- static int txnic_lmac_transmit ( struct net_device *netdev,
- struct io_buffer *iobuf ) {
- struct txnic_lmac *lmac = netdev->priv;
- struct txnic *vnic = lmac->vnic;
-
- return txnic_send ( vnic, iobuf );
- }
-
- /**
- * Poll network device
- *
- * @v netdev Network device
- */
- static void txnic_lmac_poll ( struct net_device *netdev ) {
- struct txnic_lmac *lmac = netdev->priv;
- struct txnic *vnic = lmac->vnic;
-
- /* Poll virtual NIC */
- txnic_poll ( vnic );
-
- /* Poll link state */
- txnic_lmac_poll_link ( lmac );
- }
-
- /** Network device operations */
- static struct net_device_operations txnic_lmac_operations = {
- .open = txnic_lmac_open,
- .close = txnic_lmac_close,
- .transmit = txnic_lmac_transmit,
- .poll = txnic_lmac_poll,
- };
-
- /**
- * Probe logical MAC virtual NIC
- *
- * @v lmac Logical MAC
- * @ret rc Return status code
- */
- static int txnic_lmac_probe ( struct txnic_lmac *lmac ) {
- struct txnic_bgx *bgx = lmac->bgx;
- struct txnic_pf *pf = bgx->pf;
- struct txnic *vnic;
- struct net_device *netdev;
- unsigned long membase;
- int rc;
-
- /* Sanity check */
- assert ( lmac->vnic == NULL );
-
- /* Calculate register base address */
- membase = ( pf->vf_membase + ( lmac->idx * pf->vf_stride ) );
-
- /* Allocate and initialise network device */
- vnic = txnic_alloc ( &bgx->pci->dev, membase );
- if ( ! vnic ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- netdev = vnic->netdev;
- netdev_init ( netdev, &txnic_lmac_operations );
- netdev->priv = lmac;
- lmac->vnic = vnic;
-
- /* Reset device */
- txnic_lmac_reset ( lmac );
-
- /* Set MAC address */
- memcpy ( netdev->hw_addr, lmac->mac.raw, ETH_ALEN );
-
- /* Register network device */
- if ( ( rc = register_netdev ( netdev ) ) != 0 )
- goto err_register;
- vnic->name = netdev->name;
- DBGC ( TXNICCOL ( pf ), "TXNIC %d/%d/%d is %s (%s)\n", pf->node,
- bgx->idx, lmac->idx, vnic->name, eth_ntoa ( lmac->mac.raw ) );
-
- /* Update link state */
- txnic_lmac_update_link ( lmac );
-
- return 0;
-
- unregister_netdev ( netdev );
- err_register:
- txnic_lmac_reset ( lmac );
- txnic_free ( vnic );
- lmac->vnic = NULL;
- err_alloc:
- return rc;
- }
-
- /**
- * Remove logical MAC virtual NIC
- *
- * @v lmac Logical MAC
- */
- static void txnic_lmac_remove ( struct txnic_lmac *lmac ) {
- uint64_t config;
-
- /* Sanity check */
- assert ( lmac->vnic != NULL );
-
- /* Disable packet receive and transmit */
- config = readq ( lmac->regs + BGX_CMR_CONFIG );
- config &= ~( BGX_CMR_CONFIG_DATA_PKT_TX_EN |
- BGX_CMR_CONFIG_DATA_PKT_RX_EN );
- writeq ( config, ( lmac->regs + BGX_CMR_CONFIG ) );
-
- /* Unregister network device */
- unregister_netdev ( lmac->vnic->netdev );
-
- /* Reset device */
- txnic_lmac_reset ( lmac );
-
- /* Free virtual NIC */
- txnic_free ( lmac->vnic );
- lmac->vnic = NULL;
- }
-
- /**
- * Probe all LMACs on a BGX Ethernet interface
- *
- * @v pf Physical function
- * @v bgx BGX Ethernet interface
- * @ret rc Return status code
- */
- static int txnic_lmac_probe_all ( struct txnic_pf *pf, struct txnic_bgx *bgx ) {
- unsigned int bgx_idx;
- int lmac_idx;
- int count;
- int rc;
-
- /* Sanity checks */
- bgx_idx = bgx->idx;
- assert ( pf->node == bgx->node );
- assert ( pf->bgx[bgx_idx] == NULL );
- assert ( bgx->pf == NULL );
-
- /* Associate BGX with physical function */
- pf->bgx[bgx_idx] = bgx;
- bgx->pf = pf;
-
- /* Probe all LMACs */
- count = bgx->count;
- for ( lmac_idx = 0 ; lmac_idx < count ; lmac_idx++ ) {
- if ( ( rc = txnic_lmac_probe ( &bgx->lmac[lmac_idx] ) ) != 0 )
- goto err_probe;
- }
-
- return 0;
-
- lmac_idx = count;
- err_probe:
- for ( lmac_idx-- ; lmac_idx >= 0 ; lmac_idx-- )
- txnic_lmac_remove ( &bgx->lmac[lmac_idx] );
- pf->bgx[bgx_idx] = NULL;
- bgx->pf = NULL;
- return rc;
- }
-
- /**
- * Remove all LMACs on a BGX Ethernet interface
- *
- * @v pf Physical function
- * @v bgx BGX Ethernet interface
- */
- static void txnic_lmac_remove_all ( struct txnic_pf *pf,
- struct txnic_bgx *bgx ) {
- unsigned int lmac_idx;
-
- /* Sanity checks */
- assert ( pf->bgx[bgx->idx] == bgx );
- assert ( bgx->pf == pf );
-
- /* Remove all LMACs */
- for ( lmac_idx = 0 ; lmac_idx < bgx->count ; lmac_idx++ )
- txnic_lmac_remove ( &bgx->lmac[lmac_idx] );
-
- /* Disassociate BGX from physical function */
- pf->bgx[bgx->idx] = NULL;
- bgx->pf = NULL;
- }
-
- /******************************************************************************
- *
- * NIC physical function interface
- *
- ******************************************************************************
- */
-
- /**
- * Probe PCI device
- *
- * @v pci PCI device
- * @ret rc Return status code
- */
- static int txnic_pf_probe ( struct pci_device *pci ) {
- struct txnic_pf *pf;
- struct txnic_bgx *bgx;
- unsigned long membase;
- unsigned int i;
- int rc;
-
- /* Allocate and initialise structure */
- pf = zalloc ( sizeof ( *pf ) );
- if ( ! pf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- pf->pci = pci;
- pci_set_drvdata ( pci, pf );
-
- /* Get base addresses */
- membase = pciea_bar_start ( pci, PCIEA_BEI_BAR_0 );
- pf->vf_membase = pciea_bar_start ( pci, PCIEA_BEI_VF_BAR_0 );
- pf->vf_stride = pciea_bar_size ( pci, PCIEA_BEI_VF_BAR_0 );
-
- /* Calculate node ID */
- pf->node = txnic_address_node ( membase );
- DBGC ( TXNICCOL ( pf ), "TXNIC %d/*/* PF %s at %#lx (VF %#lx+%#lx)\n",
- pf->node, pci->dev.name, membase, pf->vf_membase, pf->vf_stride);
-
- /* Fix up PCI device */
- adjust_pci_device ( pci );
-
- /* Map registers */
- pf->regs = ioremap ( membase, TXNIC_PF_BAR_SIZE );
- if ( ! pf->regs ) {
- rc = -ENODEV;
- goto err_ioremap;
- }
-
- /* Configure physical function */
- writeq ( TXNIC_PF_CFG_ENA, ( pf->regs + TXNIC_PF_CFG ) );
- writeq ( ( TXNIC_PF_BP_CFG_BP_POLL_ENA |
- TXNIC_PF_BP_CFG_BP_POLL_DLY_DEFAULT ),
- ( pf->regs + TXNIC_PF_BP_CFG ) );
- for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) {
- writeq ( ( TXNIC_PF_INTF_SEND_CFG_BLOCK_BGX |
- TXNIC_PF_INTF_SEND_CFG_BLOCK ( i ) ),
- ( pf->regs + TXNIC_PF_INTF_SEND_CFG ( i ) ) );
- writeq ( ( TXNIC_PF_INTF_BP_CFG_BP_ENA |
- TXNIC_PF_INTF_BP_CFG_BP_ID_BGX |
- TXNIC_PF_INTF_BP_CFG_BP_ID ( i ) ),
- ( pf->regs + TXNIC_PF_INTF_BP_CFG ( i ) ) );
- }
- writeq ( ( TXNIC_PF_PKIND_CFG_LENERR_EN |
- TXNIC_PF_PKIND_CFG_MAXLEN_DISABLE |
- TXNIC_PF_PKIND_CFG_MINLEN_DISABLE ),
- ( pf->regs + TXNIC_PF_PKIND_CFG(0) ) );
-
- /* Add to list of physical functions */
- list_add_tail ( &pf->list, &txnic_pfs );
-
- /* Probe all LMACs, if applicable */
- list_for_each_entry ( bgx, &txnic_bgxs, list ) {
- if ( bgx->node != pf->node )
- continue;
- if ( ( rc = txnic_lmac_probe_all ( pf, bgx ) ) != 0 )
- goto err_probe;
- }
-
- return 0;
-
- err_probe:
- for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) {
- if ( pf->bgx[i] )
- txnic_lmac_remove_all ( pf, pf->bgx[i] );
- }
- list_del ( &pf->list );
- writeq ( 0, ( pf->regs + TXNIC_PF_CFG ) );
- iounmap ( pf->regs );
- err_ioremap:
- free ( pf );
- err_alloc:
- return rc;
- }
-
- /**
- * Remove PCI device
- *
- * @v pci PCI device
- */
- static void txnic_pf_remove ( struct pci_device *pci ) {
- struct txnic_pf *pf = pci_get_drvdata ( pci );
- unsigned int i;
-
- /* Remove all LMACs, if applicable */
- for ( i = 0 ; i < TXNIC_NUM_BGX ; i++ ) {
- if ( pf->bgx[i] )
- txnic_lmac_remove_all ( pf, pf->bgx[i] );
- }
-
- /* Remove from list of physical functions */
- list_del ( &pf->list );
-
- /* Unmap registers */
- iounmap ( pf->regs );
-
- /* Free physical function */
- free ( pf );
- }
-
- /** NIC physical function PCI device IDs */
- static struct pci_device_id txnic_pf_ids[] = {
- PCI_ROM ( 0x177d, 0xa01e, "thunder-pf", "ThunderX NIC PF", 0 ),
- };
-
- /** NIC physical function PCI driver */
- struct pci_driver txnic_pf_driver __pci_driver = {
- .ids = txnic_pf_ids,
- .id_count = ( sizeof ( txnic_pf_ids ) / sizeof ( txnic_pf_ids[0] ) ),
- .probe = txnic_pf_probe,
- .remove = txnic_pf_remove,
- };
-
- /******************************************************************************
- *
- * BGX interface
- *
- ******************************************************************************
- */
-
- /** LMAC types */
- static struct txnic_lmac_type txnic_lmac_types[] = {
- [TXNIC_LMAC_XAUI] = {
- .name = "XAUI",
- .count = 1,
- .lane_to_sds = 0xe4,
- },
- [TXNIC_LMAC_RXAUI] = {
- .name = "RXAUI",
- .count = 2,
- .lane_to_sds = 0x0e04,
- },
- [TXNIC_LMAC_10G_R] = {
- .name = "10GBASE-R",
- .count = 4,
- .lane_to_sds = 0x00000000,
- },
- [TXNIC_LMAC_40G_R] = {
- .name = "40GBASE-R",
- .count = 1,
- .lane_to_sds = 0xe4,
- },
- };
-
- /**
- * Detect BGX Ethernet interface LMAC type
- *
- * @v bgx BGX Ethernet interface
- * @ret type LMAC type, or negative error
- */
- static int txnic_bgx_detect ( struct txnic_bgx *bgx ) {
- uint64_t config;
- uint64_t br_pmd_control;
- uint64_t rx_lmacs;
- unsigned int type;
-
- /* We assume that the early (pre-UEFI) firmware will have
- * configured at least the LMAC 0 type and use of link
- * training, and may have overridden the number of LMACs.
- */
-
- /* Determine type from LMAC 0 */
- config = readq ( bgx->regs + BGX_CMR_CONFIG );
- type = BGX_CMR_CONFIG_LMAC_TYPE_GET ( config );
- if ( ( type >= ( sizeof ( txnic_lmac_types ) /
- sizeof ( txnic_lmac_types[0] ) ) ) ||
- ( txnic_lmac_types[type].count == 0 ) ) {
- DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* BGX unknown type %d\n",
- bgx->node, bgx->idx, type );
- return -ENOTTY;
- }
- bgx->type = &txnic_lmac_types[type];
-
- /* Check whether link training is required */
- br_pmd_control = readq ( bgx->regs + BGX_SPU_BR_PMD_CONTROL );
- bgx->training =
- ( !! ( br_pmd_control & BGX_SPU_BR_PMD_CONTROL_TRAIN_EN ) );
-
- /* Determine number of LMACs */
- rx_lmacs = readq ( bgx->regs + BGX_CMR_RX_LMACS );
- bgx->count = BGX_CMR_RX_LMACS_LMACS_GET ( rx_lmacs );
- if ( ( bgx->count == TXNIC_NUM_LMAC ) &&
- ( bgx->type->count != TXNIC_NUM_LMAC ) ) {
- DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* assuming %d LMACs\n",
- bgx->node, bgx->idx, bgx->type->count );
- bgx->count = bgx->type->count;
- }
-
- return type;
- }
-
- /**
- * Initialise BGX Ethernet interface
- *
- * @v bgx BGX Ethernet interface
- * @v type LMAC type
- */
- static void txnic_bgx_init ( struct txnic_bgx *bgx, unsigned int type ) {
- uint64_t global_config;
- uint32_t lane_to_sds;
- unsigned int i;
-
- /* Set number of LMACs */
- writeq ( BGX_CMR_RX_LMACS_LMACS_SET ( bgx->count ),
- ( bgx->regs + BGX_CMR_RX_LMACS ) );
- writeq ( BGX_CMR_TX_LMACS_LMACS_SET ( bgx->count ),
- ( bgx->regs + BGX_CMR_TX_LMACS ) );
-
- /* Set LMAC types and lane mappings, and disable all LMACs */
- lane_to_sds = bgx->type->lane_to_sds;
- for ( i = 0 ; i < bgx->count ; i++ ) {
- writeq ( ( BGX_CMR_CONFIG_LMAC_TYPE_SET ( type ) |
- BGX_CMR_CONFIG_LANE_TO_SDS ( lane_to_sds ) ),
- ( bgx->regs + BGX_LMAC ( i ) + BGX_CMR_CONFIG ) );
- lane_to_sds >>= 8;
- }
-
- /* Reset all MAC address filtering */
- for ( i = 0 ; i < TXNIC_NUM_DMAC ; i++ )
- writeq ( 0, ( bgx->regs + BGX_CMR_RX_DMAC_CAM ( i ) ) );
-
- /* Reset NCSI steering */
- for ( i = 0 ; i < TXNIC_NUM_STEERING ; i++ )
- writeq ( 0, ( bgx->regs + BGX_CMR_RX_STEERING ( i ) ) );
-
- /* Enable backpressure to all channels */
- writeq ( BGX_CMR_CHAN_MSK_AND_ALL ( bgx->count ),
- ( bgx->regs + BGX_CMR_CHAN_MSK_AND ) );
-
- /* Strip FCS */
- global_config = readq ( bgx->regs + BGX_CMR_GLOBAL_CONFIG );
- global_config |= BGX_CMR_GLOBAL_CONFIG_FCS_STRIP;
- writeq ( global_config, ( bgx->regs + BGX_CMR_GLOBAL_CONFIG ) );
- }
-
- /**
- * Get MAC address
- *
- * @v lmac Logical MAC
- */
- static void txnic_bgx_mac ( struct txnic_lmac *lmac ) {
- struct txnic_bgx *bgx = lmac->bgx;
- unsigned int lmac_idx = TXNIC_LMAC_IDX ( lmac->idx );
- uint64_t mac;
- EFI_STATUS efirc;
- int rc;
-
- /* Extract MAC from Board Configuration protocol, if available */
- if ( txcfg ) {
- if ( ( efirc = txcfg->GetLmacProp ( txcfg, bgx->node, bgx->idx,
- lmac_idx, MAC_ADDRESS,
- sizeof ( mac ),
- &mac ) ) == 0 ) {
- lmac->mac.be64 = cpu_to_be64 ( mac );
- } else {
- rc = -EEFI ( efirc );
- DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d could not get "
- "MAC address: %s\n", bgx->node, bgx->idx,
- lmac->idx, strerror ( rc ) );
- }
- } else {
- DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d has no board "
- "configuration protocol\n", bgx->node, bgx->idx,
- lmac->idx );
- }
-
- /* Use random MAC address if none available */
- if ( ! lmac->mac.be64 ) {
- DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/%d has no MAC address\n",
- bgx->node, bgx->idx, lmac->idx );
- eth_random_addr ( lmac->mac.raw );
- }
- }
-
- /**
- * Initialise Super PHY Unit (SPU)
- *
- * @v lmac Logical MAC
- */
- static void txnic_bgx_spu_init ( struct txnic_lmac *lmac ) {
- struct txnic_bgx *bgx = lmac->bgx;
-
- /* Reset PHY */
- writeq ( BGX_SPU_CONTROL1_RESET, ( lmac->regs + BGX_SPU_CONTROL1 ) );
- mdelay ( BGX_SPU_RESET_DELAY_MS );
-
- /* Power down PHY */
- writeq ( BGX_SPU_CONTROL1_LO_PWR, ( lmac->regs + BGX_SPU_CONTROL1 ) );
-
- /* Configure training, if applicable */
- if ( bgx->training ) {
- writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LP_CUP ) );
- writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LD_CUP ) );
- writeq ( 0, ( lmac->regs + BGX_SPU_BR_PMD_LD_REP ) );
- writeq ( BGX_SPU_BR_PMD_CONTROL_TRAIN_EN,
- ( lmac->regs + BGX_SPU_BR_PMD_CONTROL ) );
- }
-
- /* Disable forward error correction */
- writeq ( 0, ( lmac->regs + BGX_SPU_FEC_CONTROL ) );
-
- /* Disable autonegotiation */
- writeq ( 0, ( lmac->regs + BGX_SPU_AN_CONTROL ) );
-
- /* Power up PHY */
- writeq ( 0, ( lmac->regs + BGX_SPU_CONTROL1 ) );
- }
-
- /**
- * Initialise LMAC
- *
- * @v bgx BGX Ethernet interface
- * @v lmac_idx LMAC index
- */
- static void txnic_bgx_lmac_init ( struct txnic_bgx *bgx,
- unsigned int lmac_idx ) {
- struct txnic_lmac *lmac = &bgx->lmac[lmac_idx];
- uint64_t config;
-
- /* Record associated BGX */
- lmac->bgx = bgx;
-
- /* Set register base address (already mapped) */
- lmac->regs = ( bgx->regs + BGX_LMAC ( lmac_idx ) );
-
- /* Calculate virtual NIC index */
- lmac->idx = TXNIC_VNIC_IDX ( bgx->idx, lmac_idx );
-
- /* Set MAC address */
- txnic_bgx_mac ( lmac );
-
- /* Initialise PHY */
- txnic_bgx_spu_init ( lmac );
-
- /* Accept all multicasts and broadcasts */
- writeq ( ( BGX_CMR_RX_DMAC_CTL_MCST_MODE_ACCEPT |
- BGX_CMR_RX_DMAC_CTL_BCST_ACCEPT ),
- ( lmac->regs + BGX_CMR_RX_DMAC_CTL ) );
-
- /* Enable LMAC */
- config = readq ( lmac->regs + BGX_CMR_CONFIG );
- config |= ( BGX_CMR_CONFIG_ENABLE |
- BGX_CMR_CONFIG_DATA_PKT_RX_EN |
- BGX_CMR_CONFIG_DATA_PKT_TX_EN );
- writeq ( config, ( lmac->regs + BGX_CMR_CONFIG ) );
- }
-
- /**
- * Probe PCI device
- *
- * @v pci PCI device
- * @ret rc Return status code
- */
- static int txnic_bgx_probe ( struct pci_device *pci ) {
- struct txnic_bgx *bgx;
- struct txnic_pf *pf;
- unsigned long membase;
- unsigned int i;
- int type;
- int rc;
-
- /* Allocate and initialise structure */
- bgx = zalloc ( sizeof ( *bgx ) );
- if ( ! bgx ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- bgx->pci = pci;
- pci_set_drvdata ( pci, bgx );
-
- /* Get base address */
- membase = pciea_bar_start ( pci, PCIEA_BEI_BAR_0 );
-
- /* Calculate node ID and index */
- bgx->node = txnic_address_node ( membase );
- bgx->idx = txnic_address_bgx ( membase );
-
- /* Fix up PCI device */
- adjust_pci_device ( pci );
-
- /* Map registers */
- bgx->regs = ioremap ( membase, TXNIC_BGX_BAR_SIZE );
- if ( ! bgx->regs ) {
- rc = -ENODEV;
- goto err_ioremap;
- }
-
- /* Detect LMAC type */
- if ( ( type = txnic_bgx_detect ( bgx ) ) < 0 ) {
- rc = type;
- goto err_detect;
- }
- DBGC ( TXNICCOL ( bgx ), "TXNIC %d/%d/* BGX %s at %#lx %dx %s%s\n",
- bgx->node, bgx->idx, pci->dev.name, membase, bgx->count,
- bgx->type->name, ( bgx->training ? "(training)" : "" ) );
-
- /* Initialise interface */
- txnic_bgx_init ( bgx, type );
-
- /* Initialise all LMACs */
- for ( i = 0 ; i < bgx->count ; i++ )
- txnic_bgx_lmac_init ( bgx, i );
-
- /* Add to list of BGX devices */
- list_add_tail ( &bgx->list, &txnic_bgxs );
-
- /* Probe all LMACs, if applicable */
- list_for_each_entry ( pf, &txnic_pfs, list ) {
- if ( pf->node != bgx->node )
- continue;
- if ( ( rc = txnic_lmac_probe_all ( pf, bgx ) ) != 0 )
- goto err_probe;
- }
-
- return 0;
-
- if ( bgx->pf )
- txnic_lmac_remove_all ( bgx->pf, bgx );
- list_del ( &bgx->list );
- err_probe:
- err_detect:
- iounmap ( bgx->regs );
- err_ioremap:
- free ( bgx );
- err_alloc:
- return rc;
- }
-
- /**
- * Remove PCI device
- *
- * @v pci PCI device
- */
- static void txnic_bgx_remove ( struct pci_device *pci ) {
- struct txnic_bgx *bgx = pci_get_drvdata ( pci );
-
- /* Remove all LMACs, if applicable */
- if ( bgx->pf )
- txnic_lmac_remove_all ( bgx->pf, bgx );
-
- /* Remove from list of BGX devices */
- list_del ( &bgx->list );
-
- /* Unmap registers */
- iounmap ( bgx->regs );
-
- /* Free BGX device */
- free ( bgx );
- }
-
- /** BGX PCI device IDs */
- static struct pci_device_id txnic_bgx_ids[] = {
- PCI_ROM ( 0x177d, 0xa026, "thunder-bgx", "ThunderX BGX", 0 ),
- };
-
- /** BGX PCI driver */
- struct pci_driver txnic_bgx_driver __pci_driver = {
- .ids = txnic_bgx_ids,
- .id_count = ( sizeof ( txnic_bgx_ids ) / sizeof ( txnic_bgx_ids[0] ) ),
- .probe = txnic_bgx_probe,
- .remove = txnic_bgx_remove,
- };
|