1 1.2 andvar /* $NetBSD: zz9k_if.c,v 1.2 2024/02/05 21:46:05 andvar Exp $ */ 2 1.1 phx 3 1.1 phx /* 4 1.1 phx * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 1.1 phx * All rights reserved. 6 1.1 phx * 7 1.1 phx * This code is derived from software contributed to The NetBSD Foundation 8 1.1 phx * by Alain Runa. 9 1.1 phx * 10 1.1 phx * Redistribution and use in source and binary forms, with or without 11 1.1 phx * modification, are permitted provided that the following conditions 12 1.1 phx * are met: 13 1.1 phx * 1. Redistributions of source code must retain the above copyright 14 1.1 phx * notice, this list of conditions and the following disclaimer. 15 1.1 phx * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 phx * notice, this list of conditions and the following disclaimer in the 17 1.1 phx * documentation and/or other materials provided with the distribution. 18 1.1 phx * 19 1.1 phx * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 1.1 phx * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 1.1 phx * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 1.1 phx * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 1.1 phx * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 1.1 phx * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 1.1 phx * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 1.1 phx * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 1.1 phx * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 1.1 phx * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 1.1 phx */ 30 1.1 phx 31 1.1 phx #include <sys/cdefs.h> 32 1.2 andvar __KERNEL_RCSID(0, "$NetBSD: zz9k_if.c,v 1.2 2024/02/05 21:46:05 andvar Exp $"); 33 1.1 phx 34 1.1 phx /* miscellaneous */ 35 1.1 phx #include <sys/types.h> /* size_t */ 36 1.1 phx #include <sys/stdint.h> /* uintXX_t */ 37 1.1 phx #include <sys/stdbool.h> /* bool */ 38 1.1 phx #include <sys/syslog.h> /* log(), LOG_ERR */ 39 1.1 phx 40 1.1 phx /* driver(9) */ 41 1.1 phx #include <sys/param.h> /* NODEV */ 42 1.1 phx #include <sys/device.h> /* CFATTACH_DECL_NEW(), device_priv() */ 43 1.1 phx #include <sys/errno.h> /* EINVAL, ENODEV, EPASSTHROUGH */ 44 1.1 phx 45 1.1 phx /* bus_space(9) and zorro bus */ 46 1.1 phx #include <sys/bus.h> /* bus_space_xxx(), bus_space_xxx_t */ 47 1.1 phx #include <sys/cpu.h> /* kvtop() */ 48 1.1 phx #include <sys/systm.h> /* aprint_xxx(), memcpy() */ 49 1.1 phx #include <amiga/dev/zbusvar.h> /* zbus_args */ 50 1.1 phx 51 1.1 phx /* arp(9) and mbuf(9) */ 52 1.1 phx #include <net/if.h> /* if_oerrors */ 53 1.1 phx #include <net/if_ether.h> /* ethercom, ifnet, ether_ifattach() */ 54 1.1 phx #include <net/if_dl.h> /* satosdl(), sockaddr_dl, dl_addr */ 55 1.1 phx #include <net/bpf.h> /* bpf_mtap(), BPF_D_OUT */ 56 1.1 phx #include <netinet/if_inarp.h> /* arp_ifinit() */ 57 1.1 phx #include <sys/mbuf.h> /* mbuf_xxx */ 58 1.1 phx #include <sys/sockio.h> /* SIOXXX */ 59 1.1 phx 60 1.1 phx /* Interrupt related */ 61 1.1 phx #include <sys/intr.h> /* splvm(), splx() */ 62 1.1 phx #include <amiga/amiga/isr.h> /* isr */ 63 1.1 phx 64 1.1 phx /* zz9k related */ 65 1.1 phx #include <amiga/dev/zz9kvar.h> /* zz9kbus_attach_args */ 66 1.1 phx #include <amiga/dev/zz9kreg.h> /* ZZ9000 registers */ 67 1.1 phx #include "zz9k_if.h" /* NZZ9K_IF */ 68 1.1 phx 69 1.1 phx 70 1.1 phx /* The allmighty softc structure */ 71 1.1 phx struct zzif_softc { 72 1.1 phx device_t sc_dev; 73 1.1 phx struct bus_space_tag sc_bst; 74 1.1 phx bus_space_tag_t sc_iot; 75 1.1 phx bus_space_handle_t sc_regh; 76 1.1 phx bus_space_handle_t sc_rxh; 77 1.1 phx bus_space_handle_t sc_txh; 78 1.1 phx 79 1.1 phx struct ethercom sc_ethercom; 80 1.1 phx struct isr sc_isr; 81 1.1 phx void* sc_txbuffer; 82 1.1 phx void* sc_rxbuffer; 83 1.1 phx uint16_t sc_sequence; 84 1.1 phx }; 85 1.1 phx 86 1.1 phx /* rx buffer contents */ 87 1.1 phx struct zzif_frame { 88 1.1 phx uint16_t size; 89 1.1 phx uint16_t serial; 90 1.1 phx struct ether_header header; 91 1.1 phx uint8_t payload[ETHER_MAX_LEN - ETHER_HDR_LEN]; 92 1.1 phx }; 93 1.1 phx 94 1.1 phx 95 1.1 phx /* ifnet related callbacks */ 96 1.1 phx static void zzif_init(struct zzif_softc *sc); 97 1.1 phx static void zzif_stop(struct zzif_softc *sc); 98 1.1 phx static void zzif_start(struct ifnet *ifp); 99 1.1 phx static int zzif_intr(void *arg); 100 1.1 phx static int zzif_ioctl(struct ifnet *ifp, u_long cmd, void *data); 101 1.1 phx 102 1.1 phx /* driver(9) essentials */ 103 1.1 phx static int zzif_match(device_t parent, cfdata_t match, void *aux); 104 1.1 phx static void zzif_attach(device_t parent, device_t self, void *aux); 105 1.1 phx CFATTACH_DECL_NEW( 106 1.1 phx zz9k_if, sizeof(struct zzif_softc), zzif_match, zzif_attach, NULL, NULL); 107 1.1 phx 108 1.1 phx 109 1.1 phx /* Oh my God, it's full of stars! */ 110 1.1 phx 111 1.1 phx static int 112 1.1 phx zzif_match(device_t parent, cfdata_t match, void *aux) 113 1.1 phx { 114 1.1 phx struct zz9kbus_attach_args *bap = aux; 115 1.1 phx 116 1.1 phx if (strcmp(bap->zzaa_name, "zz9k_if") != 0) 117 1.1 phx return 0; 118 1.1 phx 119 1.1 phx return 1; 120 1.1 phx } 121 1.1 phx 122 1.1 phx static void 123 1.1 phx zzif_attach(device_t parent, device_t self, void *aux) 124 1.1 phx { 125 1.1 phx struct zz9kbus_attach_args *bap = aux; 126 1.1 phx struct zzif_softc *sc = device_private(self); 127 1.1 phx struct zz9k_softc *psc = device_private(parent); 128 1.1 phx struct ifnet *ifp = &sc->sc_ethercom.ec_if; 129 1.1 phx uint8_t lla[ETHER_ADDR_LEN]; 130 1.1 phx 131 1.1 phx sc->sc_dev = self; 132 1.1 phx sc->sc_bst.base = bap->zzaa_base; 133 1.1 phx sc->sc_bst.absm = &amiga_bus_stride_1; 134 1.1 phx sc->sc_iot = &sc->sc_bst; 135 1.1 phx sc->sc_regh = psc->sc_regh; 136 1.1 phx 137 1.1 phx if (bus_space_map(sc->sc_iot, ZZ9K_RX_BASE, ZZ9K_RX_SIZE, 138 1.1 phx BUS_SPACE_MAP_LINEAR, &sc->sc_rxh)) { 139 1.1 phx aprint_error(": Failed to map MNT ZZ9000 eth rx buffer.\n"); 140 1.1 phx return; 141 1.1 phx } 142 1.1 phx 143 1.1 phx if (bus_space_map(sc->sc_iot, ZZ9K_TX_BASE, ZZ9K_TX_SIZE, 144 1.1 phx BUS_SPACE_MAP_LINEAR, &sc->sc_txh)) { 145 1.1 phx aprint_error(": Failed to map MNT ZZ9000 eth tx buffer.\n"); 146 1.1 phx return; 147 1.1 phx } 148 1.1 phx 149 1.1 phx /* get MAC from NIC */ 150 1.1 phx uint16_t macHI = ZZREG_R(ZZ9K_ETH_MAC_HI); 151 1.1 phx uint16_t macMD = ZZREG_R(ZZ9K_ETH_MAC_MD); 152 1.1 phx uint16_t macLO = ZZREG_R(ZZ9K_ETH_MAC_LO); 153 1.1 phx lla[0] = macHI >> 8; 154 1.1 phx lla[1] = macHI & 0xff; 155 1.1 phx lla[2] = macMD >> 8; 156 1.1 phx lla[3] = macMD & 0xff; 157 1.1 phx lla[4] = macLO >> 8; 158 1.1 phx lla[5] = macLO & 0xff; 159 1.1 phx 160 1.1 phx #if 0 161 1.1 phx aprint_normal(": Ethernet address %s " 162 1.1 phx "(10BASE-T, 100BASE-TX, AUTO)\n", ether_sprintf(lla)); 163 1.1 phx #endif 164 1.1 phx 165 1.1 phx aprint_debug_dev(sc->sc_dev, "[DEBUG] registers at %p/%p (pa/va), " 166 1.1 phx "rx buffer at %p/%p (pa/va), tx buffer at %p/%p (pa/va)\n", 167 1.1 phx (void *)kvtop((void *)sc->sc_regh), 168 1.1 phx bus_space_vaddr(sc->sc_iot, sc->sc_regh), 169 1.1 phx (void *)kvtop((void *)sc->sc_rxh), 170 1.1 phx bus_space_vaddr(sc->sc_iot, sc->sc_rxh), 171 1.1 phx (void *)kvtop((void *)sc->sc_txh), 172 1.1 phx bus_space_vaddr(sc->sc_iot, sc->sc_txh)); 173 1.1 phx 174 1.1 phx /* set rx/tx buffers */ 175 1.1 phx sc->sc_txbuffer = bus_space_vaddr(sc->sc_iot, sc->sc_txh); 176 1.1 phx sc->sc_rxbuffer = bus_space_vaddr(sc->sc_iot, sc->sc_rxh); 177 1.1 phx 178 1.1 phx /* configure the network interface */ 179 1.1 phx memcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ); 180 1.1 phx ifp->if_softc = sc; 181 1.1 phx ifp->if_ioctl = zzif_ioctl; 182 1.1 phx ifp->if_start = zzif_start; 183 1.1 phx ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 184 1.1 phx ifp->if_mtu = ETHERMTU; 185 1.1 phx 186 1.1 phx /* attach the network interface. */ 187 1.1 phx if_attach(ifp); 188 1.1 phx if_deferred_start_init(ifp, NULL); 189 1.1 phx ether_ifattach(ifp, lla); 190 1.1 phx 191 1.1 phx aprint_normal(": Ethernet address %s\n", ether_sprintf(lla)); 192 1.1 phx 193 1.1 phx /* setup the (receiving) interrupt service routine on int6 (default) */ 194 1.1 phx sc->sc_isr.isr_intr = zzif_intr; 195 1.1 phx sc->sc_isr.isr_arg = sc; 196 1.1 phx sc->sc_isr.isr_ipl = 6; 197 1.1 phx add_isr(&sc->sc_isr); 198 1.1 phx } 199 1.1 phx 200 1.1 phx static void 201 1.1 phx zzif_init(struct zzif_softc *sc) 202 1.1 phx { 203 1.1 phx struct ifnet *ifp = &sc->sc_ethercom.ec_if; 204 1.1 phx int s = splvm(); 205 1.1 phx 206 1.1 phx ZZREG_W(ZZ9K_CONFIG, ZZREG_R(ZZ9K_CONFIG) | ZZ9K_CONFIG_INT_ETH); 207 1.1 phx ifp->if_flags |= IFF_RUNNING; 208 1.1 phx ifp->if_flags &= ~IFF_OACTIVE; 209 1.1 phx if_schedule_deferred_start(ifp); 210 1.1 phx splx(s); 211 1.1 phx } 212 1.1 phx 213 1.1 phx static void 214 1.1 phx zzif_stop(struct zzif_softc *sc) 215 1.1 phx { 216 1.1 phx struct ifnet *ifp = &sc->sc_ethercom.ec_if; 217 1.1 phx int s = splvm(); 218 1.1 phx 219 1.1 phx ZZREG_W(ZZ9K_CONFIG, ZZREG_R(ZZ9K_CONFIG) & ~ZZ9K_CONFIG_INT_ETH); 220 1.1 phx ifp->if_flags &= ~IFF_RUNNING; 221 1.1 phx splx(s); 222 1.1 phx } 223 1.1 phx 224 1.1 phx static void 225 1.1 phx zzif_start(struct ifnet *ifp) 226 1.1 phx { 227 1.1 phx struct zzif_softc *sc = ifp->if_softc; 228 1.1 phx uint8_t *frame= (uint8_t *)sc->sc_txbuffer; 229 1.1 phx struct mbuf *m; 230 1.1 phx int size; 231 1.1 phx 232 1.1 phx if ((ifp->if_flags & IFF_RUNNING) == 0) 233 1.1 phx return; 234 1.1 phx 235 1.1 phx ifp->if_flags |= IFF_OACTIVE; 236 1.1 phx 237 1.1 phx for (;;) { 238 1.1 phx IF_DEQUEUE(&ifp->if_snd, m); 239 1.1 phx if (m == NULL) 240 1.1 phx break; 241 1.1 phx 242 1.1 phx /* this should never happen, otherwise there's no help */ 243 1.1 phx if ((m->m_flags & M_PKTHDR) == 0) 244 1.1 phx panic("zzif_start: no packet header mbuf"); 245 1.1 phx 246 1.1 phx size = m->m_pkthdr.len; 247 1.1 phx if (size == 0) 248 1.1 phx panic("zzif_start: header mbuf with 0 len"); 249 1.1 phx 250 1.1 phx if ((size < (ETHER_HDR_LEN)) || 251 1.1 phx (size > (ETHER_MAX_LEN-ETHER_CRC_LEN))) { 252 1.1 phx aprint_error_dev(sc->sc_dev, 253 1.1 phx ": abnormal tx frame size of %i bytes\n", size); 254 1.1 phx m_freem(m); 255 1.1 phx break; 256 1.1 phx } 257 1.1 phx 258 1.1 phx /* make bpf happy */ 259 1.1 phx bpf_mtap(ifp, m, BPF_D_OUT); 260 1.1 phx 261 1.2 andvar /* copy dequeued mbuf data to transmit buffer of the ZZ9000 */ 262 1.1 phx for (struct mbuf *n = m; n != NULL; n = n->m_next) { 263 1.1 phx memcpy(frame, n->m_data, n->m_len); 264 1.1 phx frame += n->m_len; 265 1.1 phx } 266 1.1 phx 267 1.1 phx m_freem(m); /* we don't need it anymore */ 268 1.1 phx 269 1.1 phx /* tell ZZ9000 to transmit packet with size and check result */ 270 1.1 phx ZZREG_W(ZZ9K_ETH_TX, size); 271 1.1 phx size = ZZREG_R(ZZ9K_ETH_TX); 272 1.1 phx if (size == 0) { 273 1.1 phx if_statinc(ifp, if_opackets); 274 1.1 phx } else { 275 1.1 phx if_statinc(ifp, if_oerrors); 276 1.1 phx } 277 1.1 phx } 278 1.1 phx 279 1.1 phx ifp->if_flags &= ~IFF_OACTIVE; 280 1.1 phx } 281 1.1 phx 282 1.1 phx static int 283 1.1 phx zzif_intr(void *arg) 284 1.1 phx { 285 1.1 phx struct zzif_softc *sc = arg; 286 1.1 phx 287 1.1 phx uint16_t conf = ZZREG_R(ZZ9K_CONFIG); 288 1.1 phx if ((conf & ZZ9K_CONFIG_INT_ETH) == 0) 289 1.1 phx return 0; 290 1.1 phx 291 1.1 phx ZZREG_W(ZZ9K_CONFIG, conf & ~ZZ9K_CONFIG_INT_ETH); /* disable INT */ 292 1.1 phx ZZREG_W(ZZ9K_CONFIG, ZZ9K_CONFIG_INT_ACK | ZZ9K_CONFIG_INT_ACK_ETH); 293 1.1 phx 294 1.1 phx struct zzif_frame *frame = (struct zzif_frame *)sc->sc_rxbuffer; 295 1.1 phx struct ifnet *ifp = &sc->sc_ethercom.ec_if; 296 1.1 phx struct mbuf *m; 297 1.1 phx 298 1.1 phx /* check if interrupt was due to new packet arrival */ 299 1.1 phx if (frame->serial == sc->sc_sequence) { 300 1.1 phx ZZREG_W(ZZ9K_ETH_RX, 1); /* discard packet */ 301 1.1 phx ZZREG_W(ZZ9K_CONFIG, conf); /* restore interrupt */ 302 1.1 phx return 0; /* nope, something else triggered it */ 303 1.1 phx } 304 1.1 phx 305 1.1 phx sc->sc_sequence = frame->serial; 306 1.1 phx 307 1.1 phx if ((frame->size < (ETHER_MIN_LEN-ETHER_CRC_LEN)) || 308 1.1 phx (frame->size > ETHER_MAX_LEN)) { 309 1.1 phx aprint_error_dev(sc->sc_dev, 310 1.1 phx ": abnormal rx frame size of %i bytes\n", frame->size); 311 1.1 phx ZZREG_W(ZZ9K_ETH_RX, 1); /* discard packet */ 312 1.1 phx ZZREG_W(ZZ9K_CONFIG, conf); /* restore interrupt */ 313 1.1 phx return 0; 314 1.1 phx } 315 1.1 phx 316 1.1 phx int s = splvm(); 317 1.1 phx m = m_devget((char *)&frame->header, frame->size, 0, ifp); 318 1.1 phx if_percpuq_enqueue(ifp->if_percpuq, m); 319 1.1 phx splx(s); 320 1.1 phx ZZREG_W(ZZ9K_ETH_RX, 1); /* packet served */ 321 1.1 phx ZZREG_W(ZZ9K_CONFIG, conf); /* restore interrupt */ 322 1.1 phx 323 1.1 phx return 1; 324 1.1 phx } 325 1.1 phx 326 1.1 phx static int 327 1.1 phx zzif_ioctl(struct ifnet *ifp, u_long cmd, void *data) 328 1.1 phx { 329 1.1 phx struct zzif_softc *sc = ifp->if_softc; 330 1.1 phx struct ifaddr *ifa; 331 1.1 phx struct if_laddrreq *lar; 332 1.1 phx const struct sockaddr_dl *sdl; 333 1.1 phx int retval = 0; 334 1.1 phx 335 1.1 phx int s = splvm(); 336 1.1 phx 337 1.1 phx switch (cmd) { 338 1.1 phx case SIOCINITIFADDR: 339 1.1 phx ifa = (struct ifaddr *)data; 340 1.1 phx zzif_stop(sc); 341 1.1 phx zzif_init(sc); 342 1.1 phx switch (ifa->ifa_addr->sa_family) { 343 1.1 phx case AF_INET: 344 1.1 phx arp_ifinit(ifp, ifa); 345 1.1 phx break; 346 1.1 phx default: 347 1.1 phx break; 348 1.1 phx } 349 1.1 phx break; 350 1.1 phx case SIOCSIFFLAGS: 351 1.1 phx retval = ifioctl_common(ifp, cmd, data); 352 1.1 phx if (retval != 0) 353 1.1 phx break; 354 1.1 phx 355 1.1 phx switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { 356 1.1 phx case IFF_RUNNING: /* and not UP */ 357 1.1 phx zzif_stop(sc); 358 1.1 phx break; 359 1.1 phx case IFF_UP: /* and not RUNNING */ 360 1.1 phx zzif_init(sc); 361 1.1 phx break; 362 1.1 phx case (IFF_UP | IFF_RUNNING): 363 1.1 phx zzif_stop(sc); 364 1.1 phx zzif_init(sc); 365 1.1 phx break; 366 1.1 phx default: 367 1.1 phx break; 368 1.1 phx } 369 1.1 phx break; 370 1.1 phx case SIOCADDMULTI: 371 1.1 phx case SIOCDELMULTI: 372 1.1 phx retval = EINVAL; 373 1.1 phx break; 374 1.1 phx case SIOCALIFADDR: 375 1.1 phx lar = (struct if_laddrreq *)data; 376 1.1 phx sdl = satocsdl(&lar->addr); 377 1.1 phx uint8_t *mac = (uint8_t *)&sdl->sdl_addr.dl_data; 378 1.1 phx 379 1.1 phx if ((lar->flags==IFLR_ACTIVE) && (sdl->sdl_family==AF_LINK)) { 380 1.1 phx ZZREG_W(ZZ9K_ETH_MAC_HI, (mac[0] << 8) | mac[1]); 381 1.1 phx ZZREG_W(ZZ9K_ETH_MAC_MD, (mac[2] << 8) | mac[3]); 382 1.1 phx ZZREG_W(ZZ9K_ETH_MAC_LO, (mac[4] << 8) | mac[5]); 383 1.1 phx } 384 1.1 phx aprint_normal(": Ethernet address %s", ether_sprintf(mac)); 385 1.1 phx retval = ether_ioctl(ifp, cmd, data); 386 1.1 phx break; 387 1.1 phx default: 388 1.1 phx retval = ether_ioctl(ifp, cmd, data); 389 1.1 phx break; 390 1.1 phx } 391 1.1 phx 392 1.1 phx splx(s); 393 1.1 phx 394 1.1 phx return retval; 395 1.1 phx } 396