Ejemplo n.º 1
0
static void
pcn_resetrings(pcn_t *pcnp)
{
	int i;
	uint16_t bufsz = ((~(PCN_BUFSZ) + 1) & PCN_RXLEN_BUFSZ) | PCN_RXLEN_MBO;

	pcnp->pcn_rxhead = 0;
	pcnp->pcn_txreclaim = 0;
	pcnp->pcn_txsend = 0;
	pcnp->pcn_txavail = PCN_TXRING;

	/* reset rx descriptor values */
	for (i = 0; i < PCN_RXRING; i++) {
		pcn_rx_desc_t	*rmd = &pcnp->pcn_rxdescp[i];
		pcn_buf_t	*rxb = pcnp->pcn_rxbufs[i];

		rmd->pcn_rxlen = rmd->pcn_rsvd0 = 0;
		rmd->pcn_rbaddr = rxb->pb_paddr;
		rmd->pcn_bufsz = bufsz;
		rmd->pcn_rxstat = PCN_RXSTAT_OWN;
	}
	(void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, 0,
	    PCN_RXRING * sizeof (pcn_rx_desc_t), DDI_DMA_SYNC_FORDEV);

	/* reset tx descriptor values */
	for (i = 0; i < PCN_TXRING; i++) {
		pcn_tx_desc_t	*txd = &pcnp->pcn_txdescp[i];
		pcn_buf_t	*txb = pcnp->pcn_txbufs[i];

		txd->pcn_txstat = txd->pcn_txctl = txd->pcn_uspace = 0;
		txd->pcn_tbaddr = txb->pb_paddr;
	}
	(void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, 0,
	    PCN_TXRING * sizeof (pcn_tx_desc_t), DDI_DMA_SYNC_FORDEV);

	/* set addresses of decriptors */
	pcn_csr_write(pcnp, PCN_CSR_RXADDR0, pcnp->pcn_rxdesc_paddr & 0xFFFF);
	pcn_csr_write(pcnp, PCN_CSR_RXADDR1,
	    (pcnp->pcn_rxdesc_paddr >> 16) & 0xFFFF);

	pcn_csr_write(pcnp, PCN_CSR_TXADDR0, pcnp->pcn_txdesc_paddr & 0xFFFF);
	pcn_csr_write(pcnp, PCN_CSR_TXADDR1,
	    (pcnp->pcn_txdesc_paddr >> 16) & 0xFFFF);

	/* set the ring sizes */
	pcn_csr_write(pcnp, PCN_CSR_RXRINGLEN, (~PCN_RXRING) + 1);
	pcn_csr_write(pcnp, PCN_CSR_TXRINGLEN, (~PCN_TXRING) + 1);
}
Ejemplo n.º 2
0
static int
pcn_m_unicast(void *arg, const uint8_t *macaddr)
{
	pcn_t	*pcnp = (pcn_t *)arg;
	int i;
	uint16_t addr[3];

	bcopy(macaddr, addr, sizeof (addr));

	mutex_enter(&pcnp->pcn_intrlock);
	mutex_enter(&pcnp->pcn_xmtlock);

	if (IS_RUNNING(pcnp))
		pcn_suspend(pcnp);

	for (i = 0; i < 3; i++)
		pcn_csr_write(pcnp, PCN_CSR_PAR0 + i, addr[i]);

	bcopy(macaddr, pcnp->pcn_addr, ETHERADDRL);

	if (IS_RUNNING(pcnp))
		pcn_resume(pcnp);

	mutex_exit(&pcnp->pcn_xmtlock);
	mutex_exit(&pcnp->pcn_intrlock);

	return (0);
}
Ejemplo n.º 3
0
static void
pcn_irq_en(device_t d)
{
struct pcn_softc *sc = device_get_softc(d);
/* This can be called from IRQ context -- since all register accesses
 * involve RAP we must take care to preserve it across this routine!
 */
uint32_t rap = CSR_READ_4(sc, PCN_IO32_RAP);
	/* do NOT |= INTEN since writing 1 in the wrong place may clear things */
	pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN);
	CSR_WRITE_4(sc, PCN_IO32_RAP, rap);
}
Ejemplo n.º 4
0
static int
pcn_m_multicast(void *arg, boolean_t add, const uint8_t *macaddr)
{
	pcn_t		*pcnp = (pcn_t *)arg;
	int		index;
	uint32_t	crc;
	uint16_t	bit;
	uint16_t	newval, oldval;

	/*
	 * PCNet uses the upper 6 bits of the CRC of the macaddr
	 * to index into a 64bit mask
	 */
	CRC32(crc, macaddr, ETHERADDRL, -1U, crc32_table);
	crc >>= 26;
	index = crc / 16;
	bit = (1U << (crc % 16));

	mutex_enter(&pcnp->pcn_intrlock);
	mutex_enter(&pcnp->pcn_xmtlock);
	newval = oldval = pcnp->pcn_mctab[index];

	if (add) {
		pcnp->pcn_mccount[crc]++;
		if (pcnp->pcn_mccount[crc] == 1)
			newval |= bit;
	} else {
		pcnp->pcn_mccount[crc]--;
		if (pcnp->pcn_mccount[crc] == 0)
			newval &= ~bit;
	}
	if (newval != oldval) {
		pcnp->pcn_mctab[index] = newval;
		pcn_suspend(pcnp);
		pcn_csr_write(pcnp, PCN_CSR_MAR0 + index, newval);
		pcn_resume(pcnp);
	}

	mutex_exit(&pcnp->pcn_xmtlock);
	mutex_exit(&pcnp->pcn_intrlock);

	return (0);
}
Ejemplo n.º 5
0
static int
pcn_irq_check_dis(device_t d)
{
struct pcn_softc *sc = device_get_softc(d);
/* This can be called from IRQ context -- since all register accesses
 * involve RAP we must take care to preserve it across this routine!
 */
u_int32_t rap = CSR_READ_4(sc, PCN_IO32_RAP);
u_int32_t csr;
int      rval;

	csr  = pcn_csr_read(sc, PCN_CSR_CSR);

	if ( PCN_CSR_INTR & csr ) {
		/* must not write 1 to any bit as this might clear things */
		pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN & ~(PCN_CSR_INTEN));
		rval = FILTER_HANDLED;
	} else {
		rval = FILTER_STRAY;
	}
	/* restore RAP */
	CSR_WRITE_4(sc, PCN_IO32_RAP, rap);
	return rval;
}
Ejemplo n.º 6
0
static unsigned
pcn_intr(caddr_t arg1)
{
	pcn_t		*pcnp = (void *)arg1;
	mblk_t		*mp = NULL;
	uint32_t	status, status2;
	boolean_t	do_reset = B_FALSE;

	mutex_enter(&pcnp->pcn_intrlock);

	if (IS_SUSPENDED(pcnp)) {
		mutex_exit(&pcnp->pcn_intrlock);
		return (DDI_INTR_UNCLAIMED);
	}

	while ((status = pcn_csr_read(pcnp, PCN_CSR_CSR)) & PCN_CSR_INTR) {
		pcn_csr_write(pcnp, PCN_CSR_CSR, status);

		status2 = pcn_csr_read(pcnp, PCN_CSR_EXTCTL2);

		if (status & PCN_CSR_TINT) {
			mutex_enter(&pcnp->pcn_xmtlock);
			pcn_reclaim(pcnp);
			mutex_exit(&pcnp->pcn_xmtlock);
		}

		if (status & PCN_CSR_RINT)
			mp = pcn_receive(pcnp);

		if (status & PCN_CSR_ERR) {
			do_reset = B_TRUE;
			break;
		}

		/* timer interrupt */
		if (status2 & PCN_EXTCTL2_STINT) {
			/* ack it */
			PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL2,
			    PCN_EXTCTL2_STINT);

			if (pcn_watchdog(pcnp) != DDI_SUCCESS) {
				do_reset = B_TRUE;
				break;
			}
		}
	}

	if (do_reset) {
		mutex_enter(&pcnp->pcn_xmtlock);
		pcn_resetall(pcnp);
		mutex_exit(&pcnp->pcn_xmtlock);
		mutex_exit(&pcnp->pcn_intrlock);

		mii_reset(pcnp->pcn_mii);
	} else {
		mutex_exit(&pcnp->pcn_intrlock);
	}

	if (mp)
		mac_rx(pcnp->pcn_mh, NULL, mp);

	return (DDI_INTR_CLAIMED);
}
Ejemplo n.º 7
0
static boolean_t
pcn_send(pcn_t *pcnp, mblk_t *mp)
{
	size_t		len;
	pcn_buf_t	*txb;
	pcn_tx_desc_t	*tmd;
	int		txsend;

	ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
	ASSERT(mp != NULL);

	len = msgsize(mp);
	if (len > ETHERVLANMTU) {
		pcnp->pcn_macxmt_errors++;
		freemsg(mp);
		return (B_TRUE);
	}

	if (pcnp->pcn_txavail < PCN_TXRECLAIM)
		pcn_reclaim(pcnp);

	if (pcnp->pcn_txavail == 0) {
		pcnp->pcn_wantw = B_TRUE;

		/* enable tx interrupt */
		PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_LTINTEN);
		return (B_FALSE);
	}

	txsend = pcnp->pcn_txsend;

	/*
	 * We copy the packet to a single buffer.  NetBSD sources suggest
	 * that if multiple segements are ever used, VMware has a bug that will
	 * only allow 8 segments to be used, while the physical chips allow 16
	 */
	txb = pcnp->pcn_txbufs[txsend];
	mcopymsg(mp, txb->pb_buf);	/* frees mp! */

	pcnp->pcn_opackets++;
	pcnp->pcn_obytes += len;
	if (txb->pb_buf[0] & 0x1) {
		if (bcmp(txb->pb_buf, pcn_broadcast, ETHERADDRL) != 0)
			pcnp->pcn_multixmt++;
		else
			pcnp->pcn_brdcstxmt++;
	}

	tmd = &pcnp->pcn_txdescp[txsend];

	SYNCBUF(txb, len, DDI_DMA_SYNC_FORDEV);
	tmd->pcn_txstat = 0;
	tmd->pcn_tbaddr = txb->pb_paddr;

	/* PCNet wants the 2's complement of the length of the buffer */
	tmd->pcn_txctl = (~(len) + 1) & PCN_TXCTL_BUFSZ;
	tmd->pcn_txctl |= PCN_TXCTL_MBO;
	tmd->pcn_txctl |= PCN_TXCTL_STP | PCN_TXCTL_ENP | PCN_TXCTL_ADD_FCS |
	    PCN_TXCTL_OWN | PCN_TXCTL_MORE_LTINT;

	SYNCTXDESC(pcnp, txsend, DDI_DMA_SYNC_FORDEV);

	pcnp->pcn_txavail--;
	pcnp->pcn_txsend = (txsend + 1) % PCN_TXRING;
	pcnp->pcn_txstall_time = gethrtime() + (5 * 1000000000ULL);

	pcn_csr_write(pcnp, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN);

	return (B_TRUE);
}
Ejemplo n.º 8
0
static int
pcn_initialize(pcn_t *pcnp, boolean_t getfact)
{
	int i;
	uint16_t addr[3];

	bcopy(pcnp->pcn_addr, addr, sizeof (addr));

	/*
	 * Issue a reset by reading from the RESET register.
	 * Note that we don't know if the chip is operating in
	 * 16-bit or 32-bit mode at this point, so we attempt
	 * to reset the chip both ways.  If one fails, the other
	 * will succeed.
	 */
	(void) CSR_READ_2(pcnp, PCN_IO16_RESET);
	(void) CSR_READ_4(pcnp, PCN_IO32_RESET);

	drv_usecwait(1000);

	/* Select 32-bit (DWIO) mode */
	CSR_WRITE_4(pcnp, PCN_IO32_RDP, 0);

	/* The timer is not affected by a reset, so explicitly disable */
	pcn_stop_timer(pcnp);

	/* Enable fast suspend */
	pcn_csr_write(pcnp, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE);

	/* Select Style 3 descriptors */
	pcn_bcr_write(pcnp, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI);

	/* Set MAC address */
	if (getfact)
		pcn_getfactaddr(pcnp);

	pcn_csr_write(pcnp, PCN_CSR_PAR0, addr[0]);
	pcn_csr_write(pcnp, PCN_CSR_PAR1, addr[1]);
	pcn_csr_write(pcnp, PCN_CSR_PAR2, addr[2]);

	/* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */
	PCN_BCR_CLRBIT(pcnp, PCN_BCR_MISCCFG, PCN_MISC_ASEL);

	/*
	 * XXX: need to find a way to determine when 10bt media is
	 * selected for non Am79C978, and set to PCN_PORT_10BASET
	 * instead of PCN_PORT_MII
	 */
	pcn_csr_write(pcnp, PCN_CSR_MODE, PCN_PORT_MII);

	/* Reenable auto negotiation for external phy */
	PCN_BCR_SETBIT(pcnp, PCN_BCR_MIICTL, PCN_MIICTL_XPHYANE);

	if (pcnp->pcn_promisc)
		PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);

	/* Initalize mcast addr filter */
	for (i = 0; i < 4; i++)
		pcn_csr_write(pcnp, PCN_CSR_MAR0 + i, pcnp->pcn_mctab[i]);

	pcn_resetrings(pcnp);

	/* We're not using the initialization block. */
	pcn_csr_write(pcnp, PCN_CSR_IAB1, 0);

	/*
	 * Enable burst read and write.  Also set the no underflow
	 * bit.  This will avoid transmit underruns in ceratin
	 * conditions while still providing decent performance.
	 */
	PCN_BCR_SETBIT(pcnp, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW |
	    PCN_BUSCTL_BREAD | PCN_BUSCTL_BWRITE);

	/* Enable graceful recovery from underflow. */
	PCN_CSR_SETBIT(pcnp, PCN_CSR_IMR, PCN_IMR_DXSUFLO);

	/* Enable auto-padding of short TX frames. */
	PCN_CSR_SETBIT(pcnp, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX);

	if (pcnp->pcn_type == Am79C978)
		pcn_bcr_write(pcnp, PCN_BCR_PHYSEL,
		    PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA);

	return (DDI_SUCCESS);
}