[cgem] Add a driver for the Cadence GEM NIC

Add a basic driver for the Cadence GEM network interface as emulated
by QEMU when using the RISC-V "sifive_u" machine type.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-04-19 11:54:08 +01:00
parent 0c482060d5
commit 1291dc39fd
4 changed files with 904 additions and 0 deletions

711
src/drivers/net/cgem.c Normal file
View File

@@ -0,0 +1,711 @@
/*
* Copyright (C) 2025 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 <unistd.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
#include <ipxe/iobuf.h>
#include <ipxe/timer.h>
#include <ipxe/devtree.h>
#include <ipxe/fdt.h>
#include "cgem.h"
/** @file
*
* Cadence Gigabit Ethernet MAC (GEM) network driver
*
* Based primarily on the Zynq 7000 SoC Technical Reference Manual,
* available at the time of writing from:
*
* https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM
*
*/
/******************************************************************************
*
* Device reset
*
******************************************************************************
*/
/**
* Reset hardware
*
* @v cgem Cadence GEM device
* @ret rc Return status code
*/
static int cgem_reset ( struct cgem_nic *cgem ) {
/* There is no software-driven reset capability in the
* hardware. Instead we have to write the expected reset
* values to the various registers.
*/
/* Disable all interrupts */
writel ( CGEM_IDR_ALL, ( cgem->regs + CGEM_IDR ) );
/* Clear network control register */
writel ( 0, ( cgem->regs + CGEM_NWCTRL ) );
/* Clear statistics registers now that TX and RX are stopped */
writel ( CGEM_NWCTRL_STATCLR, ( cgem->regs + CGEM_NWCTRL ) );
/* Clear TX queue base address */
writel ( 0, ( cgem->regs + CGEM_TXQBASE ) );
/* Clear RX queue base address */
writel ( 0, ( cgem->regs + CGEM_RXQBASE ) );
/* Configure DMA */
writel ( ( CGEM_DMACR_RXBUF ( CGEM_RX_LEN ) | CGEM_DMACR_TXSIZE_MAX |
CGEM_DMACR_RXSIZE_MAX | CGEM_DMACR_BLENGTH_MAX ),
( cgem->regs + CGEM_DMACR ) );
/* Enable MII interface */
writel ( CGEM_NWCTRL_MDEN, ( cgem->regs + CGEM_NWCTRL ) );
return 0;
}
/******************************************************************************
*
* PHY access
*
******************************************************************************
*/
/**
* Wait for MII operation to complete
*
* @v cgem Cadence GEM device
* @ret rc Return status code
*/
static int cgem_mii_wait ( struct cgem_nic *cgem ) {
uint32_t nwsr;
unsigned int i;
/* Wait for MII interface to become idle */
for ( i = 0 ; i < CGEM_MII_MAX_WAIT_US ; i++ ) {
/* Check if MII interface is idle */
nwsr = readl ( cgem->regs + CGEM_NWSR );
if ( nwsr & CGEM_NWSR_MII_IDLE )
return 0;
/* Delay */
udelay ( 1 );
}
DBGC ( cgem, "CGEM %s timed out waiting for MII\n", cgem->name );
return -ETIMEDOUT;
}
/**
* Read from MII register
*
* @v mdio MII interface
* @v phy PHY address
* @v reg Register address
* @ret data Data read, or negative error
*/
static int cgem_mii_read ( struct mii_interface *mdio, unsigned int phy,
unsigned int reg ) {
struct cgem_nic *cgem = container_of ( mdio, struct cgem_nic, mdio );
unsigned int data;
int rc;
/* Initiate read */
writel ( ( CGEM_PHYMNTNC_CLAUSE22 | CGEM_PHYMNTNC_OP_READ |
CGEM_PHYMNTNC_ADDR ( phy ) | CGEM_PHYMNTNC_REG ( reg ) |
CGEM_PHYMNTNC_FIXED ),
( cgem->regs + CGEM_PHYMNTNC ) );
/* Wait for read to complete */
if ( ( rc = cgem_mii_wait ( cgem ) ) != 0 )
return rc;
/* Read data */
data = ( readl ( cgem->regs + CGEM_PHYMNTNC ) &
CGEM_PHYMNTNC_DATA_MASK );
return data;
}
/**
* Write to MII register
*
* @v mdio MII interface
* @v phy PHY address
* @v reg Register address
* @v data Data to write
* @ret rc Return status code
*/
static int cgem_mii_write ( struct mii_interface *mdio, unsigned int phy,
unsigned int reg, unsigned int data ) {
struct cgem_nic *cgem = container_of ( mdio, struct cgem_nic, mdio );
int rc;
/* Initiate write */
writel ( ( CGEM_PHYMNTNC_CLAUSE22 | CGEM_PHYMNTNC_OP_READ |
CGEM_PHYMNTNC_ADDR ( phy ) | CGEM_PHYMNTNC_REG ( reg ) |
CGEM_PHYMNTNC_FIXED | data ),
( cgem->regs + CGEM_PHYMNTNC ) );
/* Wait for write to complete */
if ( ( rc = cgem_mii_wait ( cgem ) ) != 0 )
return rc;
return 0;
}
/** MII operations */
static struct mii_operations cgem_mii_operations = {
.read = cgem_mii_read,
.write = cgem_mii_write,
};
/******************************************************************************
*
* Link state
*
******************************************************************************
*/
/**
* Initialise PHY
*
* @v cgem Cadence GEM device
* @ret rc Return status code
*/
static int cgem_init_phy ( struct cgem_nic *cgem ) {
int rc;
/* Find PHY address */
if ( ( rc = mii_find ( &cgem->mii ) ) != 0 ) {
DBGC ( cgem, "CGEM %s could not find PHY address: %s\n",
cgem->name, strerror ( rc ) );
return rc;
}
/* Reset PHY */
if ( ( rc = mii_reset ( &cgem->mii ) ) != 0 ) {
DBGC ( cgem, "CGEM %s could not reset PHY: %s\n",
cgem->name, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Check link state
*
* @v netdev Network device
*/
static int cgem_check_link ( struct net_device *netdev ) {
struct cgem_nic *cgem = netdev->priv;
int rc;
/* Check link state */
if ( ( rc = mii_check_link ( &cgem->mii, netdev ) ) != 0 ) {
DBGC ( cgem, "CGEM %s could not check link: %s\n",
cgem->name, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Check link state periodically
*
* @v retry Link state check timer
* @v over Failure indicator
*/
static void cgem_expired ( struct retry_timer *timer, int over __unused ) {
struct cgem_nic *cgem = container_of ( timer, struct cgem_nic, timer );
struct net_device *netdev = cgem->netdev;
/* Restart timer */
start_timer_fixed ( timer, CGEM_LINK_INTERVAL );
/* Check link state */
cgem_check_link ( netdev );
}
/******************************************************************************
*
* Network device interface
*
******************************************************************************
*/
/**
* Create descriptor ring
*
* @v cgem Cadence GEM device
* @v ring Descriptor ring
* @ret rc Return status code
*/
static int cgem_create_ring ( struct cgem_nic *cgem, struct cgem_ring *ring ) {
struct cgem_descriptor *desc;
unsigned int i;
/* Allocate descriptor ring (on its own size) */
ring->desc = dma_alloc ( cgem->dma, &ring->map, ring->len, ring->len );
if ( ! ring->desc )
return -ENOMEM;
/* Initialise descriptor ring */
for ( i = 0 ; i < ring->count ; i++ ) {
desc = &ring->desc[i];
desc->addr = cpu_to_le32 ( CGEM_RX_ADDR_OWNED );
desc->flags = cpu_to_le32 ( CGEM_TX_FL_OWNED );
}
desc = &ring->desc[ ring->count - 1 ];
desc->addr |= cpu_to_le32 ( CGEM_RX_ADDR_WRAP );
desc->flags |= cpu_to_le32 ( CGEM_TX_FL_WRAP );
/* Program ring address */
writel ( dma ( &ring->map, ring->desc ),
( cgem->regs + ring->qbase ) );
DBGC ( cgem, "CGEM %s ring %02x is at [%08lx,%08lx)\n",
cgem->name, ring->qbase, virt_to_phys ( ring->desc ),
( virt_to_phys ( ring->desc ) + ring->len ) );
return 0;
}
/**
* Destroy descriptor ring
*
* @v cgem Cadence GEM device
* @v ring Descriptor ring
*/
static void cgem_destroy_ring ( struct cgem_nic *cgem,
struct cgem_ring *ring ) {
/* Clear ring address */
writel ( 0, ( cgem->regs + ring->qbase ) );
/* Free descriptor ring */
dma_free ( &ring->map, ring->desc, ring->len );
ring->desc = NULL;
ring->prod = 0;
ring->cons = 0;
}
/**
* Refill receive descriptor ring
*
* @v cgem Cadence GEM device
*/
static void cgem_refill_rx ( struct cgem_nic *cgem ) {
struct cgem_descriptor *rx;
struct io_buffer *iobuf;
unsigned int rx_idx;
uint32_t addr;
/* Refill ring */
while ( ( cgem->rx.prod - cgem->rx.cons ) != CGEM_NUM_RX_DESC ) {
/* Allocate I/O buffer */
iobuf = alloc_rx_iob ( CGEM_RX_LEN, cgem->dma );
if ( ! iobuf ) {
/* Wait for next refill */
break;
}
/* Get next receive descriptor */
rx_idx = ( cgem->rx.prod++ % CGEM_NUM_RX_DESC );
rx = &cgem->rx.desc[rx_idx];
/* Populate receive descriptor */
rx->flags = 0;
addr = 0;
if ( ( cgem->rx.prod % CGEM_NUM_RX_DESC ) == 0 )
addr |= CGEM_RX_ADDR_WRAP;
rx->addr = cpu_to_le32 ( addr | iob_dma ( iobuf ) );
/* Record I/O buffer */
assert ( cgem->rx_iobuf[rx_idx] == NULL );
cgem->rx_iobuf[rx_idx] = iobuf;
DBGC2 ( cgem, "CGEM %s RX %d is [%08lx,%08lx)\n",
cgem->name, rx_idx, virt_to_phys ( iobuf->data ),
( virt_to_phys ( iobuf->data ) + CGEM_RX_LEN ) );
}
}
/**
* Open network device
*
* @v netdev Network device
* @ret rc Return status code
*/
static int cgem_open ( struct net_device *netdev ) {
struct cgem_nic *cgem = netdev->priv;
union cgem_mac mac;
int rc;
/* Create transmit descriptor ring */
if ( ( rc = cgem_create_ring ( cgem, &cgem->tx ) ) != 0 )
goto err_create_tx;
/* Create receive descriptor ring */
if ( ( rc = cgem_create_ring ( cgem, &cgem->rx ) ) != 0 )
goto err_create_rx;
/* Set MAC address */
memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN );
writel ( mac.reg.low, ( cgem->regs + CGEM_LADDRL ) );
writel ( mac.reg.high, ( cgem->regs + CGEM_LADDRH ) );
/* Enable transmit and receive */
writel ( CGEM_NWCTRL_NORMAL, ( cgem->regs + CGEM_NWCTRL ) );
/* Refill receive descriptor ring */
cgem_refill_rx ( cgem );
/* Update link state */
cgem_check_link ( netdev );
/* Start link state timer */
start_timer_fixed ( &cgem->timer, CGEM_LINK_INTERVAL );
return 0;
cgem_destroy_ring ( cgem, &cgem->rx );
err_create_rx:
cgem_destroy_ring ( cgem, &cgem->tx );
err_create_tx:
return rc;
}
/**
* Close network device
*
* @v netdev Network device
*/
static void cgem_close ( struct net_device *netdev ) {
struct cgem_nic *cgem = netdev->priv;
unsigned int i;
/* Stop link state timer */
stop_timer ( &cgem->timer );
/* Reset NIC */
cgem_reset ( cgem );
/* Discard unused receive buffers */
for ( i = 0 ; i < CGEM_NUM_RX_DESC ; i++ ) {
if ( cgem->rx_iobuf[i] )
free_rx_iob ( cgem->rx_iobuf[i] );
cgem->rx_iobuf[i] = NULL;
}
/* Destroy receive descriptor ring */
cgem_destroy_ring ( cgem, &cgem->rx );
/* Destroy transmit descriptor ring */
cgem_destroy_ring ( cgem, &cgem->tx );
}
/**
* Transmit packet
*
* @v netdev Network device
* @v iobuf I/O buffer
* @ret rc Return status code
*/
static int cgem_transmit ( struct net_device *netdev,
struct io_buffer *iobuf ) {
struct cgem_nic *cgem = netdev->priv;
struct cgem_descriptor *tx;
unsigned int tx_idx;
uint32_t flags;
int rc;
/* Get next transmit descriptor */
if ( ( cgem->tx.prod - cgem->tx.cons ) >= CGEM_NUM_TX_DESC ) {
DBGC ( cgem, "CGEM %s out of transmit descriptors\n",
cgem->name );
return -ENOBUFS;
}
tx_idx = ( cgem->tx.prod % CGEM_NUM_TX_DESC );
tx = &cgem->tx.desc[tx_idx];
/* Pad to minimum length */
iob_pad ( iobuf, ETH_ZLEN );
/* Map I/O buffer */
if ( ( rc = iob_map_tx ( iobuf, cgem->dma ) ) != 0 )
return rc;
/* Update producer index */
cgem->tx.prod++;
/* Populate transmit descriptor */
flags = CGEM_TX_FL_LAST;
if ( ( cgem->tx.prod % CGEM_NUM_TX_DESC ) == 0 )
flags |= CGEM_TX_FL_WRAP;
tx->addr = cpu_to_le32 ( iob_dma ( iobuf ) );
wmb();
tx->flags = cpu_to_le32 ( flags | iob_len ( iobuf ) );
wmb();
/* Initiate transmission */
writel ( ( CGEM_NWCTRL_NORMAL | CGEM_NWCTRL_STARTTX ),
( cgem->regs + CGEM_NWCTRL ) );
DBGC2 ( cgem, "CGEM %s TX %d is [%08lx,%08lx)\n",
cgem->name, tx_idx, virt_to_phys ( iobuf->data ),
( virt_to_phys ( iobuf->data ) + iob_len ( iobuf ) ) );
return 0;
}
/**
* Poll for completed packets
*
* @V netdev Network device
*/
static void cgem_poll_tx ( struct net_device *netdev ) {
struct cgem_nic *cgem = netdev->priv;
struct cgem_descriptor *tx;
unsigned int tx_idx;
/* Check for completed packets */
while ( cgem->tx.cons != cgem->tx.prod ) {
/* Get next transmit descriptor */
tx_idx = ( cgem->tx.cons % CGEM_NUM_TX_DESC );
tx = &cgem->tx.desc[tx_idx];
/* Stop if descriptor is still owned by hardware */
if ( ! ( tx->flags & cpu_to_le32 ( CGEM_TX_FL_OWNED ) ) )
return;
DBGC2 ( cgem, "CGEM %s TX %d complete\n",
cgem->name, tx_idx );
/* Complete transmit descriptor */
netdev_tx_complete_next ( netdev );
cgem->tx.cons++;
}
}
/**
* Poll for received packets
*
* @v netdev Network device
*/
static void cgem_poll_rx ( struct net_device *netdev ) {
struct cgem_nic *cgem = netdev->priv;
struct cgem_descriptor *rx;
struct io_buffer *iobuf;
unsigned int rx_idx;
uint32_t flags;
size_t len;
/* Check for received packets */
while ( cgem->rx.cons != cgem->rx.prod ) {
/* Get next receive descriptor */
rx_idx = ( cgem->rx.cons % CGEM_NUM_RX_DESC );
rx = &cgem->rx.desc[rx_idx];
/* Stop if descriptor is still in use */
if ( ! ( rx->addr & cpu_to_le32 ( CGEM_RX_ADDR_OWNED ) ) )
return;
/* Populate I/O buffer */
iobuf = cgem->rx_iobuf[rx_idx];
cgem->rx_iobuf[rx_idx] = NULL;
flags = le32_to_cpu ( rx->flags );
len = CGEM_RX_FL_LEN ( flags );
iob_put ( iobuf, len );
DBGC2 ( cgem, "CGEM %s RX %d complete (length %zd)\n",
cgem->name, rx_idx, len );
/* Hand off to network stack */
netdev_rx ( netdev, iobuf );
cgem->rx.cons++;
}
}
/**
* Poll for completed and received packets
*
* @v netdev Network device
*/
static void cgem_poll ( struct net_device *netdev ) {
struct cgem_nic *cgem = netdev->priv;
/* Poll for TX competions */
cgem_poll_tx ( netdev );
/* Poll for RX completions */
cgem_poll_rx ( netdev );
/* Refill RX ring */
cgem_refill_rx ( cgem );
}
/** Cadence GEM network device operations */
static struct net_device_operations cgem_operations = {
.open = cgem_open,
.close = cgem_close,
.transmit = cgem_transmit,
.poll = cgem_poll,
};
/******************************************************************************
*
* Devicetree interface
*
******************************************************************************
*/
/**
* Probe devicetree device
*
* @v dt Devicetree device
* @v offset Starting node offset
* @ret rc Return status code
*/
static int cgem_probe ( struct dt_device *dt, unsigned int offset ) {
struct net_device *netdev;
struct cgem_nic *cgem;
union cgem_mac mac;
int rc;
/* Allocate and initialise net device */
netdev = alloc_etherdev ( sizeof ( *cgem ) );
if ( ! netdev ) {
rc = -ENOMEM;
goto err_alloc;
}
netdev_init ( netdev, &cgem_operations );
cgem = netdev->priv;
dt_set_drvdata ( dt, netdev );
netdev->dev = &dt->dev;
memset ( cgem, 0, sizeof ( *cgem ) );
cgem->dma = &dt->dma;
cgem->netdev = netdev;
cgem->name = netdev->dev->name;
mdio_init ( &cgem->mdio, &cgem_mii_operations );
mii_init ( &cgem->mii, &cgem->mdio, 0 );
timer_init ( &cgem->timer, cgem_expired, &netdev->refcnt );
cgem_init_ring ( &cgem->tx, CGEM_NUM_TX_DESC, CGEM_TXQBASE );
cgem_init_ring ( &cgem->rx, CGEM_NUM_RX_DESC, CGEM_RXQBASE );
/* Map registers */
cgem->regs = dt_ioremap ( dt, offset, CGEM_REG_IDX, CGEM_REG_LEN );
if ( ! cgem->regs ) {
rc = -ENODEV;
goto err_ioremap;
}
/* Reset the NIC */
if ( ( rc = cgem_reset ( cgem ) ) != 0 )
goto err_reset;
/* Initialise the PHY */
if ( ( rc = cgem_init_phy ( cgem ) ) != 0 )
goto err_init_phy;
/* Fetch devicetree MAC address */
if ( ( rc = fdt_mac ( &sysfdt, offset, netdev ) ) != 0 ) {
DBGC ( cgem, "CGEM %s could not fetch MAC: %s\n",
cgem->name, strerror ( rc ) );
goto err_mac;
}
/* Fetch current MAC address, if set */
mac.reg.low = readl ( cgem->regs + CGEM_LADDRL );
mac.reg.high = readl ( cgem->regs + CGEM_LADDRH );
memcpy ( netdev->ll_addr, mac.raw, ETH_ALEN );
/* Register network device */
if ( ( rc = register_netdev ( netdev ) ) != 0 )
goto err_register_netdev;
/* Set initial link state */
cgem_check_link ( netdev );
return 0;
unregister_netdev ( netdev );
err_register_netdev:
err_mac:
err_init_phy:
cgem_reset ( cgem );
err_reset:
iounmap ( cgem->regs );
err_ioremap:
netdev_nullify ( netdev );
netdev_put ( netdev );
err_alloc:
return rc;
}
/**
* Remove devicetree device
*
* @v dt Devicetree device
*/
static void cgem_remove ( struct dt_device *dt ) {
struct net_device *netdev = dt_get_drvdata ( dt );
struct cgem_nic *cgem = netdev->priv;
/* Unregister network device */
unregister_netdev ( netdev );
/* Reset card */
cgem_reset ( cgem );
/* Free network device */
iounmap ( cgem->regs );
netdev_nullify ( netdev );
netdev_put ( netdev );
}
/** Cadence GEM compatible model identifiers */
static const char * cgem_ids[] = {
"sifive,fu540-c000-gem",
};
/** Cadence GEM devicetree driver */
struct dt_driver cgem_driver __dt_driver = {
.name = "cgem",
.ids = cgem_ids,
.id_count = ( sizeof ( cgem_ids ) / sizeof ( cgem_ids[0] ) ),
.probe = cgem_probe,
.remove = cgem_remove,
};

189
src/drivers/net/cgem.h Normal file
View File

@@ -0,0 +1,189 @@
#ifndef _CGEM_H
#define _CGEM_H
/** @file
*
* Cadence Gigabit Ethernet MAC (GEM) network driver
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/if_ether.h>
#include <ipxe/mii.h>
#include <ipxe/dma.h>
#include <ipxe/retry.h>
/** I/O region index */
#define CGEM_REG_IDX 0
/** I/O region length */
#define CGEM_REG_LEN 0x800
/** Network control register */
#define CGEM_NWCTRL 0x000
#define CGEM_NWCTRL_STARTTX 0x00000200 /**< Start transmission */
#define CGEM_NWCTRL_STATCLR 0x00000020 /**< Clear statistics */
#define CGEM_NWCTRL_MDEN 0x00000010 /**< MII interface enable */
#define CGEM_NWCTRL_TXEN 0x00000008 /**< Transmit enable */
#define CGEM_NWCTRL_RXEN 0x00000004 /**< Receive enable */
/** Normal value for network control register while up and running */
#define CGEM_NWCTRL_NORMAL \
( CGEM_NWCTRL_MDEN | CGEM_NWCTRL_TXEN | CGEM_NWCTRL_RXEN )
/** Network configuration register */
#define CGEM_NWCFG 0x004
/** Network status register */
#define CGEM_NWSR 0x008
#define CGEM_NWSR_MII_IDLE 0x00000004 /**< MII interface is idle */
/** DMA configuration register */
#define CGEM_DMACR 0x010
#define CGEM_DMACR_RXBUF( x ) ( ( (x) / 64 ) << 16 ) /**< RX buffer size */
#define CGEM_DMACR_TXSIZE( x ) ( (x) << 10 ) /**< TX memory size */
#define CGEM_DMACR_TXSIZE_MAX \
CGEM_DMACR_TXSIZE ( 0x1 ) /**< Max TX memory size */
#define CGEM_DMACR_RXSIZE( x ) ( (x) << 8 ) /**< RX memory size */
#define CGEM_DMACR_RXSIZE_MAX \
CGEM_DMACR_RXSIZE ( 0x3 ) /**< Max RX memory size */
#define CGEM_DMACR_BLENGTH( x ) ( (x) << 0 ) /**< DMA burst length */
#define CGEM_DMACR_BLENGTH_MAX \
CGEM_DMACR_BLENGTH ( 0x10 ) /**< Max DMA burst length */
/** RX queue base address register */
#define CGEM_RXQBASE 0x018
/** TX queue base address register */
#define CGEM_TXQBASE 0x01c
/** Interrupt disable register */
#define CGEM_IDR 0x02c
#define CGEM_IDR_ALL 0xffffffff /**< Disable all interrupts */
/** PHY maintenance register */
#define CGEM_PHYMNTNC 0x034
#define CGEM_PHYMNTNC_CLAUSE22 0x40000000 /**< Clause 22 operation */
#define CGEM_PHYMNTNC_OP_WRITE 0x10000000 /**< Write to PHY register */
#define CGEM_PHYMNTNC_OP_READ 0x20000000 /**< Read from PHY register */
#define CGEM_PHYMNTNC_ADDR( x ) ( (x) << 23 ) /**< PHY address */
#define CGEM_PHYMNTNC_REG( x ) ( (x) << 18 ) /**< Register address */
#define CGEM_PHYMNTNC_FIXED 0x00020000 /**< Fixed value to write */
#define CGEM_PHYMNTNC_DATA_MASK 0x0000ffff /**< Data mask */
/** Maximum time to wait for PHY access, in microseconds */
#define CGEM_MII_MAX_WAIT_US 500
/** Link state check interval */
#define CGEM_LINK_INTERVAL ( 2 * TICKS_PER_SEC )
/** Local MAC address (low half) register */
#define CGEM_LADDRL 0x088
/** Local MAC address (high half) register */
#define CGEM_LADDRH 0x08c
/** A Cadence GEM descriptor */
struct cgem_descriptor {
/** Buffer address */
uint32_t addr;
/** Flags */
uint32_t flags;
} __attribute__ (( packed ));
/** Transmit flags */
#define CGEM_TX_FL_OWNED 0x80000000 /**< Owned by software */
#define CGEM_TX_FL_WRAP 0x40000000 /**< End of descriptor ring */
#define CGEM_TX_FL_LAST 0x00008000 /**< Last buffer in frame */
/** Transmit ring length */
#define CGEM_NUM_TX_DESC 8
/** Receive flags (in buffer address) */
#define CGEM_RX_ADDR_OWNED 0x00000001 /**< Owned by software */
#define CGEM_RX_ADDR_WRAP 0x00000002 /**< End of descriptor ring */
/** Receive flags */
#define CGEM_RX_FL_LEN( x ) ( (x) & 0x1fff ) /**< RX packet length */
/** Receive ring length */
#define CGEM_NUM_RX_DESC 8
/** Length of receive buffers
*
* Must be a multiple of 64.
*/
#define CGEM_RX_LEN 1536
/** A Cadence GEM MAC address */
union cgem_mac {
struct {
uint32_t low;
uint32_t high;
} __attribute__ (( packed )) reg;
uint8_t raw[ETH_ALEN];
};
/** A Cadence GEM descriptor ring */
struct cgem_ring {
/** Descriptors */
struct cgem_descriptor *desc;
/** Descriptor ring DMA mapping */
struct dma_mapping map;
/** Producer index */
unsigned int prod;
/** Consumer index */
unsigned int cons;
/** Queue base address register */
uint8_t qbase;
/** Number of descriptors */
uint8_t count;
/** Length of descriptors */
uint16_t len;
};
/**
* Initialise descriptor ring
*
* @v ring Descriptor ring
* @v count Number of descriptors
* @v qbase Queue base address register
*/
static inline __attribute__ (( always_inline )) void
cgem_init_ring ( struct cgem_ring *ring, unsigned int count,
unsigned int qbase ) {
ring->qbase = qbase;
ring->count = count;
ring->len = ( count * sizeof ( ring->desc[0] ) );
}
/** A Cadence GEM network card */
struct cgem_nic {
/** Registers */
void *regs;
/** DMA device */
struct dma_device *dma;
/** Network device */
struct net_device *netdev;
/** Device name (for debugging) */
const char *name;
/** PHY interface */
struct mii_interface mdio;
/** PHY device */
struct mii_device mii;
/** Link state timer */
struct retry_timer timer;
/** Transmit ring */
struct cgem_ring tx;
/** Receive ring */
struct cgem_ring rx;
/** Receive I/O buffers */
struct io_buffer *rx_iobuf[CGEM_NUM_RX_DESC];
};
#endif /* _CGEM_H */

View File

@@ -10,11 +10,14 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/device.h>
#include <ipxe/dma.h>
/** A devicetree device */
struct dt_device {
/** Generic device */
struct device dev;
/** DMA device */
struct dma_device dma;
/** Device path */
const char *path;
/** Driver for this device */

View File

@@ -232,6 +232,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_atl_hw ( ERRFILE_DRIVER | 0x00d80000 )
#define ERRFILE_atl2_hw ( ERRFILE_DRIVER | 0x00d90000 )
#define ERRFILE_devtree ( ERRFILE_DRIVER | 0x00da0000 )
#define ERRFILE_cgem ( ERRFILE_DRIVER | 0x00db0000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )