#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hd64570.c,v 1.62 2024/09/14 21:22:37 andvar Exp $");
#include "opt_inet.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/kernel.h>
#include <net/if.h>
#include <net/if_types.h>
#if defined(INET) || defined(INET6)
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#ifdef INET6
#include <netinet6/in6_var.h>
#endif
#endif
#include <net/bpf.h>
#include <sys/cpu.h>
#include <sys/bus.h>
#include <sys/intr.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/ic/hd64570reg.h>
#include <dev/ic/hd64570var.h>
#define SCA_DEBUG_RX 0x0001
#define SCA_DEBUG_TX 0x0002
#define SCA_DEBUG_CISCO 0x0004
#define SCA_DEBUG_DMA 0x0008
#define SCA_DEBUG_RXPKT 0x0010
#define SCA_DEBUG_TXPKT 0x0020
#define SCA_DEBUG_INTR 0x0040
#define SCA_DEBUG_CLOCK 0x0080
#if 0
#define SCA_DEBUG_LEVEL ( 0xFFFF )
#else
#define SCA_DEBUG_LEVEL 0
#endif
u_int32_t sca_debug = SCA_DEBUG_LEVEL;
#if SCA_DEBUG_LEVEL > 0
#define SCA_DPRINTF(l, x) do { \
if ((l) & sca_debug) \
printf x;\
} while (0)
#else
#define SCA_DPRINTF(l, x)
#endif
#if 0
#define SCA_USE_FASTQ
#endif
static inline void msci_write_1(sca_port_t *, u_int, u_int8_t);
static inline u_int8_t msci_read_1(sca_port_t *, u_int);
static inline void dmac_write_1(sca_port_t *, u_int, u_int8_t);
static inline void dmac_write_2(sca_port_t *, u_int, u_int16_t);
static inline u_int8_t dmac_read_1(sca_port_t *, u_int);
static inline u_int16_t dmac_read_2(sca_port_t *, u_int);
static void sca_msci_init(struct sca_softc *, sca_port_t *);
static void sca_dmac_init(struct sca_softc *, sca_port_t *);
static void sca_dmac_rxinit(sca_port_t *);
static int sca_dmac_intr(sca_port_t *, u_int8_t);
static int sca_msci_intr(sca_port_t *, u_int8_t);
static void sca_get_packets(sca_port_t *);
static int sca_frame_avail(sca_port_t *);
static void sca_frame_process(sca_port_t *);
static void sca_frame_read_done(sca_port_t *);
static void sca_port_starttx(sca_port_t *);
static void sca_port_up(sca_port_t *);
static void sca_port_down(sca_port_t *);
static int sca_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
const struct rtentry *);
static int sca_ioctl(struct ifnet *, u_long, void *);
static void sca_start(struct ifnet *);
static void sca_watchdog(struct ifnet *);
static struct mbuf *sca_mbuf_alloc(struct sca_softc *, void *, u_int);
#if SCA_DEBUG_LEVEL > 0
static void sca_frame_print(sca_port_t *, sca_desc_t *, u_int8_t *);
#endif
#define sca_read_1(sc, reg) (sc)->sc_read_1(sc, reg)
#define sca_read_2(sc, reg) (sc)->sc_read_2(sc, reg)
#define sca_write_1(sc, reg, val) (sc)->sc_write_1(sc, reg, val)
#define sca_write_2(sc, reg, val) (sc)->sc_write_2(sc, reg, val)
#define sca_page_addr(sc, addr) ((bus_addr_t)(u_long)(addr) & (sc)->scu_pagemask)
static inline void
msci_write_1(sca_port_t *scp, u_int reg, u_int8_t val)
{
sca_write_1(scp->sca, scp->msci_off + reg, val);
}
static inline u_int8_t
msci_read_1(sca_port_t *scp, u_int reg)
{
return sca_read_1(scp->sca, scp->msci_off + reg);
}
static inline void
dmac_write_1(sca_port_t *scp, u_int reg, u_int8_t val)
{
sca_write_1(scp->sca, scp->dmac_off + reg, val);
}
static inline void
dmac_write_2(sca_port_t *scp, u_int reg, u_int16_t val)
{
sca_write_2(scp->sca, scp->dmac_off + reg, val);
}
static inline u_int8_t
dmac_read_1(sca_port_t *scp, u_int reg)
{
return sca_read_1(scp->sca, scp->dmac_off + reg);
}
static inline u_int16_t
dmac_read_2(sca_port_t *scp, u_int reg)
{
return sca_read_2(scp->sca, scp->dmac_off + reg);
}
#if SCA_DEBUG_LEVEL > 0
static inline u_int16_t
sca_desc_read_chainp(struct sca_softc *sc, struct sca_desc *dp)
{
if (sc->sc_usedma)
return ((dp)->sd_chainp);
return (bus_space_read_2(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_chainp)));
}
#endif
static inline void
sca_desc_write_chainp(struct sca_softc *sc, struct sca_desc *dp, u_int16_t cp)
{
if (sc->sc_usedma)
(dp)->sd_chainp = cp;
else
bus_space_write_2(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp)
+ offsetof(struct sca_desc, sd_chainp), cp);
}
#if SCA_DEBUG_LEVEL > 0
static inline u_int32_t
sca_desc_read_bufp(struct sca_softc *sc, struct sca_desc *dp)
{
u_int32_t address;
if (sc->sc_usedma)
address = dp->sd_bufp | dp->sd_hbufp << 16;
else {
address = bus_space_read_2(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_bufp));
address |= bus_space_read_1(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp)
+ offsetof(struct sca_desc, sd_hbufp)) << 16;
}
return (address);
}
#endif
static inline void
sca_desc_write_bufp(struct sca_softc *sc, struct sca_desc *dp, u_int32_t bufp)
{
if (sc->sc_usedma) {
dp->sd_bufp = bufp & 0xFFFF;
dp->sd_hbufp = (bufp & 0x00FF0000) >> 16;
} else {
bus_space_write_2(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_bufp),
bufp & 0xFFFF);
bus_space_write_1(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_hbufp),
(bufp & 0x00FF0000) >> 16);
}
}
static inline u_int16_t
sca_desc_read_buflen(struct sca_softc *sc, struct sca_desc *dp)
{
if (sc->sc_usedma)
return ((dp)->sd_buflen);
return (bus_space_read_2(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_buflen)));
}
static inline void
sca_desc_write_buflen(struct sca_softc *sc, struct sca_desc *dp, u_int16_t len)
{
if (sc->sc_usedma)
(dp)->sd_buflen = len;
else
bus_space_write_2(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp)
+ offsetof(struct sca_desc, sd_buflen), len);
}
static inline u_int8_t
sca_desc_read_stat(struct sca_softc *sc, struct sca_desc *dp)
{
if (sc->sc_usedma)
return ((dp)->sd_stat);
return (bus_space_read_1(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_stat)));
}
static inline void
sca_desc_write_stat(struct sca_softc *sc, struct sca_desc *dp, u_int8_t stat)
{
if (sc->sc_usedma)
(dp)->sd_stat = stat;
else
bus_space_write_1(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_stat),
stat);
}
void
sca_init(struct sca_softc *sc)
{
if (sc->sc_numports < 1 || sc->sc_numports > 2)
panic("sca can\'t handle more than 2 or less than 1 ports");
sca_write_1(sc, SCA_DMER, 0);
sca_write_1(sc, SCA_IER0, 0);
sca_write_1(sc, SCA_IER1, 0);
sca_write_1(sc, SCA_IER2, 0);
sca_write_1(sc, SCA_ITCR,
SCA_ITCR_INTR_PRI_MSCI | SCA_ITCR_ACK_NONE | SCA_ITCR_VOUT_IVR);
#if 0
sca_write_1(sc, SCA_IVR, 0x40);
sca_write_1(sc, SCA_IMVR, 0x40);
#endif
sca_write_1(sc, SCA_PABR0, 0);
sca_write_1(sc, SCA_PABR1, 0);
sca_write_1(sc, SCA_WCRL, 0);
sca_write_1(sc, SCA_WCRM, 0);
sca_write_1(sc, SCA_WCRH, 0);
sca_write_1(sc, SCA_PCR, SCA_PCR_PR2);
sca_write_1(sc, SCA_DSR0 + SCA_DMAC_OFF_0, 0);
sca_write_1(sc, SCA_DCR0 + SCA_DMAC_OFF_0, SCA_DCR_ABRT);
sca_write_1(sc, SCA_DSR1 + SCA_DMAC_OFF_0, 0);
sca_write_1(sc, SCA_DCR1 + SCA_DMAC_OFF_0, SCA_DCR_ABRT);
sca_write_1(sc, SCA_DSR0 + SCA_DMAC_OFF_1, 0);
sca_write_1(sc, SCA_DCR0 + SCA_DMAC_OFF_1, SCA_DCR_ABRT);
sca_write_1(sc, SCA_DSR1 + SCA_DMAC_OFF_1, 0);
sca_write_1(sc, SCA_DCR1 + SCA_DMAC_OFF_1, SCA_DCR_ABRT);
sca_write_1(sc, SCA_DMER, SCA_DMER_EN);
}
void
sca_port_attach(struct sca_softc *sc, u_int port)
{
struct timeval now;
sca_port_t *scp = &sc->sc_ports[port];
struct ifnet *ifp;
static u_int ntwo_unit = 0;
scp->sca = sc;
scp->sp_port = port;
if (port == 0) {
scp->msci_off = SCA_MSCI_OFF_0;
scp->dmac_off = SCA_DMAC_OFF_0;
if(sc->sc_parent != NULL)
ntwo_unit = device_unit(sc->sc_parent) * 2 + 0;
else
ntwo_unit = 0;
} else {
scp->msci_off = SCA_MSCI_OFF_1;
scp->dmac_off = SCA_DMAC_OFF_1;
if(sc->sc_parent != NULL)
ntwo_unit = device_unit(sc->sc_parent) * 2 + 1;
else
ntwo_unit = 1;
}
sca_msci_init(sc, scp);
sca_dmac_init(sc, scp);
ifp = &scp->sp_if;
snprintf(ifp->if_xname, sizeof(ifp->if_xname), "ntwo%d", ntwo_unit);
ifp->if_softc = scp;
ifp->if_mtu = SCA_MTU;
ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
ifp->if_type = IFT_PTPSERIAL;
ifp->if_hdrlen = HDLC_HDRLEN;
ifp->if_ioctl = sca_ioctl;
ifp->if_output = sca_output;
ifp->if_watchdog = sca_watchdog;
ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
scp->linkq.ifq_maxlen = 5;
#ifdef SCA_USE_FASTQ
scp->fastq.ifq_maxlen = IFQ_MAXLEN;
#endif
IFQ_SET_READY(&ifp->if_snd);
if_attach(ifp);
if_deferred_start_init(ifp, NULL);
if_alloc_sadl(ifp);
bpf_attach(ifp, DLT_HDLC, HDLC_HDRLEN);
bpf_mtap_softint_init(ifp);
if (sc->sc_parent == NULL)
printf("%s: port %d\n", ifp->if_xname, port);
else
printf("%s at %s port %d\n",
ifp->if_xname, device_xname(sc->sc_parent), port);
getmicrotime(&now);
scp->cka_lasttx = now.tv_usec;
scp->cka_lastrx = 0;
}
#if 0
static u_int8_t
sca_msci_get_baud_rate_values(u_int32_t hz, u_int8_t *tmcp)
{
u_int32_t tmc, div;
u_int32_t clock;
clock = clock = 9830400 >> 1;
div = 0;
while ((tmc = clock / hz) > 256 || (tmc == 256 && (clock / tmc) > hz)) {
clock >>= 1;
div++;
}
if (clock / tmc > hz)
tmc++;
if (!tmc)
tmc = 1;
if (div > SCA_RXS_DIV_512) {
div = SCA_RXS_DIV_512;
tmc = 0;
}
*tmcp = (tmc & 0xFF);
return (div & 0xFF);
}
#endif
static void
sca_msci_init(struct sca_softc *sc, sca_port_t *scp)
{
msci_write_1(scp, SCA_CMD0, SCA_CMD_RESET);
msci_write_1(scp, SCA_MD00,
( SCA_MD0_CRC_1
| SCA_MD0_CRC_CCITT
| SCA_MD0_CRC_ENABLE
| SCA_MD0_MODE_HDLC));
#if 0
msci_write_1(scp, SCA_CMD0, SCA_CMD_RXRESET);
#endif
msci_write_1(scp, SCA_MD10, SCA_MD1_NOADDRCHK);
msci_write_1(scp, SCA_MD20,
(SCA_MD2_DUPLEX | SCA_MD2_ADPLLx8 | SCA_MD2_NRZ));
msci_write_1(scp, SCA_CMD0, SCA_CMD_RXRESET);
msci_write_1(scp, SCA_CTL0,
(SCA_CTL_IDLC_PATTERN
| SCA_CTL_UDRNC_AFTER_FCS
| SCA_CTL_RTS_LOW));
msci_write_1(scp, SCA_CMD0, SCA_CMD_TXRESET);
msci_write_1(scp, SCA_RXS0, scp->sp_rxs);
msci_write_1(scp, SCA_TXS0, scp->sp_txs);
msci_write_1(scp, SCA_TMC0, scp->sp_tmc);
sc->sc_clock_callback(sc->sc_aux, scp->sp_port, scp->sp_eclock);
#if 0
msci_write_1(scp, SCA_IE00, 0);
msci_write_1(scp, SCA_IE10, 0);
#else
msci_write_1(scp, SCA_IE10, SCA_ST1_UDRN);
msci_write_1(scp, SCA_IE00, SCA_ST0_TXINT);
#endif
msci_write_1(scp, SCA_IE20, 0);
msci_write_1(scp, SCA_FIE0, 0);
msci_write_1(scp, SCA_SA00, 0);
msci_write_1(scp, SCA_SA10, 0);
msci_write_1(scp, SCA_IDL0, 0x7e);
msci_write_1(scp, SCA_RRC0, 0x0e);
msci_write_1(scp, SCA_TRC00, 0x00);
msci_write_1(scp, SCA_TRC10, 0x1f);
}
static void
sca_dmac_init(struct sca_softc *sc, sca_port_t *scp)
{
sca_desc_t *desc;
u_int32_t desc_p;
u_int32_t buf_p;
int i;
if (sc->sc_usedma)
bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0, sc->scu_allocsize,
BUS_DMASYNC_PREWRITE);
else {
sc->scu_page_on(sc);
sc->scu_set_page(sc, scp->sp_txdesc_p);
}
desc = scp->sp_txdesc;
desc_p = scp->sp_txdesc_p;
buf_p = scp->sp_txbuf_p;
scp->sp_txcur = 0;
scp->sp_txinuse = 0;
#ifdef DEBUG
if ((desc_p & 0xffff0000) !=
((desc_p + sizeof(*desc) * scp->sp_ntxdesc) & 0xffff0000))
panic("sca: tx descriptors cross architecural boundary");
if ((buf_p & 0xff000000) !=
((buf_p + SCA_BSIZE * scp->sp_ntxdesc) & 0xff000000))
panic("sca: tx buffers cross architecural boundary");
#endif
for (i = 0 ; i < scp->sp_ntxdesc ; i++) {
desc_p += sizeof(sca_desc_t);
sca_desc_write_chainp(sc, desc, desc_p & 0x0000ffff);
sca_desc_write_bufp(sc, desc, buf_p);
sca_desc_write_buflen(sc, desc, SCA_BSIZE);
sca_desc_write_stat(sc, desc, 0);
desc++;
buf_p += SCA_BSIZE;
}
sca_desc_write_chainp(sc, desc - 1, scp->sp_txdesc_p & 0x0000ffff);
dmac_write_1(scp, SCA_DSR1, 0);
dmac_write_1(scp, SCA_DCR1, SCA_DCR_ABRT);
dmac_write_1(scp, SCA_DMR1, SCA_DMR_TMOD | SCA_DMR_NF);
dmac_write_1(scp, SCA_DIR1,
(SCA_DIR_EOM | SCA_DIR_EOT | SCA_DIR_BOF | SCA_DIR_COF));
dmac_write_1(scp, SCA_CPB1,
(u_int8_t)((scp->sp_txdesc_p & 0x00ff0000) >> 16));
if (!sc->sc_usedma)
sc->scu_set_page(sc, scp->sp_rxdesc_p);
desc = scp->sp_rxdesc;
desc_p = scp->sp_rxdesc_p;
buf_p = scp->sp_rxbuf_p;
#ifdef DEBUG
if ((desc_p & 0xffff0000) !=
((desc_p + sizeof(*desc) * scp->sp_nrxdesc) & 0xffff0000))
panic("sca: rx descriptors cross architecural boundary");
if ((buf_p & 0xff000000) !=
((buf_p + SCA_BSIZE * scp->sp_nrxdesc) & 0xff000000))
panic("sca: rx buffers cross architecural boundary");
#endif
for (i = 0 ; i < scp->sp_nrxdesc; i++) {
desc_p += sizeof(sca_desc_t);
sca_desc_write_chainp(sc, desc, desc_p & 0x0000ffff);
sca_desc_write_bufp(sc, desc, buf_p);
sca_desc_write_buflen(sc, desc, 0);
sca_desc_write_stat(sc, desc, 0);
desc++;
buf_p += SCA_BSIZE;
}
sca_desc_write_chainp(sc, desc - 1, scp->sp_rxdesc_p & 0x0000ffff);
sca_dmac_rxinit(scp);
if (sc->sc_usedma)
bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam,
0, sc->scu_allocsize, BUS_DMASYNC_POSTWRITE);
else
sc->scu_page_off(sc);
}
static void
sca_dmac_rxinit(sca_port_t *scp)
{
dmac_write_1(scp, SCA_DSR0, 0);
dmac_write_1(scp, SCA_DCR0, SCA_DCR_ABRT);
dmac_write_1(scp, SCA_DMR0, SCA_DMR_TMOD | SCA_DMR_NF);
dmac_write_2(scp, SCA_BFLL0, SCA_BSIZE);
scp->sp_rxstart = 0;
scp->sp_rxend = scp->sp_nrxdesc - 1;
dmac_write_1(scp, SCA_CPB0,
(u_int8_t)((scp->sp_rxdesc_p & 0x00ff0000) >> 16));
dmac_write_2(scp, SCA_CDAL0, (u_int16_t)(scp->sp_rxdesc_p & 0xffff));
dmac_write_2(scp, SCA_EDAL0, (u_int16_t)
(scp->sp_rxdesc_p + (sizeof(sca_desc_t) * scp->sp_rxend)));
dmac_write_1(scp, SCA_DIR0,
(SCA_DIR_EOT | SCA_DIR_EOM | SCA_DIR_BOF | SCA_DIR_COF));
dmac_write_1(scp, SCA_DSR0, SCA_DSR_DE);
}
static int
sca_output(
struct ifnet *ifp,
struct mbuf *m,
const struct sockaddr *dst,
const struct rtentry *rt0)
{
struct hdlc_header *hdlc;
struct ifqueue *ifq = NULL;
int s, error, len;
short mflags;
error = 0;
if ((ifp->if_flags & IFF_UP) != IFF_UP) {
error = ENETDOWN;
goto bad;
}
IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
#ifdef SCA_USE_FASTQ
if ((mtod(m, struct ip *)->ip_tos & IPTOS_LOWDELAY)
== IPTOS_LOWDELAY)
ifq = &((sca_port_t *)ifp->if_softc)->fastq;
#endif
M_PREPEND(m, sizeof(struct hdlc_header), M_DONTWAIT);
if (m == 0)
return (ENOBUFS);
hdlc = mtod(m, struct hdlc_header *);
hdlc->h_proto = htons(HDLC_PROTOCOL_IP);
break;
#endif
#ifdef INET6
case AF_INET6:
M_PREPEND(m, sizeof(struct hdlc_header), M_DONTWAIT);
if (m == 0)
return (ENOBUFS);
hdlc = mtod(m, struct hdlc_header *);
hdlc->h_proto = htons(HDLC_PROTOCOL_IPV6);
break;
#endif
default:
printf("%s: address family %d unsupported\n",
ifp->if_xname, dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
if ((m->m_flags & (M_BCAST | M_MCAST)) != 0)
hdlc->h_addr = CISCO_MULTICAST;
else
hdlc->h_addr = CISCO_UNICAST;
hdlc->h_resv = 0;
mflags = m->m_flags;
len = m->m_pkthdr.len;
s = splnet();
if (ifq != NULL) {
if (IF_QFULL(ifq)) {
IF_DROP(ifq);
m_freem(m);
error = ENOBUFS;
} else
IF_ENQUEUE(ifq, m);
} else
IFQ_ENQUEUE(&ifp->if_snd, m, error);
net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
if (error != 0) {
if_statinc_ref(ifp, nsr, if_oerrors);
if_statinc_ref(ifp, nsr, if_collisions);
IF_STAT_PUTREF(ifp);
splx(s);
return (error);
}
if_statadd_ref(ifp, nsr, if_obytes, len);
if (mflags & M_MCAST)
if_statinc_ref(ifp, nsr, if_omcasts);
IF_STAT_PUTREF(ifp);
sca_start(ifp);
splx(s);
return (error);
bad:
m_freem(m);
return (error);
}
static int
sca_ioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct ifreq *ifr;
struct ifaddr *ifa;
int error;
int s;
s = splnet();
ifr = (struct ifreq *)data;
ifa = (struct ifaddr *)data;
error = 0;
switch (cmd) {
case SIOCINITIFADDR:
switch(ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
#endif
#ifdef INET6
case AF_INET6:
#endif
#if defined(INET) || defined(INET6)
ifp->if_flags |= IFF_UP;
sca_port_up(ifp->if_softc);
break;
#endif
default:
error = EAFNOSUPPORT;
break;
}
break;
case SIOCSIFDSTADDR:
#ifdef INET
if (ifa->ifa_addr->sa_family == AF_INET)
break;
#endif
#ifdef INET6
if (ifa->ifa_addr->sa_family == AF_INET6)
break;
#endif
error = EAFNOSUPPORT;
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
if (ifr == 0) {
error = EAFNOSUPPORT;
break;
}
switch (ifreq_getaddr(cmd, ifr)->sa_family) {
#ifdef INET
case AF_INET:
break;
#endif
#ifdef INET6
case AF_INET6:
break;
#endif
default:
error = EAFNOSUPPORT;
break;
}
break;
case SIOCSIFFLAGS:
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
break;
if (ifr->ifr_flags & IFF_UP) {
ifp->if_flags |= IFF_UP;
sca_port_up(ifp->if_softc);
} else {
ifp->if_flags &= ~IFF_UP;
sca_port_down(ifp->if_softc);
}
break;
default:
error = ifioctl_common(ifp, cmd, data);
}
splx(s);
return error;
}
static void
sca_start(struct ifnet *ifp)
{
sca_port_t *scp = ifp->if_softc;
struct sca_softc *sc = scp->sca;
struct mbuf *m, *mb_head;
sca_desc_t *desc;
u_int8_t *buf, stat;
u_int32_t buf_p;
int nexttx;
int trigger_xmit;
u_int len;
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: enter start\n"));
#ifdef oldcode
if ((scp->sp_txinuse >= (scp->sp_ntxdesc - 1))
|| ((ifp->if_flags & IFF_OACTIVE) == IFF_OACTIVE))
return;
#else
if (scp->sp_txinuse
|| ((ifp->if_flags & IFF_OACTIVE) == IFF_OACTIVE))
return;
#endif
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: txinuse %d\n", scp->sp_txinuse));
if (sc->sc_usedma)
bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam,
0, sc->scu_allocsize,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
else {
sc->scu_page_on(sc);
sc->scu_set_page(sc, scp->sp_txdesc_p);
}
trigger_xmit = 0;
txloop:
IF_DEQUEUE(&scp->linkq, mb_head);
if (mb_head == NULL)
#ifdef SCA_USE_FASTQ
IF_DEQUEUE(&scp->fastq, mb_head);
if (mb_head == NULL)
#endif
IFQ_DEQUEUE(&ifp->if_snd, mb_head);
if (mb_head == NULL)
goto start_xmit;
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: got mbuf\n"));
#ifdef oldcode
if (scp->txinuse != 0) {
desc = &scp->sp_txdesc[scp->txcur];
stat = sca_desc_read_stat(sc, desc);
sca_desc_write_stat(sc, desc, stat & ~SCA_DESC_EOT);
nexttx = (scp->sp_txcur + 1) % scp->sp_ntxdesc;
} else
nexttx = 0;
#endif
if (scp->sp_txinuse)
nexttx = (scp->sp_txcur + 1) % scp->sp_ntxdesc;
else
nexttx = 0;
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: nexttx %d\n", nexttx));
buf = scp->sp_txbuf + SCA_BSIZE * nexttx;
buf_p = scp->sp_txbuf_p + SCA_BSIZE * nexttx;
desc = &scp->sp_txdesc[nexttx];
sca_desc_write_bufp(sc, desc, buf_p);
len = 0;
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: buf %x buf_p %x\n", (u_int)buf, buf_p));
#if 0
X
#endif
for (m = mb_head ; m != NULL ; m = m->m_next) {
if (m->m_len != 0) {
len += m->m_len;
if (len > SCA_BSIZE) {
m_freem(mb_head);
goto txloop;
}
SCA_DPRINTF(SCA_DEBUG_TX,
("TX: about to mbuf len %d\n", m->m_len));
if (sc->sc_usedma)
memcpy(buf, mtod(m, u_int8_t *), m->m_len);
else
bus_space_write_region_1(sc->scu_memt,
sc->scu_memh, sca_page_addr(sc, buf_p),
mtod(m, u_int8_t *), m->m_len);
buf += m->m_len;
buf_p += m->m_len;
}
}
sca_desc_write_buflen(sc, desc, len);
sca_desc_write_stat(sc, desc, SCA_DESC_EOM);
if_statinc(ifp, if_opackets);
bpf_mtap(ifp, mb_head, BPF_D_OUT);
m_freem(mb_head);
scp->sp_txcur = nexttx;
scp->sp_txinuse++;
trigger_xmit = 1;
SCA_DPRINTF(SCA_DEBUG_TX,
("TX: inuse %d index %d\n", scp->sp_txinuse, scp->sp_txcur));
#ifdef oldcode
if (scp->sp_txinuse < (scp->sp_ntxdesc - 1))
#endif
if (scp->sp_txinuse < scp->sp_ntxdesc)
goto txloop;
start_xmit:
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: trigger_xmit %d\n", trigger_xmit));
if (trigger_xmit != 0) {
desc = &scp->sp_txdesc[scp->sp_txcur];
stat = sca_desc_read_stat(sc, desc);
sca_desc_write_stat(sc, desc, stat | SCA_DESC_EOT);
}
if (sc->sc_usedma)
bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0,
sc->scu_allocsize,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
if (trigger_xmit != 0)
sca_port_starttx(scp);
if (!sc->sc_usedma)
sc->scu_page_off(sc);
}
static void
sca_watchdog(struct ifnet *ifp)
{
}
int
sca_hardintr(struct sca_softc *sc)
{
u_int8_t isr0, isr1, isr2;
int ret;
ret = 0;
SCA_DPRINTF(SCA_DEBUG_INTR, ("sca_hardintr entered\n"));
while (1) {
isr0 = sca_read_1(sc, SCA_ISR0);
isr1 = sca_read_1(sc, SCA_ISR1);
isr2 = sca_read_1(sc, SCA_ISR2);
if (isr0 == 0 && isr1 == 0 && isr2 == 0)
break;
SCA_DPRINTF(SCA_DEBUG_INTR,
("isr0 = %02x, isr1 = %02x, isr2 = %02x\n",
isr0, isr1, isr2));
if (isr1 & 0x0f)
ret += sca_dmac_intr(&sc->sc_ports[0],
isr1 & 0x0f);
if (isr1 & 0xf0)
ret += sca_dmac_intr(&sc->sc_ports[1],
(isr1 & 0xf0) >> 4);
if (isr0 & 0x0f)
ret += sca_msci_intr(&sc->sc_ports[0], isr0 & 0x0f);
if (isr0 & 0xf0)
ret += sca_msci_intr(&sc->sc_ports[1],
(isr0 & 0xf0) >> 4);
#if 0
if (isr2)
ret += sca_timer_intr(sc, isr2);
#endif
}
return (ret);
}
static int
sca_dmac_intr(sca_port_t *scp, u_int8_t isr)
{
u_int8_t dsr;
int ret;
ret = 0;
if (isr & (SCA_ISR1_DMAC_TX0A | SCA_ISR1_DMAC_TX0B)) {
SCA_DPRINTF(SCA_DEBUG_INTR,
("TX INTERRUPT port %d\n", scp->sp_port));
dsr = 1;
while (dsr != 0) {
ret++;
dsr = dmac_read_1(scp, SCA_DSR1);
dmac_write_1(scp, SCA_DSR1,
dsr | SCA_DSR_DEWD);
dsr &= ( SCA_DSR_COF | SCA_DSR_BOF | SCA_DSR_EOT);
if (dsr == 0)
break;
if (dsr & SCA_DSR_COF) {
printf("%s: TXDMA counter overflow\n",
scp->sp_if.if_xname);
scp->sp_if.if_flags &= ~IFF_OACTIVE;
scp->sp_txcur = 0;
scp->sp_txinuse = 0;
}
if (dsr & SCA_DSR_BOF) {
printf("%s: TXDMA buffer overflow, cda 0x%04x, eda 0x%04x, cpb 0x%02x\n",
scp->sp_if.if_xname,
dmac_read_2(scp, SCA_CDAL1),
dmac_read_2(scp, SCA_EDAL1),
dmac_read_1(scp, SCA_CPB1));
scp->sp_if.if_flags &= ~IFF_OACTIVE;
scp->sp_txcur = 0;
scp->sp_txinuse = 0;
}
if (dsr & SCA_DSR_EOT) {
SCA_DPRINTF(SCA_DEBUG_TX,
("Transmit completed. cda %x eda %x dsr %x\n",
dmac_read_2(scp, SCA_CDAL1),
dmac_read_2(scp, SCA_EDAL1),
dsr));
scp->sp_if.if_flags &= ~IFF_OACTIVE;
scp->sp_txcur = 0;
scp->sp_txinuse = 0;
if_schedule_deferred_start(&scp->sp_if);
}
}
}
if (isr & (SCA_ISR1_DMAC_RX0A | SCA_ISR1_DMAC_RX0B)) {
SCA_DPRINTF(SCA_DEBUG_INTR, ("RX INTERRUPT port %d\n",
(scp == &scp->sca->sc_ports[0] ? 0 : 1)));
dsr = 1;
while (dsr != 0) {
ret++;
dsr = dmac_read_1(scp, SCA_DSR0);
dmac_write_1(scp, SCA_DSR0, dsr | SCA_DSR_DEWD);
dsr &= (SCA_DSR_EOM | SCA_DSR_COF
| SCA_DSR_BOF | SCA_DSR_EOT);
if (dsr == 0)
break;
if (dsr & SCA_DSR_EOM) {
SCA_DPRINTF(SCA_DEBUG_RX, ("Got a frame!\n"));
sca_get_packets(scp);
}
if (dsr & SCA_DSR_COF) {
printf("%s: RXDMA counter overflow\n",
scp->sp_if.if_xname);
sca_dmac_rxinit(scp);
}
if (dsr & (SCA_DSR_BOF | SCA_DSR_EOT)) {
printf("%s: RXDMA buffer overflow\n",
scp->sp_if.if_xname);
sca_dmac_rxinit(scp);
}
}
}
return ret;
}
static int
sca_msci_intr(sca_port_t *scp, u_int8_t isr)
{
u_int8_t st1, trc0;
if ((st1 = msci_read_1(scp, SCA_ST10))) {
msci_write_1(scp, SCA_ST10, st1);
if (st1 & SCA_ST1_UDRN) {
trc0 = msci_read_1(scp, SCA_TRC00);
if (trc0 == 0x1f)
printf("TX: underrun - fifo depth maxed\n");
else {
if ((trc0 += 2) > 0x1f)
trc0 = 0x1f;
SCA_DPRINTF(SCA_DEBUG_TX,
("TX: udrn - incr fifo to %d\n", trc0));
msci_write_1(scp, SCA_TRC00, trc0);
}
}
}
return (0);
}
static void
sca_get_packets(sca_port_t *scp)
{
struct sca_softc *sc;
SCA_DPRINTF(SCA_DEBUG_RX, ("RX: sca_get_packets\n"));
sc = scp->sca;
if (sc->sc_usedma)
bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam,
0, sc->scu_allocsize,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
else {
sc->scu_page_on(sc);
sc->scu_set_page(sc, scp->sp_rxdesc_p);
}
while (sca_frame_avail(scp)) {
sca_frame_process(scp);
sca_frame_read_done(scp);
}
if (sc->sc_usedma)
bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam,
0, sc->scu_allocsize,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
else
sc->scu_page_off(sc);
}
static int
sca_frame_avail(sca_port_t *scp)
{
u_int16_t cda;
u_int32_t desc_p;
sca_desc_t *desc;
u_int8_t rxstat;
int cdaidx, toolong;
cda = dmac_read_2(scp, SCA_CDAL0);
desc_p = (scp->sp_rxdesc_p & 0xFFFF);
desc_p = cda - desc_p;
cdaidx = desc_p / sizeof(sca_desc_t);
SCA_DPRINTF(SCA_DEBUG_RX,
("RX: cda %x desc_p %x cdaidx %u, nrxdesc %d rxstart %d\n",
cda, desc_p, cdaidx, scp->sp_nrxdesc, scp->sp_rxstart));
if (cdaidx >= scp->sp_nrxdesc)
panic("current descriptor index out of range");
toolong = 0;
for (; scp->sp_rxstart != cdaidx; sca_frame_read_done(scp)) {
desc = &scp->sp_rxdesc[scp->sp_rxstart];
rxstat = sca_desc_read_stat(scp->sca, desc);
SCA_DPRINTF(SCA_DEBUG_RX, ("port %d RX: idx %d rxstat %x\n",
scp->sp_port, scp->sp_rxstart, rxstat));
SCA_DPRINTF(SCA_DEBUG_RX, ("port %d RX: buflen %d\n",
scp->sp_port, sca_desc_read_buflen(scp->sca, desc)));
if (rxstat & SCA_DESC_ERRORS) {
if_statinc(&scp->sp_if, if_ierrors);
toolong = 0;
continue;
}
if (rxstat & SCA_DESC_EOM) {
if (!toolong)
return (1);
toolong = 0;
} else if (!toolong) {
if_statinc(&scp->sp_if, if_ierrors);
toolong = 1;
}
SCA_DPRINTF(SCA_DEBUG_RX, ("RX: idx %d no EOM\n",
scp->sp_rxstart));
}
SCA_DPRINTF(SCA_DEBUG_RX, ("RX: returning none\n"));
return 0;
}
static void
sca_frame_process(sca_port_t *scp)
{
pktqueue_t *pktq = NULL;
struct hdlc_header *hdlc;
struct cisco_pkt *cisco;
sca_desc_t *desc;
struct mbuf *m;
u_int8_t *bufp;
u_int16_t len;
u_int32_t t;
t = time_uptime * 1000;
desc = &scp->sp_rxdesc[scp->sp_rxstart];
bufp = scp->sp_rxbuf + SCA_BSIZE * scp->sp_rxstart;
len = sca_desc_read_buflen(scp->sca, desc);
SCA_DPRINTF(SCA_DEBUG_RX,
("RX: desc %lx bufp %lx len %d\n", (bus_addr_t)desc,
(bus_addr_t)bufp, len));
#if SCA_DEBUG_LEVEL > 0
if (sca_debug & SCA_DEBUG_RXPKT)
sca_frame_print(scp, desc, bufp);
#endif
if (len < sizeof(struct hdlc_header)) {
if_statinc(&scp->sp_if, if_ierrors);
return;
}
m = sca_mbuf_alloc(scp->sca, bufp, len);
if (m == NULL) {
SCA_DPRINTF(SCA_DEBUG_RX, ("RX: no mbuf!\n"));
return;
}
m = m_pullup(m, sizeof(struct hdlc_header));
if (m == NULL) {
SCA_DPRINTF(SCA_DEBUG_RX, ("RX: no m_pullup!\n"));
return;
}
bpf_mtap_softint(&scp->sp_if, m);
if_statinc(&scp->sp_if, if_ipackets);
hdlc = mtod(m, struct hdlc_header *);
switch (ntohs(hdlc->h_proto)) {
#ifdef INET
case HDLC_PROTOCOL_IP:
SCA_DPRINTF(SCA_DEBUG_RX, ("Received IP packet\n"));
m_set_rcvif(m, &scp->sp_if);
m->m_pkthdr.len -= sizeof(struct hdlc_header);
m->m_data += sizeof(struct hdlc_header);
m->m_len -= sizeof(struct hdlc_header);
pktq = ip_pktq;
break;
#endif
#ifdef INET6
case HDLC_PROTOCOL_IPV6:
SCA_DPRINTF(SCA_DEBUG_RX, ("Received IP packet\n"));
m_set_rcvif(m, &scp->sp_if);
m->m_pkthdr.len -= sizeof(struct hdlc_header);
m->m_data += sizeof(struct hdlc_header);
m->m_len -= sizeof(struct hdlc_header);
pktq = ip6_pktq;
break;
#endif
case CISCO_KEEPALIVE:
SCA_DPRINTF(SCA_DEBUG_CISCO,
("Received CISCO keepalive packet\n"));
if (len < CISCO_PKT_LEN) {
SCA_DPRINTF(SCA_DEBUG_CISCO,
("short CISCO packet %d, wanted %d\n",
len, CISCO_PKT_LEN));
if_statinc(&scp->sp_if, if_ierrors);
goto dropit;
}
m = m_pullup(m, sizeof(struct cisco_pkt));
if (m == NULL) {
SCA_DPRINTF(SCA_DEBUG_RX, ("RX: no m_pullup!\n"));
return;
}
cisco = (struct cisco_pkt *)
(mtod(m, u_int8_t *) + HDLC_HDRLEN);
m_set_rcvif(m, &scp->sp_if);
switch (ntohl(cisco->type)) {
case CISCO_ADDR_REQ:
printf("Got CISCO addr_req, ignoring\n");
if_statinc(&scp->sp_if, if_ierrors);
goto dropit;
case CISCO_ADDR_REPLY:
printf("Got CISCO addr_reply, ignoring\n");
if_statinc(&scp->sp_if, if_ierrors);
goto dropit;
case CISCO_KEEPALIVE_REQ:
SCA_DPRINTF(SCA_DEBUG_CISCO,
("Received KA, mseq %d,"
" yseq %d, rel 0x%04x, t0"
" %04x, t1 %04x\n",
ntohl(cisco->par1), ntohl(cisco->par2),
ntohs(cisco->rel), ntohs(cisco->time0),
ntohs(cisco->time1)));
scp->cka_lastrx = ntohl(cisco->par1);
scp->cka_lasttx++;
cisco->par2 = cisco->par1;
cisco->par1 = htonl(scp->cka_lasttx);
cisco->time0 = htons((u_int16_t)(t >> 16));
cisco->time1 = htons((u_int16_t)(t & 0x0000ffff));
if (IF_QFULL(&scp->linkq)) {
IF_DROP(&scp->linkq);
goto dropit;
}
IF_ENQUEUE(&scp->linkq, m);
sca_start(&scp->sp_if);
if (!scp->sca->sc_usedma) {
scp->sca->scu_set_page(scp->sca,
scp->sp_rxdesc_p);
scp->sca->scu_page_on(scp->sca);
}
return;
default:
SCA_DPRINTF(SCA_DEBUG_CISCO,
("Unknown CISCO keepalive protocol 0x%04x\n",
ntohl(cisco->type)));
if_statinc(&scp->sp_if, if_noproto);
goto dropit;
}
return;
default:
SCA_DPRINTF(SCA_DEBUG_RX,
("Unknown/unexpected ethertype 0x%04x\n",
ntohs(hdlc->h_proto)));
if_statinc(&scp->sp_if, if_noproto);
goto dropit;
}
KASSERT(pktq != NULL);
if (__predict_false(!pktq_enqueue(pktq, m, 0))) {
if_statinc(&scp->sp_if, if_iqdrops);
goto dropit;
}
return;
dropit:
m_freem(m);
return;
}
#if SCA_DEBUG_LEVEL > 0
static void
sca_frame_print(sca_port_t *scp, sca_desc_t *desc, u_int8_t *p)
{
int i;
int nothing_yet = 1;
struct sca_softc *sc;
u_int len;
sc = scp->sca;
printf("desc va %p: chainp 0x%x bufp 0x%0x stat 0x%0x len %d\n",
desc,
sca_desc_read_chainp(sc, desc),
sca_desc_read_bufp(sc, desc),
sca_desc_read_stat(sc, desc),
(len = sca_desc_read_buflen(sc, desc)));
for (i = 0 ; i < len && i < 256; i++) {
if (nothing_yet == 1 &&
(sc->sc_usedma ? *p
: bus_space_read_1(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, p))) == 0) {
p++;
continue;
}
nothing_yet = 0;
if (i % 16 == 0)
printf("\n");
printf("%02x ",
(sc->sc_usedma ? *p
: bus_space_read_1(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, p))));
p++;
}
if (i % 16 != 1)
printf("\n");
}
#endif
static void
sca_frame_read_done(sca_port_t *scp)
{
u_int16_t edesc_p;
scp->sp_rxend = scp->sp_rxstart;
scp->sp_rxstart = (scp->sp_rxstart + 1) % scp->sp_nrxdesc;
edesc_p = (u_int16_t)scp->sp_rxdesc_p +
(sizeof(sca_desc_t) * scp->sp_rxend);
dmac_write_2(scp, SCA_EDAL0, edesc_p);
}
static void
sca_port_up(sca_port_t *scp)
{
struct sca_softc *sc = scp->sca;
struct timeval now;
#if 0
u_int8_t ier0, ier1;
#endif
#if 0
msci_write_1(scp, SCA_CMD0, SCA_CMD_TXRESET);
msci_write_1(scp, SCA_CMD0, SCA_CMD_RXRESET);
#endif
scp->sp_if.if_flags &= ~IFF_OACTIVE;
scp->sp_if.if_flags |= IFF_RUNNING;
sc->sc_dtr_callback(sc->sc_aux, scp->sp_port, 1);
msci_write_1(scp, SCA_CTL0,
(msci_read_1(scp, SCA_CTL0) & ~SCA_CTL_RTS_MASK)
| SCA_CTL_RTS_HIGH);
#if 0
ier0 = SCA_IER0_MSCI_RXRDY0 | SCA_IER0_MSCI_TXRDY0
| SCA_IER0_MSCI_RXINT0 | SCA_IER0_MSCI_TXINT0;
ier1 = SCA_IER1_DMAC_RX0A | SCA_IER1_DMAC_RX0B
| SCA_IER1_DMAC_TX0A | SCA_IER1_DMAC_TX0B;
if (scp->sp_port == 1) {
ier0 <<= 4;
ier1 <<= 4;
}
sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) | ier0);
sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) | ier1);
#else
if (scp->sp_port == 0) {
sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) | 0x0f);
sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) | 0x0f);
} else {
sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) | 0xf0);
sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) | 0xf0);
}
#endif
msci_write_1(scp, SCA_CMD0, SCA_CMD_TXENABLE);
msci_write_1(scp, SCA_CMD0, SCA_CMD_RXENABLE);
scp->sp_txinuse = 0;
scp->sp_txcur = 0;
getmicrotime(&now);
scp->cka_lasttx = now.tv_usec;
scp->cka_lastrx = 0;
}
static void
sca_port_down(sca_port_t *scp)
{
struct sca_softc *sc = scp->sca;
#if 0
u_int8_t ier0, ier1;
#endif
sc->sc_dtr_callback(sc->sc_aux, scp->sp_port, 0);
msci_write_1(scp, SCA_CTL0,
(msci_read_1(scp, SCA_CTL0) & ~SCA_CTL_RTS_MASK)
| SCA_CTL_RTS_LOW);
#if 0
ier0 = SCA_IER0_MSCI_RXRDY0 | SCA_IER0_MSCI_TXRDY0
| SCA_IER0_MSCI_RXINT0 | SCA_IER0_MSCI_TXINT0;
ier1 = SCA_IER1_DMAC_RX0A | SCA_IER1_DMAC_RX0B
| SCA_IER1_DMAC_TX0A | SCA_IER1_DMAC_TX0B;
if (scp->sp_port == 1) {
ier0 <<= 4;
ier1 <<= 4;
}
sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) & ~ier0);
sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) & ~ier1);
#else
if (scp->sp_port == 0) {
sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) & 0xf0);
sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) & 0xf0);
} else {
sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) & 0x0f);
sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) & 0x0f);
}
#endif
msci_write_1(scp, SCA_CMD0, SCA_CMD_RXDISABLE);
msci_write_1(scp, SCA_CMD0, SCA_CMD_TXDISABLE);
scp->sp_if.if_flags &= ~(IFF_OACTIVE|IFF_RUNNING);
}
void
sca_shutdown(struct sca_softc *sca)
{
sca_write_1(sca, SCA_DMER, 0);
sca_write_1(sca, SCA_IER0, 0);
sca_write_1(sca, SCA_IER1, 0);
}
static void
sca_port_starttx(sca_port_t *scp)
{
u_int32_t startdesc_p, enddesc_p;
int enddesc;
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: starttx\n"));
if (((scp->sp_if.if_flags & IFF_OACTIVE) == IFF_OACTIVE)
|| scp->sp_txinuse == 0)
return;
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: setting oactive\n"));
scp->sp_if.if_flags |= IFF_OACTIVE;
enddesc = (scp->sp_txcur + 1) % scp->sp_ntxdesc;
startdesc_p = scp->sp_txdesc_p;
enddesc_p = scp->sp_txdesc_p + sizeof(sca_desc_t) * enddesc;
SCA_DPRINTF(SCA_DEBUG_TX, ("TX: start %x end %x\n",
startdesc_p, enddesc_p));
dmac_write_2(scp, SCA_EDAL1, (u_int16_t)(enddesc_p & 0x0000ffff));
dmac_write_2(scp, SCA_CDAL1,
(u_int16_t)(startdesc_p & 0x0000ffff));
dmac_write_1(scp, SCA_DSR1, SCA_DSR_DE);
}
static struct mbuf *
sca_mbuf_alloc(struct sca_softc *sc, void *p, u_int len)
{
struct mbuf *m;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return NULL;
if (len > MHLEN) {
if (len > MCLBYTES) {
m_freem(m);
return NULL;
}
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(m);
return NULL;
}
}
if (p != NULL) {
if (sc->sc_usedma)
memcpy(mtod(m, void *), p, len);
else
bus_space_read_region_1(sc->scu_memt, sc->scu_memh,
sca_page_addr(sc, p), mtod(m, u_int8_t *), len);
}
m->m_len = len;
m->m_pkthdr.len = len;
return (m);
}
void
sca_get_base_clock(struct sca_softc *sc)
{
struct timeval btv, ctv, dtv;
u_int64_t bcnt;
u_int32_t cnt;
u_int16_t subcnt;
sca_write_1(sc, SCA_TCSR0, 0);
sca_write_1(sc, SCA_TEPR0, 0);
(void)sca_read_1(sc, SCA_TCSR0);
subcnt = sca_read_2(sc, SCA_TCNTL0);
sca_write_2(sc, SCA_TCONRL0, 0xffff);
cnt = 0;
microtime(&btv);
sca_write_1(sc, SCA_TCSR0, SCA_TCSR_TME);
for (;;) {
microtime(&ctv);
timersub(&ctv, &btv, &dtv);
if (dtv.tv_usec >= 750000)
break;
while (!(sca_read_1(sc, SCA_TCSR0) & SCA_TCSR_CMF))
;
(void)sca_read_2(sc, SCA_TCNTL0);
cnt++;
}
sca_write_1(sc, SCA_TCSR0, 0);
subcnt = sca_read_2(sc, SCA_TCNTL0);
cnt = (cnt << 16) | subcnt;
bcnt = cnt * 8;
bcnt *= (u_int64_t)750000;
bcnt /= (u_int64_t)dtv.tv_usec;
cnt = bcnt;
cnt *= 4;
cnt /= 3;
SCA_DPRINTF(SCA_DEBUG_CLOCK,
("sca: unadjusted base %lu Hz\n", (u_long)cnt));
sc->sc_baseclock = ((cnt + 100) / 200) * 200;
}
void
sca_print_clock_info(struct sca_softc *sc)
{
struct sca_port *scp;
u_int32_t mhz, div;
int i;
printf("%s: base clock %d Hz\n", device_xname(sc->sc_parent),
sc->sc_baseclock);
for (i = 0; i < sc->sc_numports; i++) {
scp = &sc->sc_ports[i];
mhz = sc->sc_baseclock / (scp->sp_tmc ? scp->sp_tmc : 256);
div = scp->sp_rxs & SCA_RXS_DIV_MASK;
printf("%s: rx clock: ", scp->sp_if.if_xname);
switch (scp->sp_rxs & SCA_RXS_CLK_MASK) {
case SCA_RXS_CLK_LINE:
printf("line");
break;
case SCA_RXS_CLK_LINE_SN:
printf("line with noise suppression");
break;
case SCA_RXS_CLK_INTERNAL:
printf("internal %d Hz", (mhz >> div));
break;
case SCA_RXS_CLK_ADPLL_OUT:
printf("adpll using internal %d Hz", (mhz >> div));
break;
case SCA_RXS_CLK_ADPLL_IN:
printf("adpll using line clock");
break;
}
printf(" tx clock: ");
div = scp->sp_txs & SCA_TXS_DIV_MASK;
switch (scp->sp_txs & SCA_TXS_CLK_MASK) {
case SCA_TXS_CLK_LINE:
printf("line\n");
break;
case SCA_TXS_CLK_INTERNAL:
printf("internal %d Hz\n", (mhz >> div));
break;
case SCA_TXS_CLK_RXCLK:
printf("rxclock\n");
break;
}
if (scp->sp_eclock)
printf("%s: outputting line clock\n",
scp->sp_if.if_xname);
}
}