2195 lines
56 KiB
C
2195 lines
56 KiB
C
/* $OpenBSD: if_nxe.c,v 1.80 2023/11/10 15:51:20 bluhm Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "bpfilter.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/device.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/timeout.h>
|
|
#include <sys/sensors.h>
|
|
#include <sys/rwlock.h>
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#endif
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
|
|
#ifdef NXE_DEBUG
|
|
int nxedebug = 0;
|
|
|
|
#define DPRINTF(l, f...) do { if (nxedebug & (l)) printf(f); } while (0)
|
|
#define DASSERT(_a) assert(_a)
|
|
#else
|
|
#define DPRINTF(l, f...)
|
|
#define DASSERT(_a)
|
|
#endif
|
|
|
|
/* this driver likes firmwares around this version */
|
|
#define NXE_VERSION_MAJOR 3
|
|
#define NXE_VERSION_MINOR 4
|
|
#define NXE_VERSION_BUILD 31
|
|
#define NXE_VERSION \
|
|
((NXE_VERSION_MAJOR << 16)|(NXE_VERSION_MINOR << 8)|(NXE_VERSION_BUILD))
|
|
|
|
|
|
/*
|
|
* PCI configuration space registers
|
|
*/
|
|
|
|
#define NXE_PCI_BAR_MEM 0x10 /* bar 0 */
|
|
#define NXE_PCI_BAR_MEM_128MB (128 * 1024 * 1024)
|
|
#define NXE_PCI_BAR_DOORBELL 0x20 /* bar 4 */
|
|
|
|
/*
|
|
* doorbell register space
|
|
*/
|
|
|
|
#define NXE_DB 0x00000000
|
|
#define NXE_DB_PEGID 0x00000003
|
|
#define NXE_DB_PEGID_RX 0x00000001 /* rx unit */
|
|
#define NXE_DB_PEGID_TX 0x00000002 /* tx unit */
|
|
#define NXE_DB_PRIVID 0x00000004 /* must be set */
|
|
#define NXE_DB_COUNT(_c) ((_c)<<3) /* count */
|
|
#define NXE_DB_CTXID(_c) ((_c)<<18) /* context id */
|
|
#define NXE_DB_OPCODE_RX_PROD 0x00000000
|
|
#define NXE_DB_OPCODE_RX_JUMBO_PROD 0x10000000
|
|
#define NXE_DB_OPCODE_RX_LRO_PROD 0x20000000
|
|
#define NXE_DB_OPCODE_CMD_PROD 0x30000000
|
|
#define NXE_DB_OPCODE_UPD_CONS 0x40000000
|
|
#define NXE_DB_OPCODE_RESET_CTX 0x50000000
|
|
|
|
/*
|
|
* register space
|
|
*/
|
|
|
|
/* different PCI functions use different registers sometimes */
|
|
#define _F(_f) ((_f) * 0x20)
|
|
|
|
/*
|
|
* driver ref section 4.2
|
|
*
|
|
* All the hardware registers are mapped in memory. Apart from the registers
|
|
* for the individual hardware blocks, the memory map includes a large number
|
|
* of software definable registers.
|
|
*
|
|
* The following table gives the memory map in the PCI address space.
|
|
*/
|
|
|
|
#define NXE_MAP_DDR_NET 0x00000000
|
|
#define NXE_MAP_DDR_MD 0x02000000
|
|
#define NXE_MAP_QDR_NET 0x04000000
|
|
#define NXE_MAP_DIRECT_CRB 0x04400000
|
|
#define NXE_MAP_OCM0 0x05000000
|
|
#define NXE_MAP_OCM1 0x05100000
|
|
#define NXE_MAP_CRB 0x06000000
|
|
|
|
/*
|
|
* Since there are a large number of registers they do not fit in a single
|
|
* PCI addressing range. Hence two windows are defined. The window starts at
|
|
* NXE_MAP_CRB, and extends to the end of the register map. The window is set
|
|
* using the NXE_REG_WINDOW_CRB register. The format of the NXE_REG_WINDOW_CRB
|
|
* register is as follows:
|
|
*/
|
|
|
|
#define NXE_WIN_CRB(_f) (0x06110210 + _F(_f))
|
|
#define NXE_WIN_CRB_0 (0<<25)
|
|
#define NXE_WIN_CRB_1 (1<<25)
|
|
|
|
/*
|
|
* The memory map inside the register windows are divided into a set of blocks.
|
|
* Each register block is owned by one hardware agent. The following table
|
|
* gives the memory map of the various register blocks in window 0. These
|
|
* registers are all in the CRB register space, so the offsets given here are
|
|
* relative to the base of the CRB offset region (NXE_MAP_CRB).
|
|
*/
|
|
|
|
#define NXE_W0_PCIE 0x00100000 /* PCI Express */
|
|
#define NXE_W0_NIU 0x00600000 /* Network Interface Unit */
|
|
#define NXE_W0_PPE_0 0x01100000 /* Protocol Processing Engine 0 */
|
|
#define NXE_W0_PPE_1 0x01200000 /* Protocol Processing Engine 1 */
|
|
#define NXE_W0_PPE_2 0x01300000 /* Protocol Processing Engine 2 */
|
|
#define NXE_W0_PPE_3 0x01400000 /* Protocol Processing Engine 3 */
|
|
#define NXE_W0_PPE_D 0x01500000 /* PPE D-cache */
|
|
#define NXE_W0_PPE_I 0x01600000 /* PPE I-cache */
|
|
|
|
/*
|
|
* These are the register blocks inside window 1.
|
|
*/
|
|
|
|
#define NXE_W1_PCIE 0x00100000
|
|
#define NXE_W1_SW 0x00200000
|
|
#define NXE_W1_SIR 0x01200000
|
|
#define NXE_W1_ROMUSB 0x01300000
|
|
|
|
/*
|
|
* Global registers
|
|
*/
|
|
#define NXE_BOOTLD_START 0x00010000
|
|
|
|
|
|
/*
|
|
* driver ref section 5
|
|
*
|
|
* CRB Window Register Descriptions
|
|
*/
|
|
|
|
/*
|
|
* PCI Express Registers
|
|
*
|
|
* Despite being in the CRB window space, they can be accessed via both
|
|
* windows. This means they are accessible "globally" without going relative
|
|
* to the start of the CRB window space.
|
|
*/
|
|
|
|
/* Interrupts */
|
|
#define NXE_ISR_VECTOR 0x06110100 /* Interrupt Vector */
|
|
#define NXE_ISR_MASK 0x06110104 /* Interrupt Mask */
|
|
#define NXE_ISR_TARGET_STATUS 0x06110118
|
|
#define NXE_ISR_TARGET_MASK 0x06110128
|
|
#define NXE_ISR_MINE(_f) (0x08 << (_f))
|
|
|
|
/* lock registers (semaphores between chipset and driver) */
|
|
#define NXE_SEM_ROM_LOCK 0x0611c010 /* ROM access lock */
|
|
#define NXE_SEM_ROM_UNLOCK 0x0611c014
|
|
#define NXE_SEM_PHY_LOCK 0x0611c018 /* PHY access lock */
|
|
#define NXE_SEM_PHY_UNLOCK 0x0611c01c
|
|
#define NXE_SEM_DONE 0x1
|
|
|
|
/*
|
|
* Network Interface Unit (NIU) Registers
|
|
*/
|
|
|
|
#define NXE_0_NIU_MODE 0x00600000
|
|
#define NXE_0_NIU_MODE_XGE (1<<2) /* XGE interface enabled */
|
|
#define NXE_0_NIU_MODE_GBE (1<<1) /* 4 GbE interfaces enabled */
|
|
#define NXE_0_NIU_SINGLE_TERM 0x00600004
|
|
#define NXE_0_NIU_INT_MASK 0x00600040
|
|
|
|
#define NXE_0_NIU_RESET_XG 0x0060001c /* reset XG */
|
|
#define NXE_0_NIU_RESET_FIFO 0x00600088 /* reset sys fifos */
|
|
|
|
#define _P(_p) ((_p) * 0x10000)
|
|
|
|
#define NXE_0_XG_CFG0(_p) (0x00670000 + _P(_p))
|
|
#define NXE_0_XG_CFG0_TX_EN (1<<0) /* TX enable */
|
|
#define NXE_0_XG_CFG0_TX_SYNC (1<<1) /* TX synced */
|
|
#define NXE_0_XG_CFG0_RX_EN (1<<2) /* RX enable */
|
|
#define NXE_0_XG_CFG0_RX_SYNC (1<<3) /* RX synced */
|
|
#define NXE_0_XG_CFG0_TX_FLOWCTL (1<<4) /* enable pause frame gen */
|
|
#define NXE_0_XG_CFG0_RX_FLOWCTL (1<<5) /* act on rxed pause frames */
|
|
#define NXE_0_XG_CFG0_LOOPBACK (1<<8) /* tx appears on rx */
|
|
#define NXE_0_XG_CFG0_TX_RST_PB (1<<15) /* reset frm tx proto block */
|
|
#define NXE_0_XG_CFG0_RX_RST_PB (1<<16) /* reset frm rx proto block */
|
|
#define NXE_0_XG_CFG0_TX_RST_MAC (1<<17) /* reset frm tx multiplexer */
|
|
#define NXE_0_XG_CFG0_RX_RST_MAC (1<<18) /* reset ctl frms and timers */
|
|
#define NXE_0_XG_CFG0_SOFT_RST (1<<31) /* soft reset */
|
|
#define NXE_0_XG_CFG1(_p) (0x00670004 + _P(_p))
|
|
#define NXE_0_XG_CFG1_REM_CRC (1<<0) /* enable crc removal */
|
|
#define NXE_0_XG_CFG1_CRC_EN (1<<1) /* append crc to tx frames */
|
|
#define NXE_0_XG_CFG1_NO_MAX (1<<5) /* rx all frames despite size */
|
|
#define NXE_0_XG_CFG1_WIRE_LO_ERR (1<<6) /* recognize local err */
|
|
#define NXE_0_XG_CFG1_PAUSE_FR_DIS (1<<8) /* disable pause frame detect */
|
|
#define NXE_0_XG_CFG1_SEQ_ERR_EN (1<<10) /* enable seq err detection */
|
|
#define NXE_0_XG_CFG1_MULTICAST (1<<12) /* accept all multicast */
|
|
#define NXE_0_XG_CFG1_PROMISC (1<<13) /* accept all frames */
|
|
#define NXE_0_XG_IPG(_p) (0x00670008 + _P(_p))
|
|
#define NXE_0_XG_MAC_LO(_p) (0x00670010 + _P(_p))
|
|
#define NXE_0_XG_MAC_HI(_p) (0x0067000c + _P(_p))
|
|
#define NXE_0_XG_STATUS(_p) (0x00670018 + _P(_p))
|
|
#define NXE_0_XG_MTU(_p) (0x0067001c + _P(_p))
|
|
#define NXE_0_XG_PAUSE_FRM(_p) (0x00670020 + _P(_p))
|
|
#define NXE_0_XG_TX_BYTES(_p) (0x00670024 + _P(_p))
|
|
#define NXE_0_XG_TX_PKTS(_p) (0x00670028 + _P(_p))
|
|
#define NXE_0_XG_RX_BYTES(_p) (0x0067002c + _P(_p))
|
|
#define NXE_0_XG_RX_PKTS(_p) (0x00670030 + _P(_p))
|
|
#define NXE_0_XG_AGGR_ERRS(_p) (0x00670034 + _P(_p))
|
|
#define NXE_0_XG_MCAST_PKTS(_p) (0x00670038 + _P(_p))
|
|
#define NXE_0_XG_UCAST_PKTS(_p) (0x0067003c + _P(_p))
|
|
#define NXE_0_XG_CRC_ERRS(_p) (0x00670040 + _P(_p))
|
|
#define NXE_0_XG_OVERSIZE(_p) (0x00670044 + _P(_p))
|
|
#define NXE_0_XG_UNDERSIZE(_p) (0x00670048 + _P(_p))
|
|
#define NXE_0_XG_LOCAL_ERRS(_p) (0x0067004c + _P(_p))
|
|
#define NXE_0_XG_REMOTE_ERRS(_p) (0x00670050 + _P(_p))
|
|
#define NXE_0_XG_CNTL_CHARS(_p) (0x00670054 + _P(_p))
|
|
#define NXE_0_XG_PAUSE_PKTS(_p) (0x00670058 + _P(_p))
|
|
|
|
/*
|
|
* Software Defined Registers
|
|
*/
|
|
|
|
/* chipset state registers */
|
|
#define NXE_1_SW_ROM_LOCK_ID 0x00202100
|
|
#define NXE_1_SW_ROM_LOCK_ID_DRV 0x0d417340
|
|
#define NXE_1_SW_PHY_LOCK_ID 0x00202120
|
|
#define NXE_1_SW_PHY_LOCK_ID_DRV 0x44524956
|
|
|
|
/* firmware version */
|
|
#define NXE_1_SW_FWVER_MAJOR 0x00202150 /* Major f/w version */
|
|
#define NXE_1_SW_FWVER_MINOR 0x00202154 /* Minor f/w version */
|
|
#define NXE_1_SW_FWVER_BUILD 0x00202158 /* Build/Sub f/w version */
|
|
|
|
/* misc */
|
|
#define NXE_1_SW_CMD_ADDR_HI 0x00202218 /* cmd ring phys addr */
|
|
#define NXE_1_SW_CMD_ADDR_LO 0x0020221c /* cmd ring phys addr */
|
|
#define NXE_1_SW_CMD_SIZE 0x002022c8 /* entries in the cmd ring */
|
|
#define NXE_1_SW_DUMMY_ADDR_HI 0x0020223c /* hi address of dummy buf */
|
|
#define NXE_1_SW_DUMMY_ADDR_LO 0x00202240 /* lo address of dummy buf */
|
|
#define NXE_1_SW_DUMMY_ADDR_LEN 1024
|
|
|
|
static const u_int32_t nxe_regmap[][4] = {
|
|
#define NXE_1_SW_CMD_PRODUCER(_f) (nxe_regmap[0][(_f)])
|
|
{ 0x00202208, 0x002023ac, 0x002023b8, 0x002023d0 },
|
|
#define NXE_1_SW_CMD_CONSUMER(_f) (nxe_regmap[1][(_f)])
|
|
{ 0x0020220c, 0x002023b0, 0x002023bc, 0x002023d4 },
|
|
|
|
#define NXE_1_SW_CONTEXT(_p) (nxe_regmap[2][(_p)])
|
|
#define NXE_1_SW_CONTEXT_SIG(_p) (0xdee0 | (_p))
|
|
{ 0x0020238c, 0x00202390, 0x0020239c, 0x002023a4 },
|
|
#define NXE_1_SW_CONTEXT_ADDR_LO(_p) (nxe_regmap[3][(_p)])
|
|
{ 0x00202388, 0x00202390, 0x00202398, 0x002023a0 },
|
|
#define NXE_1_SW_CONTEXT_ADDR_HI(_p) (nxe_regmap[4][(_p)])
|
|
{ 0x002023c0, 0x002023c4, 0x002023c8, 0x002023cc },
|
|
|
|
#define NXE_1_SW_INT_MASK(_p) (nxe_regmap[5][(_p)])
|
|
{ 0x002023d8, 0x002023e0, 0x002023e4, 0x002023e8 },
|
|
|
|
#define NXE_1_SW_RX_PRODUCER(_c) (nxe_regmap[6][(_c)])
|
|
{ 0x00202300, 0x00202344, 0x002023d8, 0x0020242c },
|
|
#define NXE_1_SW_RX_CONSUMER(_c) (nxe_regmap[7][(_c)])
|
|
{ 0x00202304, 0x00202348, 0x002023dc, 0x00202430 },
|
|
#define NXE_1_SW_RX_RING(_c) (nxe_regmap[8][(_c)])
|
|
{ 0x00202308, 0x0020234c, 0x002023f0, 0x00202434 },
|
|
#define NXE_1_SW_RX_SIZE(_c) (nxe_regmap[9][(_c)])
|
|
{ 0x0020230c, 0x00202350, 0x002023f4, 0x00202438 },
|
|
|
|
#define NXE_1_SW_RX_JUMBO_PRODUCER(_c) (nxe_regmap[10][(_c)])
|
|
{ 0x00202310, 0x00202354, 0x002023f8, 0x0020243c },
|
|
#define NXE_1_SW_RX_JUMBO_CONSUMER(_c) (nxe_regmap[11][(_c)])
|
|
{ 0x00202314, 0x00202358, 0x002023fc, 0x00202440 },
|
|
#define NXE_1_SW_RX_JUMBO_RING(_c) (nxe_regmap[12][(_c)])
|
|
{ 0x00202318, 0x0020235c, 0x00202400, 0x00202444 },
|
|
#define NXE_1_SW_RX_JUMBO_SIZE(_c) (nxe_regmap[13][(_c)])
|
|
{ 0x0020231c, 0x00202360, 0x00202404, 0x00202448 },
|
|
|
|
#define NXE_1_SW_RX_LRO_PRODUCER(_c) (nxe_regmap[14][(_c)])
|
|
{ 0x00202320, 0x00202364, 0x00202408, 0x0020244c },
|
|
#define NXE_1_SW_RX_LRO_CONSUMER(_c) (nxe_regmap[15][(_c)])
|
|
{ 0x00202324, 0x00202368, 0x0020240c, 0x00202450 },
|
|
#define NXE_1_SW_RX_LRO_RING(_c) (nxe_regmap[16][(_c)])
|
|
{ 0x00202328, 0x0020236c, 0x00202410, 0x00202454 },
|
|
#define NXE_1_SW_RX_LRO_SIZE(_c) (nxe_regmap[17][(_c)])
|
|
{ 0x0020232c, 0x00202370, 0x00202414, 0x00202458 },
|
|
|
|
#define NXE_1_SW_STATUS_RING(_c) (nxe_regmap[18][(_c)])
|
|
{ 0x00202330, 0x00202374, 0x00202418, 0x0020245c },
|
|
#define NXE_1_SW_STATUS_PRODUCER(_c) (nxe_regmap[19][(_c)])
|
|
{ 0x00202334, 0x00202378, 0x0020241c, 0x00202460 },
|
|
#define NXE_1_SW_STATUS_CONSUMER(_c) (nxe_regmap[20][(_c)])
|
|
{ 0x00202338, 0x0020237c, 0x00202420, 0x00202464 },
|
|
#define NXE_1_SW_STATUS_STATE(_c) (nxe_regmap[21][(_c)])
|
|
#define NXE_1_SW_STATUS_STATE_READY 0x0000ff01
|
|
{ 0x0020233c, 0x00202380, 0x00202424, 0x00202468 },
|
|
#define NXE_1_SW_STATUS_SIZE(_c) (nxe_regmap[22][(_c)])
|
|
{ 0x00202340, 0x00202384, 0x00202428, 0x0020246c }
|
|
};
|
|
|
|
|
|
#define NXE_1_SW_BOOTLD_CONFIG 0x002021fc
|
|
#define NXE_1_SW_BOOTLD_CONFIG_ROM 0x00000000
|
|
#define NXE_1_SW_BOOTLD_CONFIG_RAM 0x12345678
|
|
|
|
#define NXE_1_SW_CMDPEG_STATE 0x00202250 /* init status */
|
|
#define NXE_1_SW_CMDPEG_STATE_START 0xff00 /* init starting */
|
|
#define NXE_1_SW_CMDPEG_STATE_DONE 0xff01 /* init complete */
|
|
#define NXE_1_SW_CMDPEG_STATE_ACK 0xf00f /* init ack */
|
|
#define NXE_1_SW_CMDPEG_STATE_ERROR 0xffff /* init failed */
|
|
|
|
#define NXE_1_SW_XG_STATE 0x00202294 /* phy state */
|
|
#define NXE_1_SW_XG_STATE_PORT(_r, _p) (((_r)>>8*(_p))&0xff)
|
|
#define NXE_1_SW_XG_STATE_UP (1<<4)
|
|
#define NXE_1_SW_XG_STATE_DOWN (1<<5)
|
|
|
|
#define NXE_1_SW_MPORT_MODE 0x002022c4
|
|
#define NXE_1_SW_MPORT_MODE_SINGLE 0x1111
|
|
#define NXE_1_SW_MPORT_MODE_MULTI 0x2222
|
|
|
|
#define NXE_1_SW_INT_VECTOR 0x002022d4
|
|
|
|
#define NXE_1_SW_NIC_CAP_HOST 0x002023a8 /* host capabilities */
|
|
#define NXE_1_SW_NIC_CAP_FW 0x002023dc /* firmware capabilities */
|
|
#define NXE_1_SW_NIC_CAP_PORTINTR 0x1 /* per port interrupts */
|
|
#define NXE_1_SW_DRIVER_VER 0x002024a0 /* host driver version */
|
|
|
|
|
|
#define NXE_1_SW_TEMP 0x002023b4 /* Temperature sensor */
|
|
#define NXE_1_SW_TEMP_STATE(_x) ((_x)&0xffff) /* Temp state */
|
|
#define NXE_1_SW_TEMP_STATE_NONE 0x0000
|
|
#define NXE_1_SW_TEMP_STATE_OK 0x0001
|
|
#define NXE_1_SW_TEMP_STATE_WARN 0x0002
|
|
#define NXE_1_SW_TEMP_STATE_CRIT 0x0003
|
|
#define NXE_1_SW_TEMP_VAL(_x) (((_x)>>16)&0xffff) /* Temp value */
|
|
|
|
#define NXE_1_SW_V2P(_f) (0x00202490+((_f)*4)) /* virtual to phys */
|
|
|
|
/*
|
|
* ROMUSB Registers
|
|
*/
|
|
#define NXE_1_ROMUSB_STATUS 0x01300004 /* ROM Status */
|
|
#define NXE_1_ROMUSB_STATUS_DONE (1<<1)
|
|
#define NXE_1_ROMUSB_SW_RESET 0x01300008
|
|
#define NXE_1_ROMUSB_SW_RESET_DEF 0xffffffff
|
|
#define NXE_1_ROMUSB_SW_RESET_BOOT 0x0080000f
|
|
|
|
#define NXE_1_CASPER_RESET 0x01300038
|
|
#define NXE_1_CASPER_RESET_ENABLE 0x1
|
|
#define NXE_1_CASPER_RESET_DISABLE 0x1
|
|
|
|
#define NXE_1_GLB_PEGTUNE 0x0130005c /* reset register */
|
|
#define NXE_1_GLB_PEGTUNE_DONE 0x00000001
|
|
|
|
#define NXE_1_GLB_CHIPCLKCTL 0x013000a8
|
|
#define NXE_1_GLB_CHIPCLKCTL_ON 0x00003fff
|
|
|
|
/* ROM Registers */
|
|
#define NXE_1_ROM_CONTROL 0x01310000
|
|
#define NXE_1_ROM_OPCODE 0x01310004
|
|
#define NXE_1_ROM_OPCODE_READ 0x0000000b
|
|
#define NXE_1_ROM_ADDR 0x01310008
|
|
#define NXE_1_ROM_WDATA 0x0131000c
|
|
#define NXE_1_ROM_ABYTE_CNT 0x01310010
|
|
#define NXE_1_ROM_DBYTE_CNT 0x01310014 /* dummy byte count */
|
|
#define NXE_1_ROM_RDATA 0x01310018
|
|
#define NXE_1_ROM_AGT_TAG 0x0131001c
|
|
#define NXE_1_ROM_TIME_PARM 0x01310020
|
|
#define NXE_1_ROM_CLK_DIV 0x01310024
|
|
#define NXE_1_ROM_MISS_INSTR 0x01310028
|
|
|
|
/*
|
|
* flash memory layout
|
|
*
|
|
* These are offsets of memory accessible via the ROM Registers above
|
|
*/
|
|
#define NXE_FLASH_CRBINIT 0x00000000 /* crb init section */
|
|
#define NXE_FLASH_BRDCFG 0x00004000 /* board config */
|
|
#define NXE_FLASH_INITCODE 0x00006000 /* pegtune code */
|
|
#define NXE_FLASH_BOOTLD 0x00010000 /* boot loader */
|
|
#define NXE_FLASH_IMAGE 0x00043000 /* compressed image */
|
|
#define NXE_FLASH_SECONDARY 0x00200000 /* backup image */
|
|
#define NXE_FLASH_PXE 0x003d0000 /* pxe image */
|
|
#define NXE_FLASH_USER 0x003e8000 /* user region for new boards */
|
|
#define NXE_FLASH_VPD 0x003e8c00 /* vendor private data */
|
|
#define NXE_FLASH_LICENSE 0x003e9000 /* firmware license */
|
|
#define NXE_FLASH_FIXED 0x003f0000 /* backup of crbinit */
|
|
|
|
|
|
/*
|
|
* misc hardware details
|
|
*/
|
|
#define NXE_MAX_PORTS 4
|
|
#define NXE_MAX_PORT_LLADDRS 32
|
|
#define NXE_MAX_PKTLEN (64 * 1024)
|
|
|
|
|
|
/*
|
|
* hardware structures
|
|
*/
|
|
|
|
struct nxe_info {
|
|
u_int32_t ni_hdrver;
|
|
#define NXE_INFO_HDRVER_1 0x00000001
|
|
|
|
u_int32_t ni_board_mfg;
|
|
u_int32_t ni_board_type;
|
|
#define NXE_BRDTYPE_P1_BD 0x0000
|
|
#define NXE_BRDTYPE_P1_SB 0x0001
|
|
#define NXE_BRDTYPE_P1_SMAX 0x0002
|
|
#define NXE_BRDTYPE_P1_SOCK 0x0003
|
|
#define NXE_BRDTYPE_P2_SOCK_31 0x0008
|
|
#define NXE_BRDTYPE_P2_SOCK_35 0x0009
|
|
#define NXE_BRDTYPE_P2_SB35_4G 0x000a
|
|
#define NXE_BRDTYPE_P2_SB31_10G 0x000b
|
|
#define NXE_BRDTYPE_P2_SB31_2G 0x000c
|
|
#define NXE_BRDTYPE_P2_SB31_10G_IMEZ 0x000d
|
|
#define NXE_BRDTYPE_P2_SB31_10G_HMEZ 0x000e
|
|
#define NXE_BRDTYPE_P2_SB31_10G_CX4 0x000f
|
|
u_int32_t ni_board_num;
|
|
|
|
u_int32_t ni_chip_id;
|
|
u_int32_t ni_chip_minor;
|
|
u_int32_t ni_chip_major;
|
|
u_int32_t ni_chip_pkg;
|
|
u_int32_t ni_chip_lot;
|
|
|
|
u_int32_t ni_port_mask;
|
|
u_int32_t ni_peg_mask;
|
|
u_int32_t ni_icache;
|
|
u_int32_t ni_dcache;
|
|
u_int32_t ni_casper;
|
|
|
|
u_int32_t ni_lladdr0_low;
|
|
u_int32_t ni_lladdr1_low;
|
|
u_int32_t ni_lladdr2_low;
|
|
u_int32_t ni_lladdr3_low;
|
|
|
|
u_int32_t ni_mnsync_mode;
|
|
u_int32_t ni_mnsync_shift_cclk;
|
|
u_int32_t ni_mnsync_shift_mclk;
|
|
u_int32_t ni_mnwb_enable;
|
|
u_int32_t ni_mnfreq_crystal;
|
|
u_int32_t ni_mnfreq_speed;
|
|
u_int32_t ni_mnorg;
|
|
u_int32_t ni_mndepth;
|
|
u_int32_t ni_mnranks0;
|
|
u_int32_t ni_mnranks1;
|
|
u_int32_t ni_mnrd_latency0;
|
|
u_int32_t ni_mnrd_latency1;
|
|
u_int32_t ni_mnrd_latency2;
|
|
u_int32_t ni_mnrd_latency3;
|
|
u_int32_t ni_mnrd_latency4;
|
|
u_int32_t ni_mnrd_latency5;
|
|
u_int32_t ni_mnrd_latency6;
|
|
u_int32_t ni_mnrd_latency7;
|
|
u_int32_t ni_mnrd_latency8;
|
|
u_int32_t ni_mndll[18];
|
|
u_int32_t ni_mnddr_mode;
|
|
u_int32_t ni_mnddr_extmode;
|
|
u_int32_t ni_mntiming0;
|
|
u_int32_t ni_mntiming1;
|
|
u_int32_t ni_mntiming2;
|
|
|
|
u_int32_t ni_snsync_mode;
|
|
u_int32_t ni_snpt_mode;
|
|
u_int32_t ni_snecc_enable;
|
|
u_int32_t ni_snwb_enable;
|
|
u_int32_t ni_snfreq_crystal;
|
|
u_int32_t ni_snfreq_speed;
|
|
u_int32_t ni_snorg;
|
|
u_int32_t ni_sndepth;
|
|
u_int32_t ni_sndll;
|
|
u_int32_t ni_snrd_latency;
|
|
|
|
u_int32_t ni_lladdr0_high;
|
|
u_int32_t ni_lladdr1_high;
|
|
u_int32_t ni_lladdr2_high;
|
|
u_int32_t ni_lladdr3_high;
|
|
|
|
u_int32_t ni_magic;
|
|
#define NXE_INFO_MAGIC 0x12345678
|
|
|
|
u_int32_t ni_mnrd_imm;
|
|
u_int32_t ni_mndll_override;
|
|
} __packed;
|
|
|
|
struct nxe_imageinfo {
|
|
u_int32_t nim_bootld_ver;
|
|
u_int32_t nim_bootld_size;
|
|
|
|
u_int8_t nim_img_ver_major;
|
|
u_int8_t nim_img_ver_minor;
|
|
u_int16_t nim_img_ver_build;
|
|
|
|
u_int32_t min_img_size;
|
|
} __packed;
|
|
|
|
struct nxe_lladdr {
|
|
u_int8_t pad[2];
|
|
u_int8_t lladdr[6];
|
|
} __packed;
|
|
|
|
struct nxe_userinfo {
|
|
u_int8_t nu_flash_md5[1024];
|
|
|
|
struct nxe_imageinfo nu_imageinfo;
|
|
|
|
u_int32_t nu_primary;
|
|
u_int32_t nu_secondary;
|
|
|
|
u_int64_t nu_lladdr[NXE_MAX_PORTS][NXE_MAX_PORT_LLADDRS];
|
|
|
|
u_int32_t nu_subsys_id;
|
|
|
|
u_int8_t nu_serial[32];
|
|
|
|
u_int32_t nu_bios_ver;
|
|
} __packed;
|
|
|
|
/* hw structures actually used in the io path */
|
|
|
|
struct nxe_ctx_ring {
|
|
u_int64_t r_addr;
|
|
u_int32_t r_size;
|
|
u_int32_t r_reserved;
|
|
};
|
|
|
|
#define NXE_RING_RX 0
|
|
#define NXE_RING_RX_JUMBO 1
|
|
#define NXE_RING_RX_LRO 2
|
|
#define NXE_NRING 3
|
|
|
|
struct nxe_ctx {
|
|
u_int64_t ctx_cmd_consumer_addr;
|
|
|
|
struct nxe_ctx_ring ctx_cmd_ring;
|
|
|
|
struct nxe_ctx_ring ctx_rx_rings[NXE_NRING];
|
|
|
|
u_int64_t ctx_status_ring_addr;
|
|
u_int32_t ctx_status_ring_size;
|
|
|
|
u_int32_t ctx_id;
|
|
} __packed;
|
|
|
|
struct nxe_tx_desc {
|
|
u_int8_t tx_tcp_offset;
|
|
u_int8_t tx_ip_offset;
|
|
u_int16_t tx_flags;
|
|
#define NXE_TXD_F_OPCODE_TX (0x01 << 7)
|
|
|
|
u_int8_t tx_nbufs;
|
|
u_int16_t tx_length; /* XXX who makes a 24bit field? */
|
|
u_int8_t tx_length_hi;
|
|
|
|
u_int64_t tx_addr_2;
|
|
|
|
u_int16_t tx_id;
|
|
u_int16_t tx_mss;
|
|
|
|
u_int8_t tx_port;
|
|
u_int8_t tx_tso_hdr_len;
|
|
u_int16_t tx_ipsec_id;
|
|
|
|
u_int64_t tx_addr_3;
|
|
|
|
u_int64_t tx_addr_1;
|
|
|
|
u_int16_t tx_slen_1;
|
|
u_int16_t tx_slen_2;
|
|
u_int16_t tx_slen_3;
|
|
u_int16_t tx_slen_4;
|
|
|
|
u_int64_t tx_addr_4;
|
|
|
|
u_int64_t tx_reserved;
|
|
} __packed;
|
|
#define NXE_TXD_SEGS 4
|
|
#define NXE_TXD_DESCS 8
|
|
#define NXE_TXD_MAX_SEGS (NXE_TXD_SEGS * NXE_TXD_DESCS)
|
|
|
|
struct nxe_rx_desc {
|
|
u_int16_t rx_id;
|
|
u_int16_t rx_flags;
|
|
u_int32_t rx_len; /* packet length */
|
|
u_int64_t rx_addr;
|
|
} __packed;
|
|
#define NXE_RXD_MAX_SEGS 1
|
|
|
|
struct nxe_status_desc {
|
|
u_int8_t st_lro;
|
|
u_int8_t st_owner;
|
|
u_int16_t st_id;
|
|
u_int16_t st_len;
|
|
u_int16_t st_flags;
|
|
} __packed;
|
|
|
|
/*
|
|
* driver definitions
|
|
*/
|
|
|
|
struct nxe_board {
|
|
u_int32_t brd_type;
|
|
u_int brd_mode;
|
|
};
|
|
|
|
struct nxe_dmamem {
|
|
bus_dmamap_t ndm_map;
|
|
bus_dma_segment_t ndm_seg;
|
|
size_t ndm_size;
|
|
caddr_t ndm_kva;
|
|
};
|
|
#define NXE_DMA_MAP(_ndm) ((_ndm)->ndm_map)
|
|
#define NXE_DMA_LEN(_ndm) ((_ndm)->ndm_size)
|
|
#define NXE_DMA_DVA(_ndm) ((_ndm)->ndm_map->dm_segs[0].ds_addr)
|
|
#define NXE_DMA_KVA(_ndm) ((void *)(_ndm)->ndm_kva)
|
|
|
|
struct nxe_pkt {
|
|
int pkt_id;
|
|
bus_dmamap_t pkt_dmap;
|
|
struct mbuf *pkt_m;
|
|
TAILQ_ENTRY(nxe_pkt) pkt_link;
|
|
};
|
|
|
|
struct nxe_pkt_list {
|
|
struct nxe_pkt *npl_pkts;
|
|
TAILQ_HEAD(, nxe_pkt) npl_free;
|
|
TAILQ_HEAD(, nxe_pkt) npl_used;
|
|
};
|
|
|
|
struct nxe_ring {
|
|
struct nxe_dmamem *nr_dmamem;
|
|
u_int8_t *nr_pos;
|
|
|
|
u_int nr_slot;
|
|
int nr_ready;
|
|
|
|
size_t nr_desclen;
|
|
u_int nr_nentries;
|
|
};
|
|
|
|
/*
|
|
* autoconf glue
|
|
*/
|
|
|
|
struct nxe_softc {
|
|
struct device sc_dev;
|
|
|
|
bus_dma_tag_t sc_dmat;
|
|
|
|
bus_space_tag_t sc_memt;
|
|
bus_space_handle_t sc_memh;
|
|
bus_size_t sc_mems;
|
|
bus_space_handle_t sc_crbh;
|
|
bus_space_tag_t sc_dbt;
|
|
bus_space_handle_t sc_dbh;
|
|
bus_size_t sc_dbs;
|
|
|
|
void *sc_ih;
|
|
|
|
int sc_function;
|
|
int sc_port;
|
|
int sc_window;
|
|
|
|
const struct nxe_board *sc_board;
|
|
u_int sc_fw_major;
|
|
u_int sc_fw_minor;
|
|
u_int sc_fw_build;
|
|
|
|
struct arpcom sc_ac;
|
|
struct ifmedia sc_media;
|
|
|
|
struct nxe_pkt_list *sc_tx_pkts;
|
|
struct nxe_pkt_list *sc_rx_pkts;
|
|
|
|
/* allocations for the hw */
|
|
struct nxe_dmamem *sc_dummy_dma;
|
|
struct nxe_dmamem *sc_dummy_rx;
|
|
|
|
struct nxe_dmamem *sc_ctx;
|
|
u_int32_t *sc_cmd_consumer;
|
|
u_int32_t sc_cmd_consumer_cur;
|
|
|
|
struct nxe_ring *sc_cmd_ring;
|
|
struct nxe_ring *sc_rx_rings[NXE_NRING];
|
|
struct nxe_ring *sc_status_ring;
|
|
|
|
/* monitoring */
|
|
struct timeout sc_tick;
|
|
struct ksensor sc_sensor;
|
|
struct ksensordev sc_sensor_dev;
|
|
|
|
/* ioctl lock */
|
|
struct rwlock sc_lock;
|
|
};
|
|
|
|
int nxe_match(struct device *, void *, void *);
|
|
void nxe_attach(struct device *, struct device *, void *);
|
|
int nxe_intr(void *);
|
|
|
|
const struct cfattach nxe_ca = {
|
|
sizeof(struct nxe_softc),
|
|
nxe_match,
|
|
nxe_attach
|
|
};
|
|
|
|
struct cfdriver nxe_cd = {
|
|
NULL,
|
|
"nxe",
|
|
DV_IFNET
|
|
};
|
|
|
|
/* init code */
|
|
int nxe_pci_map(struct nxe_softc *,
|
|
struct pci_attach_args *);
|
|
void nxe_pci_unmap(struct nxe_softc *);
|
|
|
|
int nxe_board_info(struct nxe_softc *);
|
|
int nxe_user_info(struct nxe_softc *);
|
|
int nxe_init(struct nxe_softc *);
|
|
void nxe_uninit(struct nxe_softc *);
|
|
void nxe_mountroot(struct device *);
|
|
|
|
/* chip state */
|
|
void nxe_tick(void *);
|
|
void nxe_link_state(struct nxe_softc *);
|
|
|
|
/* interface operations */
|
|
int nxe_ioctl(struct ifnet *, u_long, caddr_t);
|
|
void nxe_start(struct ifnet *);
|
|
int nxe_complete(struct nxe_softc *);
|
|
void nxe_watchdog(struct ifnet *);
|
|
|
|
void nxe_rx_start(struct nxe_softc *);
|
|
|
|
void nxe_up(struct nxe_softc *);
|
|
void nxe_lladdr(struct nxe_softc *);
|
|
void nxe_iff(struct nxe_softc *);
|
|
void nxe_down(struct nxe_softc *);
|
|
|
|
int nxe_up_fw(struct nxe_softc *);
|
|
|
|
/* ifmedia operations */
|
|
int nxe_media_change(struct ifnet *);
|
|
void nxe_media_status(struct ifnet *, struct ifmediareq *);
|
|
|
|
|
|
/* ring handling */
|
|
struct nxe_ring *nxe_ring_alloc(struct nxe_softc *, size_t, u_int);
|
|
void nxe_ring_sync(struct nxe_softc *, struct nxe_ring *,
|
|
int);
|
|
void nxe_ring_free(struct nxe_softc *, struct nxe_ring *);
|
|
int nxe_ring_readable(struct nxe_ring *, int);
|
|
int nxe_ring_writeable(struct nxe_ring *, int);
|
|
void *nxe_ring_cur(struct nxe_softc *, struct nxe_ring *);
|
|
void *nxe_ring_next(struct nxe_softc *, struct nxe_ring *);
|
|
|
|
struct mbuf *nxe_load_pkt(struct nxe_softc *, bus_dmamap_t,
|
|
struct mbuf *);
|
|
struct mbuf *nxe_coalesce_m(struct mbuf *);
|
|
|
|
/* pkts */
|
|
struct nxe_pkt_list *nxe_pkt_alloc(struct nxe_softc *, u_int, int);
|
|
void nxe_pkt_free(struct nxe_softc *,
|
|
struct nxe_pkt_list *);
|
|
void nxe_pkt_put(struct nxe_pkt_list *, struct nxe_pkt *);
|
|
struct nxe_pkt *nxe_pkt_get(struct nxe_pkt_list *);
|
|
struct nxe_pkt *nxe_pkt_used(struct nxe_pkt_list *);
|
|
|
|
|
|
/* wrapper around dmaable memory allocations */
|
|
struct nxe_dmamem *nxe_dmamem_alloc(struct nxe_softc *, bus_size_t,
|
|
bus_size_t);
|
|
void nxe_dmamem_free(struct nxe_softc *,
|
|
struct nxe_dmamem *);
|
|
|
|
/* low level hardware access goo */
|
|
u_int32_t nxe_read(struct nxe_softc *, bus_size_t);
|
|
void nxe_write(struct nxe_softc *, bus_size_t, u_int32_t);
|
|
int nxe_wait(struct nxe_softc *, bus_size_t, u_int32_t,
|
|
u_int32_t, u_int);
|
|
|
|
void nxe_doorbell(struct nxe_softc *, u_int32_t);
|
|
|
|
int nxe_crb_set(struct nxe_softc *, int);
|
|
u_int32_t nxe_crb_read(struct nxe_softc *, bus_size_t);
|
|
void nxe_crb_write(struct nxe_softc *, bus_size_t,
|
|
u_int32_t);
|
|
int nxe_crb_wait(struct nxe_softc *, bus_size_t,
|
|
u_int32_t, u_int32_t, u_int);
|
|
|
|
int nxe_rom_lock(struct nxe_softc *);
|
|
void nxe_rom_unlock(struct nxe_softc *);
|
|
int nxe_rom_read(struct nxe_softc *, u_int32_t,
|
|
u_int32_t *);
|
|
int nxe_rom_read_region(struct nxe_softc *, u_int32_t,
|
|
void *, size_t);
|
|
|
|
|
|
/* misc bits */
|
|
#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
|
|
|
|
/* let's go! */
|
|
|
|
const struct pci_matchid nxe_devices[] = {
|
|
{ PCI_VENDOR_NETXEN, PCI_PRODUCT_NETXEN_NXB_10GXXR },
|
|
{ PCI_VENDOR_NETXEN, PCI_PRODUCT_NETXEN_NXB_10GCX4 },
|
|
{ PCI_VENDOR_NETXEN, PCI_PRODUCT_NETXEN_NXB_4GCU },
|
|
{ PCI_VENDOR_NETXEN, PCI_PRODUCT_NETXEN_NXB_IMEZ },
|
|
{ PCI_VENDOR_NETXEN, PCI_PRODUCT_NETXEN_NXB_HMEZ },
|
|
{ PCI_VENDOR_NETXEN, PCI_PRODUCT_NETXEN_NXB_IMEZ_2 },
|
|
{ PCI_VENDOR_NETXEN, PCI_PRODUCT_NETXEN_NXB_HMEZ_2 }
|
|
};
|
|
|
|
const struct nxe_board nxe_boards[] = {
|
|
{ NXE_BRDTYPE_P2_SB35_4G, NXE_0_NIU_MODE_GBE },
|
|
{ NXE_BRDTYPE_P2_SB31_10G, NXE_0_NIU_MODE_XGE },
|
|
{ NXE_BRDTYPE_P2_SB31_2G, NXE_0_NIU_MODE_GBE },
|
|
{ NXE_BRDTYPE_P2_SB31_10G_IMEZ, NXE_0_NIU_MODE_XGE },
|
|
{ NXE_BRDTYPE_P2_SB31_10G_HMEZ, NXE_0_NIU_MODE_XGE },
|
|
{ NXE_BRDTYPE_P2_SB31_10G_CX4, NXE_0_NIU_MODE_XGE }
|
|
};
|
|
|
|
int
|
|
nxe_match(struct device *parent, void *match, void *aux)
|
|
{
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
if (PCI_CLASS(pa->pa_class) != PCI_CLASS_NETWORK)
|
|
return (0);
|
|
|
|
return (pci_matchbyid(pa, nxe_devices, nitems(nxe_devices)));
|
|
}
|
|
|
|
void
|
|
nxe_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct nxe_softc *sc = (struct nxe_softc *)self;
|
|
struct pci_attach_args *pa = aux;
|
|
pci_intr_handle_t ih;
|
|
struct ifnet *ifp;
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
sc->sc_function = pa->pa_function;
|
|
sc->sc_window = -1;
|
|
|
|
rw_init(&sc->sc_lock, NULL);
|
|
|
|
if (nxe_pci_map(sc, pa) != 0) {
|
|
/* error already printed by nxe_pci_map() */
|
|
return;
|
|
}
|
|
|
|
nxe_crb_set(sc, 1);
|
|
|
|
if (nxe_board_info(sc) != 0) {
|
|
/* error already printed by nxe_board_info() */
|
|
goto unmap;
|
|
}
|
|
|
|
if (nxe_user_info(sc) != 0) {
|
|
/* error already printed by nxe_board_info() */
|
|
goto unmap;
|
|
}
|
|
|
|
if (nxe_init(sc) != 0) {
|
|
/* error already printed by nxe_init() */
|
|
goto unmap;
|
|
}
|
|
|
|
if (pci_intr_map(pa, &ih) != 0) {
|
|
printf(": unable to map interrupt\n");
|
|
goto uninit;
|
|
}
|
|
sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_NET,
|
|
nxe_intr, sc, DEVNAME(sc));
|
|
if (sc->sc_ih == NULL) {
|
|
printf(": unable to establish interrupt\n");
|
|
goto uninit;
|
|
}
|
|
|
|
ifp = &sc->sc_ac.ac_if;
|
|
ifp->if_softc = sc;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
ifp->if_capabilities = IFCAP_VLAN_MTU;
|
|
ifp->if_ioctl = nxe_ioctl;
|
|
ifp->if_start = nxe_start;
|
|
ifp->if_watchdog = nxe_watchdog;
|
|
ifp->if_hardmtu = MCLBYTES - ETHER_HDR_LEN - ETHER_CRC_LEN;
|
|
strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
|
|
ifq_init_maxlen(&ifp->if_snd, 512); /* XXX */
|
|
|
|
ifmedia_init(&sc->sc_media, 0, nxe_media_change, nxe_media_status);
|
|
ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_AUTO, 0, NULL);
|
|
ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO);
|
|
|
|
if_attach(ifp);
|
|
ether_ifattach(ifp);
|
|
|
|
printf(": %s firmware %d.%d.%d address %s\n",
|
|
pci_intr_string(pa->pa_pc, ih),
|
|
sc->sc_fw_major, sc->sc_fw_minor, sc->sc_fw_build,
|
|
ether_sprintf(sc->sc_ac.ac_enaddr));
|
|
return;
|
|
|
|
uninit:
|
|
nxe_uninit(sc);
|
|
unmap:
|
|
nxe_pci_unmap(sc);
|
|
}
|
|
|
|
int
|
|
nxe_pci_map(struct nxe_softc *sc, struct pci_attach_args *pa)
|
|
{
|
|
pcireg_t memtype;
|
|
|
|
memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, NXE_PCI_BAR_MEM);
|
|
if (pci_mapreg_map(pa, NXE_PCI_BAR_MEM, memtype, 0, &sc->sc_memt,
|
|
&sc->sc_memh, NULL, &sc->sc_mems, 0) != 0) {
|
|
printf(": unable to map host registers\n");
|
|
return (1);
|
|
}
|
|
if (sc->sc_mems != NXE_PCI_BAR_MEM_128MB) {
|
|
printf(": unexpected register map size\n");
|
|
goto unmap_mem;
|
|
}
|
|
|
|
/* set up the CRB window */
|
|
if (bus_space_subregion(sc->sc_memt, sc->sc_memh, NXE_MAP_CRB,
|
|
sc->sc_mems - NXE_MAP_CRB, &sc->sc_crbh) != 0) {
|
|
printf(": unable to create CRB window\n");
|
|
goto unmap_mem;
|
|
}
|
|
|
|
memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, NXE_PCI_BAR_DOORBELL);
|
|
if (pci_mapreg_map(pa, NXE_PCI_BAR_DOORBELL, memtype, 0, &sc->sc_dbt,
|
|
&sc->sc_dbh, NULL, &sc->sc_dbs, 0) != 0) {
|
|
printf(": unable to map doorbell registers\n");
|
|
/* bus_space(9) says i dont have to unmap subregions */
|
|
goto unmap_mem;
|
|
}
|
|
|
|
config_mountroot(&sc->sc_dev, nxe_mountroot);
|
|
return (0);
|
|
|
|
unmap_mem:
|
|
bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems);
|
|
sc->sc_mems = 0;
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
nxe_pci_unmap(struct nxe_softc *sc)
|
|
{
|
|
bus_space_unmap(sc->sc_dbt, sc->sc_dbh, sc->sc_dbs);
|
|
sc->sc_dbs = 0;
|
|
/* bus_space(9) says i dont have to unmap the crb subregion */
|
|
bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems);
|
|
sc->sc_mems = 0;
|
|
}
|
|
|
|
int
|
|
nxe_intr(void *xsc)
|
|
{
|
|
struct nxe_softc *sc = xsc;
|
|
u_int32_t vector;
|
|
|
|
DASSERT(sc->sc_window == 1);
|
|
|
|
vector = nxe_crb_read(sc, NXE_1_SW_INT_VECTOR);
|
|
if (!ISSET(vector, NXE_ISR_MINE(sc->sc_function)))
|
|
return (0);
|
|
|
|
nxe_crb_write(sc, NXE_1_SW_INT_VECTOR, 0x80 << sc->sc_function);
|
|
|
|
/* the interrupt is mine! we should do some work now */
|
|
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
nxe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
|
|
{
|
|
struct nxe_softc *sc = ifp->if_softc;
|
|
struct ifreq *ifr = (struct ifreq *)addr;
|
|
int s, error = 0;
|
|
|
|
rw_enter_write(&sc->sc_lock);
|
|
s = splnet();
|
|
|
|
timeout_del(&sc->sc_tick);
|
|
|
|
switch (cmd) {
|
|
case SIOCSIFADDR:
|
|
SET(ifp->if_flags, IFF_UP);
|
|
/* FALLTHROUGH */
|
|
|
|
case SIOCSIFFLAGS:
|
|
if (ISSET(ifp->if_flags, IFF_UP)) {
|
|
if (ISSET(ifp->if_flags, IFF_RUNNING))
|
|
error = ENETRESET;
|
|
else
|
|
nxe_up(sc);
|
|
} else {
|
|
if (ISSET(ifp->if_flags, IFF_RUNNING))
|
|
nxe_down(sc);
|
|
}
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
case SIOCSIFMEDIA:
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
|
|
break;
|
|
|
|
default:
|
|
error = ether_ioctl(ifp, &sc->sc_ac, cmd, addr);
|
|
}
|
|
|
|
if (error == ENETRESET) {
|
|
if (ISSET(ifp->if_flags, IFF_RUNNING)) {
|
|
nxe_crb_set(sc, 0);
|
|
nxe_iff(sc);
|
|
nxe_crb_set(sc, 1);
|
|
}
|
|
error = 0;
|
|
}
|
|
|
|
nxe_tick(sc);
|
|
|
|
splx(s);
|
|
rw_exit_write(&sc->sc_lock);
|
|
return (error);
|
|
}
|
|
|
|
void
|
|
nxe_up(struct nxe_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
static const u_int rx_ring_sizes[] = { 16384, 1024, 128 };
|
|
struct {
|
|
struct nxe_ctx ctx;
|
|
u_int32_t cmd_consumer;
|
|
} __packed *dmamem;
|
|
struct nxe_ctx *ctx;
|
|
struct nxe_ctx_ring *ring;
|
|
struct nxe_ring *nr;
|
|
u_int64_t dva;
|
|
u_int32_t intr_scheme;
|
|
int i;
|
|
|
|
if (nxe_up_fw(sc) != 0)
|
|
return;
|
|
|
|
/* allocate pkt lists */
|
|
sc->sc_tx_pkts = nxe_pkt_alloc(sc, 128, NXE_TXD_MAX_SEGS);
|
|
if (sc->sc_tx_pkts == NULL)
|
|
return;
|
|
sc->sc_rx_pkts = nxe_pkt_alloc(sc, 128, NXE_RXD_MAX_SEGS);
|
|
if (sc->sc_rx_pkts == NULL)
|
|
goto free_tx_pkts;
|
|
|
|
/* allocate the context memory and the consumer field */
|
|
sc->sc_ctx = nxe_dmamem_alloc(sc, sizeof(*dmamem), PAGE_SIZE);
|
|
if (sc->sc_ctx == NULL)
|
|
goto free_rx_pkts;
|
|
|
|
dmamem = NXE_DMA_KVA(sc->sc_ctx);
|
|
dva = NXE_DMA_DVA(sc->sc_ctx);
|
|
|
|
ctx = &dmamem->ctx;
|
|
ctx->ctx_cmd_consumer_addr = htole64(dva + sizeof(dmamem->ctx));
|
|
ctx->ctx_id = htole32(sc->sc_function);
|
|
|
|
sc->sc_cmd_consumer = &dmamem->cmd_consumer;
|
|
sc->sc_cmd_consumer_cur = 0;
|
|
|
|
/* allocate the cmd/tx ring */
|
|
sc->sc_cmd_ring = nxe_ring_alloc(sc,
|
|
sizeof(struct nxe_tx_desc), 1024 /* XXX */);
|
|
if (sc->sc_cmd_ring == NULL)
|
|
goto free_ctx;
|
|
|
|
ctx->ctx_cmd_ring.r_addr =
|
|
htole64(NXE_DMA_DVA(sc->sc_cmd_ring->nr_dmamem));
|
|
ctx->ctx_cmd_ring.r_size = htole32(sc->sc_cmd_ring->nr_nentries);
|
|
|
|
/* allocate the status ring */
|
|
sc->sc_status_ring = nxe_ring_alloc(sc,
|
|
sizeof(struct nxe_status_desc), 16384 /* XXX */);
|
|
if (sc->sc_status_ring == NULL)
|
|
goto free_cmd_ring;
|
|
|
|
ctx->ctx_status_ring_addr =
|
|
htole64(NXE_DMA_DVA(sc->sc_status_ring->nr_dmamem));
|
|
ctx->ctx_status_ring_size = htole32(sc->sc_status_ring->nr_nentries);
|
|
|
|
/* allocate something to point the jumbo and lro rings at */
|
|
sc->sc_dummy_rx = nxe_dmamem_alloc(sc, NXE_MAX_PKTLEN, PAGE_SIZE);
|
|
if (sc->sc_dummy_rx == NULL)
|
|
goto free_status_ring;
|
|
|
|
/* allocate the rx rings */
|
|
for (i = 0; i < NXE_NRING; i++) {
|
|
ring = &ctx->ctx_rx_rings[i];
|
|
nr = nxe_ring_alloc(sc, sizeof(struct nxe_rx_desc),
|
|
rx_ring_sizes[i]);
|
|
if (nr == NULL)
|
|
goto free_rx_rings;
|
|
|
|
ring->r_addr = htole64(NXE_DMA_DVA(nr->nr_dmamem));
|
|
ring->r_size = htole32(nr->nr_nentries);
|
|
|
|
sc->sc_rx_rings[i] = nr;
|
|
nxe_ring_sync(sc, sc->sc_rx_rings[i], BUS_DMASYNC_PREWRITE);
|
|
}
|
|
|
|
/* nothing can possibly go wrong now */
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_dummy_rx),
|
|
0, NXE_DMA_LEN(sc->sc_dummy_rx), BUS_DMASYNC_PREREAD);
|
|
nxe_ring_sync(sc, sc->sc_status_ring, BUS_DMASYNC_PREREAD);
|
|
nxe_ring_sync(sc, sc->sc_cmd_ring, BUS_DMASYNC_PREWRITE);
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_ctx),
|
|
0, NXE_DMA_LEN(sc->sc_ctx),
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
|
|
nxe_crb_write(sc, NXE_1_SW_CONTEXT_ADDR_LO(sc->sc_function),
|
|
(u_int32_t)dva);
|
|
nxe_crb_write(sc, NXE_1_SW_CONTEXT_ADDR_HI(sc->sc_function),
|
|
(u_int32_t)(dva >> 32));
|
|
nxe_crb_write(sc, NXE_1_SW_CONTEXT(sc->sc_port),
|
|
NXE_1_SW_CONTEXT_SIG(sc->sc_port));
|
|
|
|
nxe_crb_set(sc, 0);
|
|
nxe_crb_write(sc, NXE_0_XG_MTU(sc->sc_function),
|
|
MCLBYTES - ETHER_ALIGN);
|
|
nxe_lladdr(sc);
|
|
nxe_iff(sc);
|
|
nxe_crb_set(sc, 1);
|
|
|
|
SET(ifp->if_flags, IFF_RUNNING);
|
|
ifq_clr_oactive(&ifp->if_snd);
|
|
|
|
/* enable interrupts */
|
|
intr_scheme = nxe_crb_read(sc, NXE_1_SW_NIC_CAP_FW);
|
|
if (intr_scheme != NXE_1_SW_NIC_CAP_PORTINTR)
|
|
nxe_write(sc, NXE_ISR_MASK, 0x77f);
|
|
nxe_crb_write(sc, NXE_1_SW_INT_MASK(sc->sc_function), 0x1);
|
|
if (intr_scheme != NXE_1_SW_NIC_CAP_PORTINTR)
|
|
nxe_crb_write(sc, NXE_1_SW_INT_VECTOR, 0x0);
|
|
nxe_write(sc, NXE_ISR_TARGET_MASK, 0xbff);
|
|
|
|
return;
|
|
|
|
free_rx_rings:
|
|
while (i > 0) {
|
|
i--;
|
|
nxe_ring_sync(sc, sc->sc_rx_rings[i], BUS_DMASYNC_POSTWRITE);
|
|
nxe_ring_free(sc, sc->sc_rx_rings[i]);
|
|
}
|
|
|
|
nxe_dmamem_free(sc, sc->sc_dummy_rx);
|
|
free_status_ring:
|
|
nxe_ring_free(sc, sc->sc_status_ring);
|
|
free_cmd_ring:
|
|
nxe_ring_free(sc, sc->sc_cmd_ring);
|
|
free_ctx:
|
|
nxe_dmamem_free(sc, sc->sc_ctx);
|
|
free_rx_pkts:
|
|
nxe_pkt_free(sc, sc->sc_rx_pkts);
|
|
free_tx_pkts:
|
|
nxe_pkt_free(sc, sc->sc_tx_pkts);
|
|
}
|
|
|
|
int
|
|
nxe_up_fw(struct nxe_softc *sc)
|
|
{
|
|
u_int32_t r;
|
|
|
|
r = nxe_crb_read(sc, NXE_1_SW_CMDPEG_STATE);
|
|
if (r == NXE_1_SW_CMDPEG_STATE_ACK)
|
|
return (0);
|
|
|
|
if (r != NXE_1_SW_CMDPEG_STATE_DONE)
|
|
return (1);
|
|
|
|
nxe_crb_write(sc, NXE_1_SW_NIC_CAP_HOST, NXE_1_SW_NIC_CAP_PORTINTR);
|
|
nxe_crb_write(sc, NXE_1_SW_MPORT_MODE, NXE_1_SW_MPORT_MODE_MULTI);
|
|
nxe_crb_write(sc, NXE_1_SW_CMDPEG_STATE, NXE_1_SW_CMDPEG_STATE_ACK);
|
|
|
|
/* XXX busy wait in a process context is naughty */
|
|
if (!nxe_crb_wait(sc, NXE_1_SW_STATUS_STATE(sc->sc_function),
|
|
0xffffffff, NXE_1_SW_STATUS_STATE_READY, 1000))
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
nxe_lladdr(struct nxe_softc *sc)
|
|
{
|
|
u_int8_t *lladdr = sc->sc_ac.ac_enaddr;
|
|
|
|
DASSERT(sc->sc_window == 0);
|
|
|
|
nxe_crb_write(sc, NXE_0_XG_MAC_LO(sc->sc_port),
|
|
(lladdr[0] << 16) | (lladdr[1] << 24));
|
|
nxe_crb_write(sc, NXE_0_XG_MAC_HI(sc->sc_port),
|
|
(lladdr[2] << 0) | (lladdr[3] << 8) |
|
|
(lladdr[4] << 16) | (lladdr[5] << 24));
|
|
}
|
|
|
|
void
|
|
nxe_iff(struct nxe_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
u_int32_t cfg1 = 0x1447; /* XXX */
|
|
|
|
DASSERT(sc->sc_window == 0);
|
|
|
|
CLR(ifp->if_flags, IFF_ALLMULTI);
|
|
|
|
if (ISSET(ifp->if_flags, IFF_PROMISC) || sc->sc_ac.ac_multicnt > 0) {
|
|
SET(ifp->if_flags, IFF_ALLMULTI);
|
|
if (ISSET(ifp->if_flags, IFF_PROMISC))
|
|
cfg1 |= NXE_0_XG_CFG1_PROMISC;
|
|
else
|
|
cfg1 |= NXE_0_XG_CFG1_MULTICAST;
|
|
}
|
|
|
|
nxe_crb_write(sc, NXE_0_XG_CFG0(sc->sc_port),
|
|
NXE_0_XG_CFG0_TX_EN | NXE_0_XG_CFG0_RX_EN);
|
|
nxe_crb_write(sc, NXE_0_XG_CFG1(sc->sc_port), cfg1);
|
|
}
|
|
|
|
void
|
|
nxe_down(struct nxe_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
int i;
|
|
|
|
CLR(ifp->if_flags, IFF_RUNNING | IFF_ALLMULTI);
|
|
ifq_clr_oactive(&ifp->if_snd);
|
|
|
|
/* XXX turn the chip off */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_ctx),
|
|
0, NXE_DMA_LEN(sc->sc_ctx),
|
|
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
|
|
nxe_ring_sync(sc, sc->sc_cmd_ring, BUS_DMASYNC_POSTWRITE);
|
|
nxe_ring_sync(sc, sc->sc_status_ring, BUS_DMASYNC_POSTREAD);
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_dummy_rx),
|
|
0, NXE_DMA_LEN(sc->sc_dummy_rx), BUS_DMASYNC_POSTREAD);
|
|
|
|
for (i = 0; i < NXE_NRING; i++) {
|
|
nxe_ring_sync(sc, sc->sc_rx_rings[i], BUS_DMASYNC_POSTWRITE);
|
|
nxe_ring_free(sc, sc->sc_rx_rings[i]);
|
|
}
|
|
nxe_dmamem_free(sc, sc->sc_dummy_rx);
|
|
nxe_ring_free(sc, sc->sc_status_ring);
|
|
nxe_ring_free(sc, sc->sc_cmd_ring);
|
|
nxe_dmamem_free(sc, sc->sc_ctx);
|
|
nxe_pkt_free(sc, sc->sc_rx_pkts);
|
|
nxe_pkt_free(sc, sc->sc_tx_pkts);
|
|
}
|
|
|
|
void
|
|
nxe_start(struct ifnet *ifp)
|
|
{
|
|
struct nxe_softc *sc = ifp->if_softc;
|
|
struct nxe_ring *nr = sc->sc_cmd_ring;
|
|
struct nxe_tx_desc *txd;
|
|
struct nxe_pkt *pkt;
|
|
struct mbuf *m;
|
|
bus_dmamap_t dmap;
|
|
bus_dma_segment_t *segs;
|
|
int nsegs;
|
|
|
|
if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
|
|
ifq_is_oactive(&ifp->if_snd) ||
|
|
ifq_empty(&ifp->if_snd))
|
|
return;
|
|
|
|
if (nxe_ring_writeable(nr, sc->sc_cmd_consumer_cur) < NXE_TXD_DESCS) {
|
|
ifq_set_oactive(&ifp->if_snd);
|
|
return;
|
|
}
|
|
|
|
nxe_ring_sync(sc, nr, BUS_DMASYNC_POSTWRITE);
|
|
txd = nxe_ring_cur(sc, nr);
|
|
bzero(txd, sizeof(struct nxe_tx_desc));
|
|
|
|
do {
|
|
m = ifq_deq_begin(&ifp->if_snd);
|
|
if (m == NULL)
|
|
break;
|
|
|
|
pkt = nxe_pkt_get(sc->sc_tx_pkts);
|
|
if (pkt == NULL) {
|
|
ifq_deq_rollback(&ifp->if_snd, m);
|
|
ifq_set_oactive(&ifp->if_snd);
|
|
break;
|
|
}
|
|
|
|
ifq_deq_commit(&ifp->if_snd, m);
|
|
|
|
dmap = pkt->pkt_dmap;
|
|
m = nxe_load_pkt(sc, dmap, m);
|
|
if (m == NULL) {
|
|
nxe_pkt_put(sc->sc_tx_pkts, pkt);
|
|
ifp->if_oerrors++;
|
|
break;
|
|
}
|
|
|
|
#if NBPFILTER > 0
|
|
if (ifp->if_bpf)
|
|
bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
|
|
#endif
|
|
|
|
pkt->pkt_m = m;
|
|
|
|
txd->tx_flags = htole16(NXE_TXD_F_OPCODE_TX);
|
|
txd->tx_nbufs = dmap->dm_nsegs;
|
|
txd->tx_length = htole16(dmap->dm_mapsize);
|
|
txd->tx_port = sc->sc_port;
|
|
|
|
segs = dmap->dm_segs;
|
|
nsegs = dmap->dm_nsegs;
|
|
do {
|
|
switch ((nsegs > NXE_TXD_SEGS) ?
|
|
NXE_TXD_SEGS : nsegs) {
|
|
case 4:
|
|
txd->tx_addr_4 = htole64(segs[3].ds_addr);
|
|
txd->tx_slen_4 = htole32(segs[3].ds_len);
|
|
case 3:
|
|
txd->tx_addr_3 = htole64(segs[2].ds_addr);
|
|
txd->tx_slen_3 = htole32(segs[2].ds_len);
|
|
case 2:
|
|
txd->tx_addr_2 = htole64(segs[1].ds_addr);
|
|
txd->tx_slen_2 = htole32(segs[1].ds_len);
|
|
case 1:
|
|
txd->tx_addr_1 = htole64(segs[0].ds_addr);
|
|
txd->tx_slen_1 = htole32(segs[0].ds_len);
|
|
break;
|
|
default:
|
|
panic("%s: unexpected segments in tx map",
|
|
DEVNAME(sc));
|
|
}
|
|
|
|
nsegs -= NXE_TXD_SEGS;
|
|
segs += NXE_TXD_SEGS;
|
|
|
|
pkt->pkt_id = nr->nr_slot;
|
|
|
|
txd = nxe_ring_next(sc, nr);
|
|
bzero(txd, sizeof(struct nxe_tx_desc));
|
|
} while (nsegs > 0);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
} while (nr->nr_ready >= NXE_TXD_DESCS);
|
|
|
|
nxe_ring_sync(sc, nr, BUS_DMASYNC_PREWRITE);
|
|
nxe_crb_write(sc, NXE_1_SW_CMD_PRODUCER(sc->sc_function), nr->nr_slot);
|
|
}
|
|
|
|
int
|
|
nxe_complete(struct nxe_softc *sc)
|
|
{
|
|
struct nxe_pkt *pkt;
|
|
int new_cons, cur_cons;
|
|
int rv = 0;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_ctx),
|
|
0, NXE_DMA_LEN(sc->sc_ctx),
|
|
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
|
|
new_cons = letoh32(*sc->sc_cmd_consumer);
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_ctx),
|
|
0, NXE_DMA_LEN(sc->sc_ctx),
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
|
|
cur_cons = sc->sc_cmd_consumer_cur;
|
|
pkt = nxe_pkt_used(sc->sc_tx_pkts);
|
|
|
|
while (pkt != NULL && cur_cons != new_cons) {
|
|
if (pkt->pkt_id == cur_cons) {
|
|
bus_dmamap_sync(sc->sc_dmat, pkt->pkt_dmap,
|
|
0, pkt->pkt_dmap->dm_mapsize,
|
|
BUS_DMASYNC_POSTWRITE);
|
|
bus_dmamap_unload(sc->sc_dmat, pkt->pkt_dmap);
|
|
|
|
m_freem(pkt->pkt_m);
|
|
|
|
nxe_pkt_put(sc->sc_tx_pkts, pkt);
|
|
|
|
pkt = nxe_pkt_used(sc->sc_tx_pkts);
|
|
}
|
|
|
|
cur_cons++;
|
|
cur_cons %= sc->sc_cmd_ring->nr_nentries;
|
|
|
|
rv = 1;
|
|
}
|
|
|
|
if (rv == 1) {
|
|
sc->sc_cmd_consumer_cur = cur_cons;
|
|
ifq_clr_oactive(&sc->sc_ac.ac_if.if_snd);
|
|
}
|
|
|
|
return (rv);
|
|
}
|
|
|
|
struct mbuf *
|
|
nxe_coalesce_m(struct mbuf *m)
|
|
{
|
|
struct mbuf *m0;
|
|
|
|
MGETHDR(m0, M_DONTWAIT, MT_DATA);
|
|
if (m0 == NULL)
|
|
goto err;
|
|
|
|
if (m->m_pkthdr.len > MHLEN) {
|
|
MCLGET(m0, M_DONTWAIT);
|
|
if (!(m0->m_flags & M_EXT)) {
|
|
m_freem(m0);
|
|
m0 = NULL;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t));
|
|
m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len;
|
|
|
|
err:
|
|
m_freem(m);
|
|
return (m0);
|
|
}
|
|
|
|
struct mbuf *
|
|
nxe_load_pkt(struct nxe_softc *sc, bus_dmamap_t dmap, struct mbuf *m)
|
|
{
|
|
switch (bus_dmamap_load_mbuf(sc->sc_dmat, dmap, m, BUS_DMA_NOWAIT)) {
|
|
case 0:
|
|
break;
|
|
|
|
case EFBIG:
|
|
m = nxe_coalesce_m(m);
|
|
if (m == NULL)
|
|
break;
|
|
|
|
if (bus_dmamap_load_mbuf(sc->sc_dmat, dmap, m,
|
|
BUS_DMA_NOWAIT) == 0)
|
|
break;
|
|
|
|
/* we get here on error */
|
|
/* FALLTHROUGH */
|
|
default:
|
|
m_freem(m);
|
|
m = NULL;
|
|
break;
|
|
}
|
|
|
|
return (m);
|
|
}
|
|
|
|
void
|
|
nxe_rx_start(struct nxe_softc *sc)
|
|
{
|
|
struct nxe_ring *nr = sc->sc_rx_rings[NXE_RING_RX];
|
|
struct nxe_rx_desc *rxd;
|
|
struct nxe_pkt *pkt;
|
|
struct mbuf *m;
|
|
|
|
if (nxe_ring_writeable(nr, 0) == 0)
|
|
return;
|
|
|
|
nxe_ring_sync(sc, nr, BUS_DMASYNC_POSTWRITE);
|
|
rxd = nxe_ring_cur(sc, nr);
|
|
|
|
for (;;) {
|
|
pkt = nxe_pkt_get(sc->sc_rx_pkts);
|
|
if (pkt == NULL)
|
|
goto done;
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m == NULL)
|
|
goto put_pkt;
|
|
|
|
MCLGET(m, M_DONTWAIT);
|
|
if (!ISSET(m->m_flags, M_EXT))
|
|
goto free_m;
|
|
|
|
m->m_data += ETHER_ALIGN;
|
|
m->m_len = m->m_pkthdr.len = MCLBYTES - ETHER_ALIGN;
|
|
|
|
if (bus_dmamap_load_mbuf(sc->sc_dmat, pkt->pkt_dmap, m,
|
|
BUS_DMA_NOWAIT) != 0)
|
|
goto free_m;
|
|
|
|
pkt->pkt_m = m;
|
|
|
|
bzero(rxd, sizeof(struct nxe_rx_desc));
|
|
rxd->rx_len = htole32(m->m_len);
|
|
rxd->rx_id = pkt->pkt_id;
|
|
rxd->rx_addr = htole64(pkt->pkt_dmap->dm_segs[0].ds_addr);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, pkt->pkt_dmap, 0,
|
|
pkt->pkt_dmap->dm_mapsize, BUS_DMASYNC_PREREAD);
|
|
|
|
rxd = nxe_ring_next(sc, nr);
|
|
|
|
if (nr->nr_ready == 0)
|
|
goto done;
|
|
}
|
|
|
|
free_m:
|
|
m_freem(m);
|
|
put_pkt:
|
|
nxe_pkt_put(sc->sc_rx_pkts, pkt);
|
|
done:
|
|
nxe_ring_sync(sc, nr, BUS_DMASYNC_PREWRITE);
|
|
nxe_crb_write(sc, NXE_1_SW_RX_PRODUCER(sc->sc_function), nr->nr_slot);
|
|
nxe_doorbell(sc, NXE_DB_PEGID_RX | NXE_DB_PRIVID |
|
|
NXE_DB_OPCODE_RX_PROD |
|
|
NXE_DB_COUNT(nr->nr_slot) | NXE_DB_CTXID(sc->sc_function));
|
|
}
|
|
|
|
void
|
|
nxe_watchdog(struct ifnet *ifp)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
int
|
|
nxe_media_change(struct ifnet *ifp)
|
|
{
|
|
/* ignore for now */
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
nxe_media_status(struct ifnet *ifp, struct ifmediareq *imr)
|
|
{
|
|
struct nxe_softc *sc = ifp->if_softc;
|
|
|
|
imr->ifm_active = IFM_ETHER | IFM_AUTO;
|
|
imr->ifm_status = IFM_AVALID;
|
|
|
|
nxe_link_state(sc);
|
|
if (LINK_STATE_IS_UP(ifp->if_link_state))
|
|
imr->ifm_status |= IFM_ACTIVE;
|
|
}
|
|
|
|
void
|
|
nxe_link_state(struct nxe_softc *sc)
|
|
{
|
|
struct ifnet *ifp = &sc->sc_ac.ac_if;
|
|
int link_state = LINK_STATE_DOWN;
|
|
u_int32_t r;
|
|
|
|
DASSERT(sc->sc_window == 1);
|
|
|
|
r = nxe_crb_read(sc, NXE_1_SW_XG_STATE);
|
|
if (NXE_1_SW_XG_STATE_PORT(r, sc->sc_function) & NXE_1_SW_XG_STATE_UP)
|
|
link_state = LINK_STATE_UP;
|
|
|
|
if (ifp->if_link_state != link_state) {
|
|
ifp->if_link_state = link_state;
|
|
if_link_state_change(ifp);
|
|
}
|
|
}
|
|
|
|
int
|
|
nxe_board_info(struct nxe_softc *sc)
|
|
{
|
|
struct nxe_info *ni;
|
|
int rv = 1;
|
|
int i;
|
|
|
|
ni = malloc(sizeof(struct nxe_info), M_TEMP, M_NOWAIT);
|
|
if (ni == NULL) {
|
|
printf(": unable to allocate temporary memory\n");
|
|
return (1);
|
|
}
|
|
|
|
if (nxe_rom_read_region(sc, NXE_FLASH_BRDCFG, ni,
|
|
sizeof(struct nxe_info)) != 0) {
|
|
printf(": unable to read board info\n");
|
|
goto out;
|
|
}
|
|
|
|
if (ni->ni_hdrver != NXE_INFO_HDRVER_1) {
|
|
printf(": unexpected board info header version 0x%08x\n",
|
|
ni->ni_hdrver);
|
|
goto out;
|
|
}
|
|
if (ni->ni_magic != NXE_INFO_MAGIC) {
|
|
printf(": board info magic is invalid\n");
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < nitems(nxe_boards); i++) {
|
|
if (ni->ni_board_type == nxe_boards[i].brd_type) {
|
|
sc->sc_board = &nxe_boards[i];
|
|
break;
|
|
}
|
|
}
|
|
if (sc->sc_board == NULL) {
|
|
printf(": unknown board type %04x\n", ni->ni_board_type);
|
|
goto out;
|
|
}
|
|
|
|
rv = 0;
|
|
out:
|
|
free(ni, M_TEMP, 0);
|
|
return (rv);
|
|
}
|
|
|
|
int
|
|
nxe_user_info(struct nxe_softc *sc)
|
|
{
|
|
struct nxe_userinfo *nu;
|
|
u_int64_t lladdr;
|
|
struct nxe_lladdr *la;
|
|
int rv = 1;
|
|
|
|
nu = malloc(sizeof(struct nxe_userinfo), M_TEMP, M_NOWAIT);
|
|
if (nu == NULL) {
|
|
printf(": unable to allocate temp memory\n");
|
|
return (1);
|
|
}
|
|
if (nxe_rom_read_region(sc, NXE_FLASH_USER, nu,
|
|
sizeof(struct nxe_userinfo)) != 0) {
|
|
printf(": unable to read user info\n");
|
|
goto out;
|
|
}
|
|
|
|
sc->sc_fw_major = nu->nu_imageinfo.nim_img_ver_major;
|
|
sc->sc_fw_minor = nu->nu_imageinfo.nim_img_ver_minor;
|
|
sc->sc_fw_build = letoh16(nu->nu_imageinfo.nim_img_ver_build);
|
|
|
|
if (sc->sc_fw_major > NXE_VERSION_MAJOR ||
|
|
sc->sc_fw_major < NXE_VERSION_MAJOR ||
|
|
sc->sc_fw_minor > NXE_VERSION_MINOR ||
|
|
sc->sc_fw_minor < NXE_VERSION_MINOR) {
|
|
printf(": firmware %d.%d.%d is unsupported by this driver\n",
|
|
sc->sc_fw_major, sc->sc_fw_minor, sc->sc_fw_build);
|
|
goto out;
|
|
}
|
|
|
|
lladdr = swap64(nu->nu_lladdr[sc->sc_function][0]);
|
|
la = (struct nxe_lladdr *)&lladdr;
|
|
bcopy(la->lladdr, sc->sc_ac.ac_enaddr, ETHER_ADDR_LEN);
|
|
|
|
rv = 0;
|
|
out:
|
|
free(nu, M_TEMP, 0);
|
|
return (rv);
|
|
}
|
|
|
|
int
|
|
nxe_init(struct nxe_softc *sc)
|
|
{
|
|
u_int64_t dva;
|
|
u_int32_t r;
|
|
|
|
/* stop the chip from processing */
|
|
nxe_crb_write(sc, NXE_1_SW_CMD_PRODUCER(sc->sc_function), 0);
|
|
nxe_crb_write(sc, NXE_1_SW_CMD_CONSUMER(sc->sc_function), 0);
|
|
nxe_crb_write(sc, NXE_1_SW_CMD_ADDR_HI, 0);
|
|
nxe_crb_write(sc, NXE_1_SW_CMD_ADDR_LO, 0);
|
|
|
|
/*
|
|
* if this is the first port on the device it needs some special
|
|
* treatment to get things going.
|
|
*/
|
|
if (sc->sc_function == 0) {
|
|
/* init adapter offload */
|
|
sc->sc_dummy_dma = nxe_dmamem_alloc(sc,
|
|
NXE_1_SW_DUMMY_ADDR_LEN, PAGE_SIZE);
|
|
if (sc->sc_dummy_dma == NULL) {
|
|
printf(": unable to allocate dummy memory\n");
|
|
return (1);
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_dummy_dma),
|
|
0, NXE_DMA_LEN(sc->sc_dummy_dma), BUS_DMASYNC_PREREAD);
|
|
|
|
dva = NXE_DMA_DVA(sc->sc_dummy_dma);
|
|
nxe_crb_write(sc, NXE_1_SW_DUMMY_ADDR_HI, dva >> 32);
|
|
nxe_crb_write(sc, NXE_1_SW_DUMMY_ADDR_LO, dva);
|
|
|
|
r = nxe_crb_read(sc, NXE_1_SW_BOOTLD_CONFIG);
|
|
if (r == 0x55555555) {
|
|
r = nxe_crb_read(sc, NXE_1_ROMUSB_SW_RESET);
|
|
if (r != NXE_1_ROMUSB_SW_RESET_BOOT) {
|
|
printf(": unexpected boot state\n");
|
|
goto err;
|
|
}
|
|
|
|
/* clear */
|
|
nxe_crb_write(sc, NXE_1_SW_BOOTLD_CONFIG, 0);
|
|
}
|
|
|
|
/* start the device up */
|
|
nxe_crb_write(sc, NXE_1_SW_DRIVER_VER, NXE_VERSION);
|
|
nxe_crb_write(sc, NXE_1_GLB_PEGTUNE, NXE_1_GLB_PEGTUNE_DONE);
|
|
|
|
/*
|
|
* the firmware takes a long time to boot, so we'll check
|
|
* it later on, and again when we want to bring a port up.
|
|
*/
|
|
}
|
|
|
|
return (0);
|
|
|
|
err:
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_dummy_dma),
|
|
0, NXE_DMA_LEN(sc->sc_dummy_dma), BUS_DMASYNC_POSTREAD);
|
|
nxe_dmamem_free(sc, sc->sc_dummy_dma);
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
nxe_uninit(struct nxe_softc *sc)
|
|
{
|
|
if (sc->sc_function == 0) {
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(sc->sc_dummy_dma),
|
|
0, NXE_DMA_LEN(sc->sc_dummy_dma), BUS_DMASYNC_POSTREAD);
|
|
nxe_dmamem_free(sc, sc->sc_dummy_dma);
|
|
}
|
|
}
|
|
|
|
void
|
|
nxe_mountroot(struct device *self)
|
|
{
|
|
struct nxe_softc *sc = (struct nxe_softc *)self;
|
|
|
|
DASSERT(sc->sc_window == 1);
|
|
|
|
if (!nxe_crb_wait(sc, NXE_1_SW_CMDPEG_STATE, 0xffffffff,
|
|
NXE_1_SW_CMDPEG_STATE_DONE, 10000)) {
|
|
printf("%s: firmware bootstrap failed, code 0x%08x\n",
|
|
DEVNAME(sc), nxe_crb_read(sc, NXE_1_SW_CMDPEG_STATE));
|
|
return;
|
|
}
|
|
|
|
sc->sc_port = nxe_crb_read(sc, NXE_1_SW_V2P(sc->sc_function));
|
|
if (sc->sc_port == 0x55555555)
|
|
sc->sc_port = sc->sc_function;
|
|
|
|
nxe_crb_write(sc, NXE_1_SW_NIC_CAP_HOST, NXE_1_SW_NIC_CAP_PORTINTR);
|
|
nxe_crb_write(sc, NXE_1_SW_MPORT_MODE, NXE_1_SW_MPORT_MODE_MULTI);
|
|
nxe_crb_write(sc, NXE_1_SW_CMDPEG_STATE, NXE_1_SW_CMDPEG_STATE_ACK);
|
|
|
|
sc->sc_sensor.type = SENSOR_TEMP;
|
|
strlcpy(sc->sc_sensor_dev.xname, DEVNAME(sc),
|
|
sizeof(sc->sc_sensor_dev.xname));
|
|
sensor_attach(&sc->sc_sensor_dev, &sc->sc_sensor);
|
|
sensordev_install(&sc->sc_sensor_dev);
|
|
|
|
timeout_set(&sc->sc_tick, nxe_tick, sc);
|
|
nxe_tick(sc);
|
|
}
|
|
|
|
void
|
|
nxe_tick(void *xsc)
|
|
{
|
|
struct nxe_softc *sc = xsc;
|
|
u_int32_t temp;
|
|
int window;
|
|
int s;
|
|
|
|
s = splnet();
|
|
window = nxe_crb_set(sc, 1);
|
|
temp = nxe_crb_read(sc, NXE_1_SW_TEMP);
|
|
nxe_link_state(sc);
|
|
nxe_crb_set(sc, window);
|
|
splx(s);
|
|
|
|
sc->sc_sensor.value = NXE_1_SW_TEMP_VAL(temp) * 1000000 + 273150000;
|
|
sc->sc_sensor.flags = 0;
|
|
|
|
switch (NXE_1_SW_TEMP_STATE(temp)) {
|
|
case NXE_1_SW_TEMP_STATE_NONE:
|
|
sc->sc_sensor.status = SENSOR_S_UNSPEC;
|
|
break;
|
|
case NXE_1_SW_TEMP_STATE_OK:
|
|
sc->sc_sensor.status = SENSOR_S_OK;
|
|
break;
|
|
case NXE_1_SW_TEMP_STATE_WARN:
|
|
sc->sc_sensor.status = SENSOR_S_WARN;
|
|
break;
|
|
case NXE_1_SW_TEMP_STATE_CRIT:
|
|
/* we should probably bring things down if this is true */
|
|
sc->sc_sensor.status = SENSOR_S_CRIT;
|
|
break;
|
|
default:
|
|
sc->sc_sensor.flags = SENSOR_FUNKNOWN;
|
|
break;
|
|
}
|
|
|
|
timeout_add_sec(&sc->sc_tick, 5);
|
|
}
|
|
|
|
|
|
struct nxe_ring *
|
|
nxe_ring_alloc(struct nxe_softc *sc, size_t desclen, u_int nentries)
|
|
{
|
|
struct nxe_ring *nr;
|
|
|
|
nr = malloc(sizeof(struct nxe_ring), M_DEVBUF, M_WAITOK);
|
|
|
|
nr->nr_dmamem = nxe_dmamem_alloc(sc, desclen * nentries, PAGE_SIZE);
|
|
if (nr->nr_dmamem == NULL) {
|
|
free(nr, M_DEVBUF, 0);
|
|
return (NULL);
|
|
}
|
|
|
|
nr->nr_pos = NXE_DMA_KVA(nr->nr_dmamem);
|
|
nr->nr_slot = 0;
|
|
nr->nr_desclen = desclen;
|
|
nr->nr_nentries = nentries;
|
|
|
|
return (nr);
|
|
}
|
|
|
|
void
|
|
nxe_ring_sync(struct nxe_softc *sc, struct nxe_ring *nr, int flags)
|
|
{
|
|
bus_dmamap_sync(sc->sc_dmat, NXE_DMA_MAP(nr->nr_dmamem),
|
|
0, NXE_DMA_LEN(nr->nr_dmamem), flags);
|
|
}
|
|
|
|
void
|
|
nxe_ring_free(struct nxe_softc *sc, struct nxe_ring *nr)
|
|
{
|
|
nxe_dmamem_free(sc, nr->nr_dmamem);
|
|
free(nr, M_DEVBUF, 0);
|
|
}
|
|
|
|
int
|
|
nxe_ring_readable(struct nxe_ring *nr, int producer)
|
|
{
|
|
nr->nr_ready = producer - nr->nr_slot;
|
|
if (nr->nr_ready < 0)
|
|
nr->nr_ready += nr->nr_nentries;
|
|
|
|
return (nr->nr_ready);
|
|
}
|
|
|
|
int
|
|
nxe_ring_writeable(struct nxe_ring *nr, int consumer)
|
|
{
|
|
nr->nr_ready = consumer - nr->nr_slot;
|
|
if (nr->nr_ready <= 0)
|
|
nr->nr_ready += nr->nr_nentries;
|
|
|
|
return (nr->nr_ready);
|
|
}
|
|
|
|
void *
|
|
nxe_ring_cur(struct nxe_softc *sc, struct nxe_ring *nr)
|
|
{
|
|
return (nr->nr_pos);
|
|
}
|
|
|
|
void *
|
|
nxe_ring_next(struct nxe_softc *sc, struct nxe_ring *nr)
|
|
{
|
|
if (++nr->nr_slot >= nr->nr_nentries) {
|
|
nr->nr_slot = 0;
|
|
nr->nr_pos = NXE_DMA_KVA(nr->nr_dmamem);
|
|
} else
|
|
nr->nr_pos += nr->nr_desclen;
|
|
|
|
nr->nr_ready--;
|
|
|
|
return (nr->nr_pos);
|
|
}
|
|
|
|
struct nxe_pkt_list *
|
|
nxe_pkt_alloc(struct nxe_softc *sc, u_int npkts, int nsegs)
|
|
{
|
|
struct nxe_pkt_list *npl;
|
|
struct nxe_pkt *pkt;
|
|
int i;
|
|
|
|
npl = malloc(sizeof(*npl), M_DEVBUF, M_WAITOK | M_ZERO);
|
|
pkt = mallocarray(npkts, sizeof(*pkt), M_DEVBUF, M_WAITOK | M_ZERO);
|
|
|
|
npl->npl_pkts = pkt;
|
|
TAILQ_INIT(&npl->npl_free);
|
|
TAILQ_INIT(&npl->npl_used);
|
|
for (i = 0; i < npkts; i++) {
|
|
pkt = &npl->npl_pkts[i];
|
|
|
|
pkt->pkt_id = i;
|
|
if (bus_dmamap_create(sc->sc_dmat, NXE_MAX_PKTLEN, nsegs,
|
|
NXE_MAX_PKTLEN, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
|
|
&pkt->pkt_dmap) != 0) {
|
|
nxe_pkt_free(sc, npl);
|
|
return (NULL);
|
|
}
|
|
|
|
TAILQ_INSERT_TAIL(&npl->npl_free, pkt, pkt_link);
|
|
}
|
|
|
|
return (npl);
|
|
}
|
|
|
|
void
|
|
nxe_pkt_free(struct nxe_softc *sc, struct nxe_pkt_list *npl)
|
|
{
|
|
struct nxe_pkt *pkt;
|
|
|
|
while ((pkt = nxe_pkt_get(npl)) != NULL)
|
|
bus_dmamap_destroy(sc->sc_dmat, pkt->pkt_dmap);
|
|
|
|
free(npl->npl_pkts, M_DEVBUF, 0);
|
|
free(npl, M_DEVBUF, sizeof *npl);
|
|
}
|
|
|
|
struct nxe_pkt *
|
|
nxe_pkt_get(struct nxe_pkt_list *npl)
|
|
{
|
|
struct nxe_pkt *pkt;
|
|
|
|
pkt = TAILQ_FIRST(&npl->npl_free);
|
|
if (pkt != NULL) {
|
|
TAILQ_REMOVE(&npl->npl_free, pkt, pkt_link);
|
|
TAILQ_INSERT_TAIL(&npl->npl_used, pkt, pkt_link);
|
|
}
|
|
|
|
return (pkt);
|
|
}
|
|
|
|
void
|
|
nxe_pkt_put(struct nxe_pkt_list *npl, struct nxe_pkt *pkt)
|
|
{
|
|
TAILQ_REMOVE(&npl->npl_used, pkt, pkt_link);
|
|
TAILQ_INSERT_TAIL(&npl->npl_free, pkt, pkt_link);
|
|
|
|
}
|
|
|
|
struct nxe_pkt *
|
|
nxe_pkt_used(struct nxe_pkt_list *npl)
|
|
{
|
|
return (TAILQ_FIRST(&npl->npl_used));
|
|
}
|
|
|
|
struct nxe_dmamem *
|
|
nxe_dmamem_alloc(struct nxe_softc *sc, bus_size_t size, bus_size_t align)
|
|
{
|
|
struct nxe_dmamem *ndm;
|
|
int nsegs;
|
|
|
|
ndm = malloc(sizeof(*ndm), M_DEVBUF, M_WAITOK | M_ZERO);
|
|
ndm->ndm_size = size;
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
|
|
BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &ndm->ndm_map) != 0)
|
|
goto ndmfree;
|
|
|
|
if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &ndm->ndm_seg, 1,
|
|
&nsegs, BUS_DMA_WAITOK |BUS_DMA_ZERO) != 0)
|
|
goto destroy;
|
|
|
|
if (bus_dmamem_map(sc->sc_dmat, &ndm->ndm_seg, nsegs, size,
|
|
&ndm->ndm_kva, BUS_DMA_WAITOK) != 0)
|
|
goto free;
|
|
|
|
if (bus_dmamap_load(sc->sc_dmat, ndm->ndm_map, ndm->ndm_kva, size,
|
|
NULL, BUS_DMA_WAITOK) != 0)
|
|
goto unmap;
|
|
|
|
return (ndm);
|
|
|
|
unmap:
|
|
bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, size);
|
|
free:
|
|
bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1);
|
|
destroy:
|
|
bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map);
|
|
ndmfree:
|
|
free(ndm, M_DEVBUF, sizeof *ndm);
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
void
|
|
nxe_dmamem_free(struct nxe_softc *sc, struct nxe_dmamem *ndm)
|
|
{
|
|
bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, ndm->ndm_size);
|
|
bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1);
|
|
bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map);
|
|
free(ndm, M_DEVBUF, sizeof *ndm);
|
|
}
|
|
|
|
u_int32_t
|
|
nxe_read(struct nxe_softc *sc, bus_size_t r)
|
|
{
|
|
bus_space_barrier(sc->sc_memt, sc->sc_memh, r, 4,
|
|
BUS_SPACE_BARRIER_READ);
|
|
return (bus_space_read_4(sc->sc_memt, sc->sc_memh, r));
|
|
}
|
|
|
|
void
|
|
nxe_write(struct nxe_softc *sc, bus_size_t r, u_int32_t v)
|
|
{
|
|
bus_space_write_4(sc->sc_memt, sc->sc_memh, r, v);
|
|
bus_space_barrier(sc->sc_memt, sc->sc_memh, r, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
}
|
|
|
|
int
|
|
nxe_wait(struct nxe_softc *sc, bus_size_t r, u_int32_t m, u_int32_t v,
|
|
u_int timeout)
|
|
{
|
|
while ((nxe_read(sc, r) & m) != v) {
|
|
if (timeout == 0)
|
|
return (0);
|
|
|
|
delay(1000);
|
|
timeout--;
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
nxe_doorbell(struct nxe_softc *sc, u_int32_t v)
|
|
{
|
|
bus_space_write_4(sc->sc_memt, sc->sc_memh, NXE_DB, v);
|
|
bus_space_barrier(sc->sc_memt, sc->sc_memh, NXE_DB, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
}
|
|
|
|
int
|
|
nxe_crb_set(struct nxe_softc *sc, int window)
|
|
{
|
|
int oldwindow = sc->sc_window;
|
|
u_int32_t r;
|
|
|
|
if (sc->sc_window != window) {
|
|
sc->sc_window = window;
|
|
|
|
r = window ? NXE_WIN_CRB_1 : NXE_WIN_CRB_0;
|
|
nxe_write(sc, NXE_WIN_CRB(sc->sc_function), r);
|
|
|
|
if (nxe_read(sc, NXE_WIN_CRB(sc->sc_function)) != r)
|
|
printf("%s: crb window hasnt moved\n", DEVNAME(sc));
|
|
}
|
|
|
|
return (oldwindow);
|
|
}
|
|
|
|
u_int32_t
|
|
nxe_crb_read(struct nxe_softc *sc, bus_size_t r)
|
|
{
|
|
bus_space_barrier(sc->sc_memt, sc->sc_crbh, r, 4,
|
|
BUS_SPACE_BARRIER_READ);
|
|
return (bus_space_read_4(sc->sc_memt, sc->sc_crbh, r));
|
|
}
|
|
|
|
void
|
|
nxe_crb_write(struct nxe_softc *sc, bus_size_t r, u_int32_t v)
|
|
{
|
|
bus_space_write_4(sc->sc_memt, sc->sc_crbh, r, v);
|
|
bus_space_barrier(sc->sc_memt, sc->sc_crbh, r, 4,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
}
|
|
|
|
int
|
|
nxe_crb_wait(struct nxe_softc *sc, bus_size_t r, u_int32_t m, u_int32_t v,
|
|
u_int timeout)
|
|
{
|
|
while ((nxe_crb_read(sc, r) & m) != v) {
|
|
if (timeout == 0)
|
|
return (0);
|
|
|
|
delay(1000);
|
|
timeout--;
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
nxe_rom_lock(struct nxe_softc *sc)
|
|
{
|
|
if (!nxe_wait(sc, NXE_SEM_ROM_LOCK, 0xffffffff,
|
|
NXE_SEM_DONE, 10000))
|
|
return (1);
|
|
nxe_crb_write(sc, NXE_1_SW_ROM_LOCK_ID, NXE_1_SW_ROM_LOCK_ID);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
nxe_rom_unlock(struct nxe_softc *sc)
|
|
{
|
|
nxe_read(sc, NXE_SEM_ROM_UNLOCK);
|
|
}
|
|
|
|
int
|
|
nxe_rom_read(struct nxe_softc *sc, u_int32_t r, u_int32_t *v)
|
|
{
|
|
int rv = 1;
|
|
|
|
DASSERT(sc->sc_window == 1);
|
|
|
|
if (nxe_rom_lock(sc) != 0)
|
|
return (1);
|
|
|
|
/* set the rom address */
|
|
nxe_crb_write(sc, NXE_1_ROM_ADDR, r);
|
|
|
|
/* set the xfer len */
|
|
nxe_crb_write(sc, NXE_1_ROM_ABYTE_CNT, 3);
|
|
delay(100); /* used to prevent bursting on the chipset */
|
|
nxe_crb_write(sc, NXE_1_ROM_DBYTE_CNT, 0);
|
|
|
|
/* set opcode and wait for completion */
|
|
nxe_crb_write(sc, NXE_1_ROM_OPCODE, NXE_1_ROM_OPCODE_READ);
|
|
if (!nxe_crb_wait(sc, NXE_1_ROMUSB_STATUS, NXE_1_ROMUSB_STATUS_DONE,
|
|
NXE_1_ROMUSB_STATUS_DONE, 100))
|
|
goto err;
|
|
|
|
/* reset counters */
|
|
nxe_crb_write(sc, NXE_1_ROM_ABYTE_CNT, 0);
|
|
delay(100);
|
|
nxe_crb_write(sc, NXE_1_ROM_DBYTE_CNT, 0);
|
|
|
|
*v = nxe_crb_read(sc, NXE_1_ROM_RDATA);
|
|
|
|
rv = 0;
|
|
err:
|
|
nxe_rom_unlock(sc);
|
|
return (rv);
|
|
}
|
|
|
|
int
|
|
nxe_rom_read_region(struct nxe_softc *sc, u_int32_t r, void *buf,
|
|
size_t buflen)
|
|
{
|
|
u_int32_t *databuf = buf;
|
|
int i;
|
|
|
|
#ifdef NXE_DEBUG
|
|
if ((buflen % 4) != 0)
|
|
panic("nxe_read_rom_region: buflen is wrong (%d)", buflen);
|
|
#endif
|
|
|
|
buflen = buflen / 4;
|
|
for (i = 0; i < buflen; i++) {
|
|
if (nxe_rom_read(sc, r, &databuf[i]) != 0)
|
|
return (1);
|
|
|
|
r += sizeof(u_int32_t);
|
|
}
|
|
|
|
return (0);
|
|
}
|