1 /* $NetBSD: if_mc.c,v 1.16 2009/03/14 15:36:09 dsl Exp $ */
4 * Copyright (c) 1997 David Huang <khym@bga.com>
7 * Portions of this code are based on code by Denton Gentry <denny1@home.com>
8 * and Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * Bus attachment and DMA routines for the mc driver (Centris/Quadra
33 * 660av and Quadra 840av onboard ethernet, based on the AMD Am79C940
34 * MACE ethernet chip). Also uses the PSC (Peripheral Subsystem
35 * Controller) for DMA to and from the MACE.
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: if_mc.c,v 1.16 2009/03/14 15:36:09 dsl Exp $");
41 #include <sys/param.h>
42 #include <sys/device.h>
43 #include <sys/malloc.h>
44 #include <sys/socket.h>
45 #include <sys/systm.h>
48 #include <net/if_ether.h>
49 #include <net/if_media.h>
51 #include <uvm/uvm_extern.h>
53 #include <dev/ofw/openfirm.h>
55 #include <machine/bus.h>
56 #include <machine/autoconf.h>
57 #include <machine/pio.h>
59 #include <macppc/dev/am79c950reg.h>
60 #include <macppc/dev/if_mcvar.h>
62 #define MC_BUFSIZE 0x800
64 hide
int mc_match(struct device
*, struct cfdata
*, void *);
65 hide
void mc_attach(struct device
*, struct device
*, void *);
66 hide
void mc_init(struct mc_softc
*sc
);
67 hide
void mc_putpacket(struct mc_softc
*sc
, u_int len
);
68 hide
int mc_dmaintr(void *arg
);
69 hide
void mc_reset_rxdma(struct mc_softc
*sc
);
70 hide
void mc_reset_txdma(struct mc_softc
*sc
);
71 hide
void mc_select_utp(struct mc_softc
*sc
);
72 hide
void mc_select_aui(struct mc_softc
*sc
);
73 hide
int mc_mediachange(struct mc_softc
*sc
);
74 hide
void mc_mediastatus(struct mc_softc
*sc
, struct ifmediareq
*);
79 /*IFM_ETHER | IFM_AUTO,*/
82 #define N_SUPMEDIA (sizeof(mc_supmedia) / sizeof(int));
84 CFATTACH_DECL(mc
, sizeof(struct mc_softc
),
85 mc_match
, mc_attach
, NULL
, NULL
);
88 mc_match(struct device
*parent
, struct cfdata
*cf
, void *aux
)
90 struct confargs
*ca
= aux
;
92 if (strcmp(ca
->ca_name
, "mace") != 0)
96 if (ca
->ca_nreg
/ sizeof(int) != 6)
99 /* requires 3 intrs */
100 if (ca
->ca_nintr
/ sizeof(int) != 3)
107 mc_attach(struct device
*parent
, struct device
*self
, void *aux
)
109 struct confargs
*ca
= aux
;
110 struct mc_softc
*sc
= (struct mc_softc
*)self
;
111 u_int8_t myaddr
[ETHER_ADDR_LEN
];
114 sc
->sc_node
= ca
->ca_node
;
115 sc
->sc_regt
= ca
->ca_tag
;
118 reg
[0] += ca
->ca_baseaddr
;
119 reg
[2] += ca
->ca_baseaddr
;
120 reg
[4] += ca
->ca_baseaddr
;
122 sc
->sc_txdma
= mapiodev(reg
[2], reg
[3]);
123 sc
->sc_rxdma
= mapiodev(reg
[4], reg
[5]);
124 bus_space_map(sc
->sc_regt
, reg
[0], reg
[1], 0, &sc
->sc_regh
);
127 sc
->sc_txdmacmd
= dbdma_alloc(sizeof(dbdma_command_t
) * 2);
128 sc
->sc_rxdmacmd
= (void *)dbdma_alloc(sizeof(dbdma_command_t
) * 8);
129 memset(sc
->sc_txdmacmd
, 0, sizeof(dbdma_command_t
) * 2);
130 memset(sc
->sc_rxdmacmd
, 0, sizeof(dbdma_command_t
) * 8);
132 printf(": irq %d,%d,%d",
133 ca
->ca_intr
[0], ca
->ca_intr
[1], ca
->ca_intr
[2]);
135 if (OF_getprop(sc
->sc_node
, "local-mac-address", myaddr
, 6) != 6) {
136 printf(": failed to get MAC address.\n");
140 /* allocate memory for transmit buffer and mark it non-cacheable */
141 sc
->sc_txbuf
= malloc(PAGE_SIZE
, M_DEVBUF
, M_WAITOK
);
142 sc
->sc_txbuf_phys
= kvtop(sc
->sc_txbuf
);
143 memset(sc
->sc_txbuf
, 0, PAGE_SIZE
);
146 * allocate memory for receive buffer and mark it non-cacheable
147 * XXX This should use the bus_dma interface, since the buffer
148 * needs to be physically contiguous. However, it seems that
149 * at least on my system, malloc() does allocate contiguous
150 * memory. If it's not, suggest reducing the number of buffers
151 * to 2, which will fit in one 4K page.
153 sc
->sc_rxbuf
= malloc(MC_NPAGES
* PAGE_SIZE
, M_DEVBUF
, M_WAITOK
);
154 sc
->sc_rxbuf_phys
= kvtop(sc
->sc_rxbuf
);
155 memset(sc
->sc_rxbuf
, 0, MC_NPAGES
* PAGE_SIZE
);
157 if ((int)sc
->sc_txbuf
& PGOFSET
)
158 printf("txbuf is not page-aligned\n");
159 if ((int)sc
->sc_rxbuf
& PGOFSET
)
160 printf("rxbuf is not page-aligned\n");
162 sc
->sc_bus_init
= mc_init
;
163 sc
->sc_putpacket
= mc_putpacket
;
166 /* disable receive DMA */
167 dbdma_reset(sc
->sc_rxdma
);
169 /* disable transmit DMA */
170 dbdma_reset(sc
->sc_txdma
);
172 /* install interrupt handlers */
173 /*intr_establish(ca->ca_intr[1], IST_EDGE, IPL_NET, mc_dmaintr, sc);*/
174 intr_establish(ca
->ca_intr
[2], IST_EDGE
, IPL_NET
, mc_dmaintr
, sc
);
175 intr_establish(ca
->ca_intr
[0], IST_EDGE
, IPL_NET
, mcintr
, sc
);
177 sc
->sc_biucc
= XMTSP_64
;
178 sc
->sc_fifocc
= XMTFW_16
| RCVFW_64
| XMTFWU
| RCVFWU
|
180 /*sc->sc_plscc = PORTSEL_10BT;*/
181 sc
->sc_plscc
= PORTSEL_GPSI
| ENPLSIO
;
183 /* mcsetup returns 1 if something fails */
184 if (mcsetup(sc
, myaddr
)) {
185 printf("mcsetup returns non zero\n");
189 sc
->sc_mediachange
= mc_mediachange
;
190 sc
->sc_mediastatus
= mc_mediastatus
;
191 sc
->sc_supmedia
= mc_supmedia
;
192 sc
->sc_nsupmedia
= N_SUPMEDIA
;
193 sc
->sc_defaultmedia
= IFM_ETHER
| IFM_10_T
;
197 /* Bus-specific initialization */
199 mc_init(struct mc_softc
*sc
)
206 mc_putpacket(struct mc_softc
*sc
, u_int len
)
208 dbdma_command_t
*cmd
= sc
->sc_txdmacmd
;
210 DBDMA_BUILD(cmd
, DBDMA_CMD_OUT_LAST
, 0, len
, sc
->sc_txbuf_phys
,
211 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
213 dbdma_start(sc
->sc_txdma
, sc
->sc_txdmacmd
);
217 * Interrupt handler for the MACE DMA completion interrupts
220 mc_dmaintr(void *arg
)
222 struct mc_softc
*sc
= arg
;
223 int status
, offset
, statoff
;
226 dbdma_command_t
*cmd
;
228 /* We've received some packets from the MACE */
230 /* Loop through, processing each of the packets */
232 for (n
= 0; n
< MC_RXDMABUFS
; n
++, i
++) {
233 if (i
== MC_RXDMABUFS
)
236 cmd
= &sc
->sc_rxdmacmd
[i
];
237 /* flushcache(cmd, sizeof(dbdma_command_t)); */
238 status
= in16rb(&cmd
->d_status
);
239 resid
= in16rb(&cmd
->d_resid
);
241 /*if ((status & D_ACTIVE) == 0)*/
242 if ((status
& 0x40) == 0)
246 if (in16rb(&cmd
->d_count
) != ETHERMTU
+ 22)
247 printf("bad d_count\n");
250 datalen
= in16rb(&cmd
->d_count
) - resid
;
251 datalen
-= 4; /* 4 == status bytes */
253 if (datalen
< 4 + sizeof(struct ether_header
)) {
254 printf("short packet len=%d\n", datalen
);
259 offset
= i
* MC_BUFSIZE
;
260 statoff
= offset
+ datalen
;
262 DBDMA_BUILD_CMD(cmd
, DBDMA_CMD_STOP
, 0, 0, 0, 0);
263 __asm
volatile("eieio");
265 /* flushcache(sc->sc_rxbuf + offset, datalen + 4); */
267 sc
->sc_rxframe
.rx_rcvcnt
= sc
->sc_rxbuf
[statoff
+ 0];
268 sc
->sc_rxframe
.rx_rcvsts
= sc
->sc_rxbuf
[statoff
+ 1];
269 sc
->sc_rxframe
.rx_rntpc
= sc
->sc_rxbuf
[statoff
+ 2];
270 sc
->sc_rxframe
.rx_rcvcc
= sc
->sc_rxbuf
[statoff
+ 3];
271 sc
->sc_rxframe
.rx_frame
= sc
->sc_rxbuf
+ offset
;
276 DBDMA_BUILD_CMD(cmd
, DBDMA_CMD_IN_LAST
, 0, DBDMA_INT_ALWAYS
,
277 DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
278 __asm
volatile("eieio");
284 dbdma_continue(sc
->sc_rxdma
);
290 mc_reset_rxdma(struct mc_softc
*sc
)
292 dbdma_command_t
*cmd
= sc
->sc_rxdmacmd
;
293 dbdma_regmap_t
*dmareg
= sc
->sc_rxdma
;
297 /* Disable receiver, reset the DMA channels */
298 maccc
= NIC_GET(sc
, MACE_MACCC
);
299 NIC_PUT(sc
, MACE_MACCC
, maccc
& ~ENRCV
);
303 for (i
= 0; i
< MC_RXDMABUFS
; i
++) {
304 DBDMA_BUILD(cmd
, DBDMA_CMD_IN_LAST
, 0, ETHERMTU
+ 22,
305 sc
->sc_rxbuf_phys
+ MC_BUFSIZE
* i
, DBDMA_INT_ALWAYS
,
306 DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
310 DBDMA_BUILD(cmd
, DBDMA_CMD_NOP
, 0, 0, 0,
311 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
, DBDMA_BRANCH_ALWAYS
);
312 out32rb(&cmd
->d_cmddep
, kvtop((void *)sc
->sc_rxdmacmd
));
315 dbdma_start(dmareg
, sc
->sc_rxdmacmd
);
319 /* Reenable receiver, reenable DMA */
320 NIC_PUT(sc
, MACE_MACCC
, maccc
);
324 mc_reset_txdma(struct mc_softc
*sc
)
326 dbdma_command_t
*cmd
= sc
->sc_txdmacmd
;
327 dbdma_regmap_t
*dmareg
= sc
->sc_txdma
;
330 /* disable transmitter */
331 maccc
= NIC_GET(sc
, MACE_MACCC
);
332 NIC_PUT(sc
, MACE_MACCC
, maccc
& ~ENXMT
);
336 DBDMA_BUILD(cmd
, DBDMA_CMD_OUT_LAST
, 0, 0, sc
->sc_txbuf_phys
,
337 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
339 DBDMA_BUILD(cmd
, DBDMA_CMD_STOP
, 0, 0, 0,
340 DBDMA_INT_NEVER
, DBDMA_WAIT_NEVER
, DBDMA_BRANCH_NEVER
);
342 out32rb(&dmareg
->d_cmdptrhi
, 0);
343 out32rb(&dmareg
->d_cmdptrlo
, kvtop((void *)sc
->sc_txdmacmd
));
345 /* restore old value */
346 NIC_PUT(sc
, MACE_MACCC
, maccc
);
350 mc_select_utp(struct mc_softc
*sc
)
352 sc
->sc_plscc
= PORTSEL_GPSI
| ENPLSIO
;
356 mc_select_aui(struct mc_softc
*sc
)
358 sc
->sc_plscc
= PORTSEL_AUI
;
362 mc_mediachange(struct mc_softc
*sc
)
364 struct ifmedia
*ifm
= &sc
->sc_media
;
366 if (IFM_TYPE(ifm
->ifm_media
) != IFM_ETHER
)
369 switch (IFM_SUBTYPE(ifm
->ifm_media
)) {
387 mc_mediastatus(struct mc_softc
*sc
, struct ifmediareq
*ifmr
)
389 if (sc
->sc_plscc
== PORTSEL_AUI
)
390 ifmr
->ifm_active
= IFM_ETHER
| IFM_10_5
;
392 ifmr
->ifm_active
= IFM_ETHER
| IFM_10_T
;