340 lines
7.9 KiB
C
340 lines
7.9 KiB
C
#ident "@(#)ledma.c 1.1 94/10/31 SMI"
|
|
|
|
/*
|
|
* Copyright (c) 1991 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include "le.h"
|
|
#if NLE > 0
|
|
|
|
/*
|
|
* DMA2 "ledma" driver.
|
|
*
|
|
* This driver identifies "ledma", maps in the DMA2 E-CSR register,
|
|
* attaches the child node "le", and calls the le driver routine
|
|
* leopsadd() to add an entry to the le driver ops linked list
|
|
* indicating the DMA2-specific init() and intr() routines
|
|
* to be called by the le driver from leinit() and leintr().
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/time.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/debug.h>
|
|
#include <sundev/mbvar.h>
|
|
#include <sun/autoconf.h>
|
|
#include <machine/mmu.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <sunif/if_lereg.h>
|
|
#include <sunif/if_levar.h>
|
|
|
|
#if defined(SUN4M_35)
|
|
#include <vm/hat.h>
|
|
#include <machine/vm_hat.h>
|
|
#include <vm/as.h>
|
|
#include <vm/seg.h>
|
|
#include <vm/page.h>
|
|
#include <machine/pte.h>
|
|
int lebufnocache = 1;
|
|
|
|
#include <machine/cpu.h>
|
|
#include <machine/iommu.h>
|
|
#endif
|
|
|
|
/*
|
|
* DMA2 E-CSR bit definitions.
|
|
* Refer to the Campus II DMA2 Chip Specification for details.
|
|
*/
|
|
#define E_INT_PEND (1 << 0) /* e_irq_ is active */
|
|
#define E_ERR_PEND (1 << 1) /* memory time-out, protection, */
|
|
/* or parity */
|
|
#define E_DRAINING (3 << 2) /* both bits set when e-cache is */
|
|
/* draining */
|
|
#define E_INT_EN (1 << 4) /* enable intr on E_INT_PEND or */
|
|
/* E_ERR_PEND */
|
|
#define E_INVALIDATE (1 << 5) /* invalidate e-cache */
|
|
#define E_SLAVE_ERR (1 << 6) /* slave access size error */
|
|
#define E_RESET (1 << 7) /* reset lance interface & */
|
|
/* invalidate e-cache */
|
|
#define E_DRAIN (1 << 10) /* drain e-cache */
|
|
#define E_DSBL_WR_DRN (1 << 11) /* disable e-cache drain on */
|
|
/* lance descr writes */
|
|
#define E_DSBL_RD_DRN (1 << 12) /* disable e-cache drain on */
|
|
/* slave reads */
|
|
#define E_ILACC (1 << 15) /* modify dma cycle for ilacc */
|
|
/* (untested) */
|
|
#define E_DSBL_BUF_WR (1 << 16) /* disable slave write buffer to */
|
|
/* lance */
|
|
#define E_DSBL_WR_INVAL (1 << 17) /* disable slave write auto */
|
|
/* e-cache invalidate */
|
|
#define E_BURST_SIZE (3 << 18) /* sbus burst sizes mask */
|
|
#define E_ALE_AS (1 << 20) /* define pin 35 active high/low */
|
|
#define E_LOOP_TEST (1 << 21) /* enable external ethernet loopback */
|
|
#define E_TP_AUI (1 << 22) /* select TP or AUI */
|
|
#define E_DEV_ID (0xf << 28) /* device id mask */
|
|
|
|
#define E_DMA2_ID 0xa0000000 /* DMA2 E-CSR device id */
|
|
|
|
#define E_BURST_MASK 0x000C0000 /* dma2 e-csr burst size mask */
|
|
|
|
/*
|
|
* DMA2 E-CSR SBus burst sizes
|
|
*/
|
|
#define E_BURST16 (0 << 18) /* 16 byte SBus bursts */
|
|
#define E_BURST32 (1 << 18) /* 32 byte SBus bursts */
|
|
|
|
|
|
static int ledmaidentify(), ledmaattach(), ledmainit(), ledmaintr();
|
|
struct dev_ops ledma_ops = {
|
|
1,
|
|
ledmaidentify,
|
|
ledmaattach
|
|
};
|
|
|
|
static int
|
|
ledmaidentify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "ledma") == 0)
|
|
return (1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
int ledmabufsize = 128 * 1024; /* 128K ledma buffer size */
|
|
|
|
static int
|
|
ledmaattach(dev)
|
|
register struct dev_info *dev;
|
|
{
|
|
static int unit = 0;
|
|
u_int *csr;
|
|
caddr_t membase = NULL;
|
|
caddr_t iobase = NULL;
|
|
struct leops *lop;
|
|
#if defined(SUN4M_35)
|
|
caddr_t kp;
|
|
struct page *pp;
|
|
union ptpe * ptpe;
|
|
extern struct as kas;
|
|
extern struct iommuinfo iommu_info;
|
|
#endif
|
|
|
|
dev->devi_unit = unit++;
|
|
|
|
if ((dev->devi_nreg > 1) ||
|
|
(dev->devi_reg->reg_size < sizeof (u_int))) {
|
|
printf("ledma%d: bad regs specification\n", dev->devi_unit);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Map in the E-CSR control register.
|
|
*/
|
|
csr = (u_int *) map_regs(dev->devi_reg->reg_addr, sizeof (u_int),
|
|
dev->devi_reg->reg_bustype);
|
|
if (csr == NULL) {
|
|
printf("ledma%d: unable to map registers\n", dev->devi_unit);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Sanity check.
|
|
*/
|
|
if ((*csr & E_DEV_ID) != E_DMA2_ID) {
|
|
printf("ledma%d: invalid DMA2 E-CSR format: 0x%x\n",
|
|
dev->devi_unit, *csr);
|
|
#if defined(SUN4M_35)
|
|
if (iommu_info.ccoher == 0)
|
|
printf("ignored ...\n");
|
|
else
|
|
return (-1);
|
|
#else
|
|
return (-1);
|
|
#endif
|
|
}
|
|
|
|
dev->devi_data = (caddr_t) csr;
|
|
|
|
#ifdef IOMMU
|
|
/*
|
|
* For platforms with an IOMMU, such as those based on Sun-4M,
|
|
* we allocate some kernel memory, statically IO map it, and
|
|
* treat it as a Programmed I/O device (copy to/from) rather
|
|
* than dynamically IO mapping and unmapping each buffer to
|
|
* transmit and receive. This may change if the cost of
|
|
* IO mapping gets cheaper but unfortunately it's the best
|
|
* tradeoff for now.
|
|
*
|
|
* 'membase' is the CPU-addressable memory base
|
|
* 'iobase' is the device-addressable memory base
|
|
*/
|
|
if ((membase = kmem_alloc((u_int) ledmabufsize)) == NULL) {
|
|
printf("ledma%d: kmem_alloc(%d) failed\n", dev->devi_unit,
|
|
ledmabufsize);
|
|
return (-1);
|
|
}
|
|
/* NOSTRICT */
|
|
if ((iobase = (caddr_t) mb_nbmapalloc(dvmamap, membase,
|
|
ledmabufsize, MDR_BIGSBUS, (int(*)())0, (caddr_t) 0)) == NULL) {
|
|
printf("ledma%d: mb_mapalloc failed\n", dev->devi_unit);
|
|
kmem_free(membase, (u_int) ledmabufsize);
|
|
return (-1);
|
|
}
|
|
|
|
#if defined(SUN4M_35)
|
|
if (iommu_info.ccoher == 0) {
|
|
for (kp = membase; (u_int)kp < ((u_int)membase + ledmabufsize) ;
|
|
(u_int)kp += MMU_PAGESIZE) {
|
|
ptpe = hat_ptefind( &kas, kp);
|
|
pp = ptetopp(&ptpe->pte);
|
|
(void)hat_pagecache(pp, 1);
|
|
}
|
|
}
|
|
#endif
|
|
#endif IOMMU
|
|
|
|
report_dev(dev);
|
|
attach_devs(dev); /* attach child "le" node */
|
|
|
|
/*
|
|
* Add entry to le driver ops list
|
|
*/
|
|
if ((lop = (struct leops *) kmem_alloc(sizeof (struct leops)))
|
|
== NULL) {
|
|
printf("lebuf%d: kmem_alloc failed!\n", dev->devi_unit);
|
|
return (-1);
|
|
}
|
|
lop->lo_dev = dev->devi_slaves;
|
|
lop->lo_flags = LO_PIO | LO_IOADDR;
|
|
lop->lo_membase = membase;
|
|
lop->lo_iobase = iobase;
|
|
lop->lo_size = ledmabufsize;
|
|
lop->lo_init = ledmainit;
|
|
lop->lo_intr = ledmaintr;
|
|
lop->lo_arg = (caddr_t) dev;
|
|
leopsadd(lop);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int le_last_tp_aui;
|
|
/*
|
|
* "Exported" by address and called by the le driver.
|
|
*/
|
|
static
|
|
ledmainit(dev, tp_aui, init)
|
|
struct dev_info *dev;
|
|
int tp_aui;
|
|
int init;
|
|
{
|
|
u_int *csr = (u_int *) dev->devi_data;
|
|
struct dev_info *dip;
|
|
int burst;
|
|
u_int tcsr;
|
|
|
|
/*
|
|
* A nonzero tp_aui argument means Twisted Pair.
|
|
* Zero means AUI.
|
|
*/
|
|
if (tp_aui)
|
|
tp_aui = E_TP_AUI;
|
|
|
|
|
|
/*
|
|
* Hard reset.
|
|
* Readback flushes any system write buffers.
|
|
*/
|
|
if (init) {
|
|
*csr = E_RESET;
|
|
|
|
if (*csr) /* readback */
|
|
;
|
|
|
|
if (tp_aui)
|
|
/*
|
|
* AT&T 7213 requires us to wait for 20ms during the
|
|
* switch between TP and AUI.
|
|
*/
|
|
DELAY(20000);
|
|
else
|
|
DELAY(100);
|
|
|
|
/*
|
|
* Determine proper SBus burst size to use.
|
|
*/
|
|
for (dip = dev->devi_parent; dip != top_devinfo;
|
|
dip = dip->devi_parent)
|
|
if (strcmp(dip->devi_name, "sbus") == 0)
|
|
break;
|
|
if (dip == top_devinfo) {
|
|
printf("ledmainit: parent sbus node not found!\n");
|
|
return;
|
|
}
|
|
burst = (getprop(dip->devi_nodeid, "burst-sizes", 0) & 32) ?
|
|
E_BURST32 : E_BURST16;
|
|
|
|
/*
|
|
* Initialize E_CSR.
|
|
*/
|
|
*csr = E_INT_EN | tp_aui | E_INVALIDATE | E_DSBL_RD_DRN |
|
|
E_DSBL_WR_INVAL | burst;
|
|
|
|
if (*csr) /* readback */
|
|
;
|
|
|
|
DELAY(20000);
|
|
le_last_tp_aui = tp_aui;
|
|
} else
|
|
/*
|
|
* Avoid to delay 20ms unless we actually need to
|
|
* switch the TP/AUI. Here we assume that the driver has
|
|
* already called ledmainit() at least once with the
|
|
* init being 1.
|
|
*/
|
|
if (tp_aui != le_last_tp_aui) {
|
|
|
|
/*
|
|
* Just set the TP/AUI bits.
|
|
*/
|
|
tcsr = *csr & ~E_TP_AUI;
|
|
tcsr |= tp_aui;
|
|
*csr = tcsr;
|
|
|
|
if (*csr) /* readback */
|
|
;
|
|
|
|
DELAY(20000);
|
|
le_last_tp_aui = tp_aui;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* "Exported" by address and called by the le driver.
|
|
* Return 1 if "serviced", 0 otherwise.
|
|
*/
|
|
static
|
|
ledmaintr(dev)
|
|
struct dev_info *dev;
|
|
{
|
|
u_int *csr = (u_int *) dev->devi_data;
|
|
|
|
if (*csr & (E_ERR_PEND | E_SLAVE_ERR)) {
|
|
if (*csr & E_ERR_PEND)
|
|
printf("DMA: memory time out or protection error!\n");
|
|
if (*csr & E_SLAVE_ERR)
|
|
printf("DMA: LANCE register slave access error!\n");
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
#endif NLE > 0
|