1 /* $NetBSD: if_mc.c,v 1.5 2001/07/22 11:29:46 wiz Exp $ */ 2 3 /*- 4 * Copyright (c) 1997 David Huang <khym@bga.com> 5 * All rights reserved. 6 * 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>. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 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 17 * 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. 28 * 29 */ 30 31 /* 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. 36 */ 37 38 #include <sys/param.h> 39 #include <sys/device.h> 40 #include <sys/malloc.h> 41 #include <sys/socket.h> 42 #include <sys/systm.h> 43 44 #include <net/if.h> 45 #include <net/if_ether.h> 46 #include <net/if_media.h> 47 48 #include <uvm/uvm_extern.h> 49 50 #include <dev/ofw/openfirm.h> 51 52 #include <machine/pio.h> 53 #include <machine/bus.h> 54 #include <machine/autoconf.h> 55 56 #include <macppc/dev/am79c950reg.h> 57 #include <macppc/dev/if_mcvar.h> 58 59 #define MC_BUFSIZE 0x800 60 61 hide int mc_match __P((struct device *, struct cfdata *, void *)); 62 hide void mc_attach __P((struct device *, struct device *, void *)); 63 hide void mc_init __P((struct mc_softc *sc)); 64 hide void mc_putpacket __P((struct mc_softc *sc, u_int len)); 65 hide int mc_dmaintr __P((void *arg)); 66 hide void mc_reset_rxdma __P((struct mc_softc *sc)); 67 hide void mc_reset_txdma __P((struct mc_softc *sc)); 68 hide void mc_select_utp __P((struct mc_softc *sc)); 69 hide void mc_select_aui __P((struct mc_softc *sc)); 70 hide int mc_mediachange __P((struct mc_softc *sc)); 71 hide void mc_mediastatus __P((struct mc_softc *sc, struct ifmediareq *)); 72 73 int mc_supmedia[] = { 74 IFM_ETHER | IFM_10_T, 75 IFM_ETHER | IFM_10_5, 76 /*IFM_ETHER | IFM_AUTO,*/ 77 }; 78 79 #define N_SUPMEDIA (sizeof(mc_supmedia) / sizeof(int)); 80 81 struct cfattach mc_ca = { 82 sizeof(struct mc_softc), mc_match, mc_attach 83 }; 84 85 hide int 86 mc_match(parent, cf, aux) 87 struct device *parent; 88 struct cfdata *cf; 89 void *aux; 90 { 91 struct confargs *ca = aux; 92 93 if (strcmp(ca->ca_name, "mace") != 0) 94 return 0; 95 96 /* requires 6 regs */ 97 if (ca->ca_nreg / sizeof(int) != 6) 98 return 0; 99 100 /* requires 3 intrs */ 101 if (ca->ca_nintr / sizeof(int) != 3) 102 return 0; 103 104 return 1; 105 } 106 107 hide void 108 mc_attach(parent, self, aux) 109 struct device *parent, *self; 110 void *aux; 111 { 112 struct confargs *ca = aux; 113 struct mc_softc *sc = (struct mc_softc *)self; 114 u_int8_t myaddr[ETHER_ADDR_LEN]; 115 u_int *reg; 116 117 sc->sc_node = ca->ca_node; 118 119 reg = ca->ca_reg; 120 reg[0] += ca->ca_baseaddr; 121 reg[2] += ca->ca_baseaddr; 122 reg[4] += ca->ca_baseaddr; 123 124 sc->sc_txdma = mapiodev(reg[2], reg[3]); 125 sc->sc_rxdma = mapiodev(reg[4], reg[5]); 126 bus_space_map(sc->sc_regt, reg[0], reg[1], 0, &sc->sc_regh); 127 /* XXX sc_regt is uninitialized */ 128 sc->sc_tail = 0; 129 sc->sc_txdmacmd = dbdma_alloc(sizeof(dbdma_command_t) * 2); 130 sc->sc_rxdmacmd = (void *)dbdma_alloc(sizeof(dbdma_command_t) * 8); 131 memset(sc->sc_txdmacmd, 0, sizeof(dbdma_command_t) * 2); 132 memset(sc->sc_rxdmacmd, 0, sizeof(dbdma_command_t) * 8); 133 134 printf(": irq %d,%d,%d", 135 ca->ca_intr[0], ca->ca_intr[1], ca->ca_intr[2]); 136 137 if (OF_getprop(sc->sc_node, "local-mac-address", myaddr, 6) != 6) { 138 printf(": failed to get MAC address.\n"); 139 return; 140 } 141 142 /* allocate memory for transmit buffer and mark it non-cacheable */ 143 sc->sc_txbuf = malloc(NBPG, M_DEVBUF, M_WAITOK); 144 sc->sc_txbuf_phys = kvtop(sc->sc_txbuf); 145 memset(sc->sc_txbuf, 0, NBPG); 146 147 /* 148 * allocate memory for receive buffer and mark it non-cacheable 149 * XXX This should use the bus_dma interface, since the buffer 150 * needs to be physically contiguous. However, it seems that 151 * at least on my system, malloc() does allocate contiguous 152 * memory. If it's not, suggest reducing the number of buffers 153 * to 2, which will fit in one 4K page. 154 */ 155 sc->sc_rxbuf = malloc(MC_NPAGES * NBPG, M_DEVBUF, M_WAITOK); 156 sc->sc_rxbuf_phys = kvtop(sc->sc_rxbuf); 157 memset(sc->sc_rxbuf, 0, MC_NPAGES * NBPG); 158 159 if ((int)sc->sc_txbuf & PGOFSET) 160 printf("txbuf is not page-aligned\n"); 161 if ((int)sc->sc_rxbuf & PGOFSET) 162 printf("rxbuf is not page-aligned\n"); 163 164 sc->sc_bus_init = mc_init; 165 sc->sc_putpacket = mc_putpacket; 166 167 168 /* disable receive DMA */ 169 dbdma_reset(sc->sc_rxdma); 170 171 /* disable transmit DMA */ 172 dbdma_reset(sc->sc_txdma); 173 174 /* install interrupt handlers */ 175 /*intr_establish(ca->ca_intr[1], IST_LEVEL, IPL_NET, mc_dmaintr, sc);*/ 176 intr_establish(ca->ca_intr[2], IST_LEVEL, IPL_NET, mc_dmaintr, sc); 177 intr_establish(ca->ca_intr[0], IST_LEVEL, IPL_NET, mcintr, sc); 178 179 sc->sc_biucc = XMTSP_64; 180 sc->sc_fifocc = XMTFW_16 | RCVFW_64 | XMTFWU | RCVFWU | 181 XMTBRST | RCVBRST; 182 /*sc->sc_plscc = PORTSEL_10BT;*/ 183 sc->sc_plscc = PORTSEL_GPSI | ENPLSIO; 184 185 /* mcsetup returns 1 if something fails */ 186 if (mcsetup(sc, myaddr)) { 187 printf("mcsetup returns non zero\n"); 188 return; 189 } 190 #ifdef NOTYET 191 sc->sc_mediachange = mc_mediachange; 192 sc->sc_mediastatus = mc_mediastatus; 193 sc->sc_supmedia = mc_supmedia; 194 sc->sc_nsupmedia = N_SUPMEDIA; 195 sc->sc_defaultmedia = IFM_ETHER | IFM_10_T; 196 #endif 197 } 198 199 /* Bus-specific initialization */ 200 hide void 201 mc_init(sc) 202 struct mc_softc *sc; 203 { 204 mc_reset_rxdma(sc); 205 mc_reset_txdma(sc); 206 } 207 208 hide void 209 mc_putpacket(sc, len) 210 struct mc_softc *sc; 211 u_int len; 212 { 213 dbdma_command_t *cmd = sc->sc_txdmacmd; 214 215 DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, len, sc->sc_txbuf_phys, 216 DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); 217 218 dbdma_start(sc->sc_txdma, sc->sc_txdmacmd); 219 } 220 221 /* 222 * Interrupt handler for the MACE DMA completion interrupts 223 */ 224 int 225 mc_dmaintr(arg) 226 void *arg; 227 { 228 struct mc_softc *sc = arg; 229 int status, offset, statoff; 230 int datalen, resid; 231 int i, n; 232 dbdma_command_t *cmd; 233 234 /* We've received some packets from the MACE */ 235 236 /* Loop through, processing each of the packets */ 237 i = sc->sc_tail; 238 for (n = 0; n < MC_RXDMABUFS; n++, i++) { 239 if (i == MC_RXDMABUFS) 240 i = 0; 241 242 cmd = &sc->sc_rxdmacmd[i]; 243 /* flushcache(cmd, sizeof(dbdma_command_t)); */ 244 status = dbdma_ld16(&cmd->d_status); 245 resid = dbdma_ld16(&cmd->d_resid); 246 247 /*if ((status & D_ACTIVE) == 0)*/ 248 if ((status & 0x40) == 0) 249 continue; 250 251 #if 1 252 if (dbdma_ld16(&cmd->d_count) != ETHERMTU + 22) 253 printf("bad d_count\n"); 254 #endif 255 256 datalen = dbdma_ld16(&cmd->d_count) - resid; 257 datalen -= 4; /* 4 == status bytes */ 258 259 if (datalen < 4 + sizeof(struct ether_header)) { 260 printf("short packet len=%d\n", datalen); 261 /* continue; */ 262 goto next; 263 } 264 265 offset = i * MC_BUFSIZE; 266 statoff = offset + datalen; 267 268 DBDMA_BUILD_CMD(cmd, DBDMA_CMD_STOP, 0, 0, 0, 0); 269 __asm __volatile("eieio"); 270 271 /* flushcache(sc->sc_rxbuf + offset, datalen + 4); */ 272 273 sc->sc_rxframe.rx_rcvcnt = sc->sc_rxbuf[statoff + 0]; 274 sc->sc_rxframe.rx_rcvsts = sc->sc_rxbuf[statoff + 1]; 275 sc->sc_rxframe.rx_rntpc = sc->sc_rxbuf[statoff + 2]; 276 sc->sc_rxframe.rx_rcvcc = sc->sc_rxbuf[statoff + 3]; 277 sc->sc_rxframe.rx_frame = sc->sc_rxbuf + offset; 278 279 mc_rint(sc); 280 281 next: 282 DBDMA_BUILD_CMD(cmd, DBDMA_CMD_IN_LAST, 0, DBDMA_INT_ALWAYS, 283 DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); 284 __asm __volatile("eieio"); 285 cmd->d_status = 0; 286 cmd->d_resid = 0; 287 sc->sc_tail = i + 1; 288 } 289 290 dbdma_continue(sc->sc_rxdma); 291 292 return 1; 293 } 294 295 hide void 296 mc_reset_rxdma(sc) 297 struct mc_softc *sc; 298 { 299 dbdma_command_t *cmd = sc->sc_rxdmacmd; 300 dbdma_regmap_t *dmareg = sc->sc_rxdma; 301 int i; 302 u_int8_t maccc; 303 304 /* Disable receiver, reset the DMA channels */ 305 maccc = NIC_GET(sc, MACE_MACCC); 306 NIC_PUT(sc, MACE_MACCC, maccc & ~ENRCV); 307 308 dbdma_reset(dmareg); 309 310 for (i = 0; i < MC_RXDMABUFS; i++) { 311 DBDMA_BUILD(cmd, DBDMA_CMD_IN_LAST, 0, ETHERMTU + 22, 312 sc->sc_rxbuf_phys + MC_BUFSIZE * i, DBDMA_INT_ALWAYS, 313 DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); 314 cmd++; 315 } 316 317 DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0, 318 DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS); 319 dbdma_st32(&cmd->d_cmddep, kvtop((caddr_t)sc->sc_rxdmacmd)); 320 cmd++; 321 322 dbdma_start(dmareg, sc->sc_rxdmacmd); 323 324 sc->sc_tail = 0; 325 326 /* Reenable receiver, reenable DMA */ 327 NIC_PUT(sc, MACE_MACCC, maccc); 328 } 329 330 hide void 331 mc_reset_txdma(sc) 332 struct mc_softc *sc; 333 { 334 dbdma_command_t *cmd = sc->sc_txdmacmd; 335 dbdma_regmap_t *dmareg = sc->sc_txdma; 336 u_int8_t maccc; 337 338 /* disable transmitter */ 339 maccc = NIC_GET(sc, MACE_MACCC); 340 NIC_PUT(sc, MACE_MACCC, maccc & ~ENXMT); 341 342 dbdma_reset(dmareg); 343 344 DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, 0, sc->sc_txbuf_phys, 345 DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); 346 cmd++; 347 DBDMA_BUILD(cmd, DBDMA_CMD_STOP, 0, 0, 0, 348 DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); 349 350 out32rb(&dmareg->d_cmdptrhi, 0); 351 out32rb(&dmareg->d_cmdptrlo, kvtop((caddr_t)sc->sc_txdmacmd)); 352 353 /* restore old value */ 354 NIC_PUT(sc, MACE_MACCC, maccc); 355 } 356 357 void 358 mc_select_utp(sc) 359 struct mc_softc *sc; 360 { 361 sc->sc_plscc = PORTSEL_GPSI | ENPLSIO; 362 } 363 364 void 365 mc_select_aui(sc) 366 struct mc_softc *sc; 367 { 368 sc->sc_plscc = PORTSEL_AUI; 369 } 370 371 int 372 mc_mediachange(sc) 373 struct mc_softc *sc; 374 { 375 struct ifmedia *ifm = &sc->sc_media; 376 377 if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 378 return EINVAL; 379 380 switch (IFM_SUBTYPE(ifm->ifm_media)) { 381 382 case IFM_10_T: 383 mc_select_utp(sc); 384 break; 385 386 case IFM_10_5: 387 mc_select_aui(sc); 388 break; 389 390 default: 391 return EINVAL; 392 } 393 394 return 0; 395 } 396 397 void 398 mc_mediastatus(sc, ifmr) 399 struct mc_softc *sc; 400 struct ifmediareq *ifmr; 401 { 402 if (sc->sc_plscc == PORTSEL_AUI) 403 ifmr->ifm_active = IFM_ETHER | IFM_10_5; 404 else 405 ifmr->ifm_active = IFM_ETHER | IFM_10_T; 406 } 407