3 * Copyright (c) 2008 The NetBSD Foundation, Inc.
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Matt Thomas <matt@3am-software.com>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
33 #include "opt_gemini.h"
36 __KERNEL_RCSID(0, "$NetBSD$");
38 #include <sys/param.h>
39 #include <sys/device.h>
41 #include <sys/ioctl.h>
44 #include <net/if_media.h>
45 #include <net/if_ether.h>
46 #include <net/if_dl.h>
52 #include <machine/bus.h>
54 #include <arm/gemini/gemini_var.h>
55 #include <arm/gemini/gemini_ipm.h>
57 #define GPN_MOF 0x00 /* Middle Of Frame */
58 #define GPN_SOF 0x01 /* Start of Frame */
59 #define GPN_EOF 0x02 /* End of Frame */
60 #define GPN_FRAME 0x03 /* Complete Frame */
62 #define GPN_IFUP 0x05 /* partner is up */
63 #define GPN_IFDOWN 0x06 /* partner is down */
65 #define GPN_ACK0 0x10 /* placeholder */
66 #define GPN_ACK1 0x11 /* Ack 1 descriptor */
67 #define GPN_ACK2 0x12 /* Ack 2 descriptors */
68 #define GPN_ACK3 0x13 /* Ack 3 descriptors */
69 #define GPN_ACK4 0x14 /* Ack 4 descriptors */
70 #define GPN_ACK5 0x15 /* Ack 5 descriptors */
71 #define GPN_ACK6 0x16 /* Ack 6 descriptors */
72 #define GPN_ACK7 0x17 /* Ack 7 descriptors */
73 #define GPN_ACK8 0x18 /* Ack 8 descriptors */
74 #define GPN_ACK9 0x19 /* Ack 9 descriptors */
75 #define GPN_ACK10 0x1a /* Ack 10 descriptors */
76 #define GPN_ACK11 0x1b /* Ack 11 descriptors */
77 #define GPN_ACK12 0x1c /* Ack 12 descriptors */
78 #define GPN_ACK13 0x1d /* Ack 13 descriptors */
79 #define GPN_ACK14 0x1e /* Ack 14 descriptors */
95 uint8_t agd_txids
[14];
98 #define MAX_TXACTIVE 60
101 struct mbuf
*ti_mbuf
;
107 bus_dma_tag_t sc_dmat
;
108 struct ifmedia sc_im
;
109 struct ethercom sc_ec
;
110 #define sc_if sc_ec.ec_if
114 ipm_gpn_ack_desc_t sc_ack_desc
;
115 struct mbuf
*sc_rxmbuf
;
116 struct gpn_txinfo sc_txinfo
[MAX_TXACTIVE
];
118 bool sc_remoteup
; /* remote side up? */
121 CTASSERT((GPN_SOF
| GPN_EOF
) == GPN_FRAME
);
122 CTASSERT((GPN_SOF
& GPN_EOF
) == 0);
124 extern struct cfdriver gpn_cd
;
126 static void gpn_ifstart(struct ifnet
*);
130 m_crc32_le(struct mbuf
*m
)
132 static const uint32_t crctab
[] = {
133 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
134 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
135 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
136 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
141 crc
= 0xffffffffU
; /* initial value */
143 for (; m
; m
= m
->m_next
) {
144 for (i
= 0; i
< m
->m_len
; i
++) {
146 crc
= (crc
>> 4) ^ crctab
[crc
& 0xf];
147 crc
= (crc
>> 4) ^ crctab
[crc
& 0xf];
156 gpn_free_dmamaps(struct gpn_softc
*sc
)
158 struct gpn_txinfo
*ti
= sc
->sc_txinfo
;
159 struct gpn_txinfo
* const end_ti
= ti
+ __arraycount(sc
->sc_txinfo
);
161 for (; ti
< end_ti
; ti
++) {
162 if (ti
->ti_map
== NULL
)
164 bus_dmamap_destroy(sc
->sc_dmat
, ti
->ti_map
);
170 gpn_alloc_dmamaps(struct gpn_softc
*sc
)
172 struct gpn_txinfo
*ti
= sc
->sc_txinfo
;
173 struct gpn_txinfo
* const end_ti
= ti
+ __arraycount(sc
->sc_txinfo
);
176 for (error
= 0; ti
< end_ti
; ti
++) {
177 if (ti
->ti_map
!= NULL
)
179 error
= bus_dmamap_create(sc
->sc_dmat
,
181 BUS_DMA_ALLOCNOW
|BUS_DMA_WAITOK
,
188 gpn_free_dmamaps(sc
);
194 gpn_add_data(struct gpn_softc
*sc
, bus_addr_t addr
, bus_size_t len
)
202 m
->m_pkthdr
.len
+= len
;
204 while (m
->m_next
!= NULL
)
208 space
= M_TRAILINGSPACE(m
);
213 gemini_ipm_copyin(mtod(m
, uint8_t *) + m
->m_len
, addr
,
221 MGET(m0
, M_DONTWAIT
, MT_DATA
);
226 MCLGET(m0
, M_DONTWAIT
);
227 if (m0
->m_flags
& M_EXT
)
238 gpn_ack_txid(struct gpn_softc
*sc
, unsigned int txid
)
240 ipm_gpn_ack_desc_t
* const agd
= &sc
->sc_ack_desc
;
241 agd
->agd_txids
[agd
->agd_subtype
] = txid
;
242 if (++agd
->agd_subtype
== __arraycount(agd
->agd_txids
)) {
243 agd
->agd_subtype
+= GPN_ACK0
;
245 gemini_ipm_produce(agd
, 1);
246 agd
->agd_subtype
= 0;
251 gpn_process_data(struct gpn_softc
*sc
, const ipm_gpn_desc_t
*gd
)
253 struct ifnet
* const ifp
= &sc
->sc_if
;
254 size_t pktlen
= gd
->gd_pktlen64
* 64;
255 unsigned int subtype
= gd
->gd_subtype
;
258 if ((subtype
& GPN_SOF
) == 0 && sc
->sc_rxmbuf
== NULL
) {
263 if ((subtype
& GPN_SOF
) && sc
->sc_rxmbuf
!= NULL
) {
265 m_freem(sc
->sc_rxmbuf
);
266 sc
->sc_rxmbuf
= NULL
;
269 if (sc
->sc_rxmbuf
== NULL
) {
271 MGETHDR(m
, M_DONTWAIT
, MT_DATA
);
276 if (pktlen
> MHLEN
- 2) {
277 MCLGET(m
, M_DONTWAIT
);
278 if ((m
->m_flags
& M_EXT
) == 0) {
284 m
->m_data
+= 2; /* makes ethernet payload 32bit aligned */
290 ok
= gpn_add_data(sc
, gd
->gd_addr1
, gd
->gd_len1
);
291 if (ok
&& gd
->gd_addr2
&& gd
->gd_len2
)
292 ok
= gpn_add_data(sc
, gd
->gd_addr2
, gd
->gd_len2
);
295 m_freem(sc
->sc_rxmbuf
);
296 sc
->sc_rxmbuf
= NULL
;
300 if (subtype
& GPN_EOF
) {
303 sc
->sc_rxmbuf
= NULL
;
304 m
->m_pkthdr
.rcvif
= ifp
;
305 KASSERT(((m
->m_pkthdr
.len
+ 63) >> 6) == gd
->gd_pktlen64
);
307 ifp
->if_ibytes
+= m
->m_pkthdr
.len
;
310 bpf_mtap(ifp
->if_bpf
, m
);
313 printf("%s: rx len=%d crc=%#x\n", ifp
->if_xname
,
314 m
->m_pkthdr
.len
, m_crc32_le(m
));
316 (*ifp
->if_input
)(ifp
, m
);
320 gpn_ack_txid(sc
, gd
->gd_txid
);
324 gpn_free_txid(struct gpn_softc
*sc
, size_t txid
)
326 struct gpn_txinfo
* const ti
= sc
->sc_txinfo
+ txid
;
328 KASSERT(txid
< MAX_TXACTIVE
);
330 if (ti
->ti_mbuf
== NULL
)
333 bus_dmamap_sync(sc
->sc_dmat
, ti
->ti_map
,
334 0, ti
->ti_mbuf
->m_len
, BUS_DMASYNC_POSTREAD
);
335 bus_dmamap_unload(sc
->sc_dmat
, ti
->ti_map
);
336 m_freem(ti
->ti_mbuf
);
339 KASSERT(sc
->sc_txactive
< MAX_TXACTIVE
);
340 if (sc
->sc_if
.if_flags
& IFF_OACTIVE
) {
341 sc
->sc_if
.if_flags
&= ~IFF_OACTIVE
;
342 gpn_ifstart(&sc
->sc_if
);
348 gpn_ipm_rebate(void *arg
, size_t count
)
350 struct gpn_softc
* const sc
= arg
;
354 sc
->sc_free
+= count
;
356 sc
->sc_if
.if_flags
&= ~IFF_OACTIVE
;
357 gpn_ifstart(&sc
->sc_if
);
362 gpn_ifstart(struct ifnet
*ifp
)
364 struct gpn_softc
* const sc
= ifp
->if_softc
;
369 ipm_gpn_desc_t
*last_gd
;
372 if (sc
->sc_free
== 0) {
373 ifp
->if_flags
|= IFF_OACTIVE
;
377 IF_DEQUEUE(&ifp
->if_snd
, m
);
381 if ((ifp
->if_flags
& IFF_UP
) == 0) {
387 * Make sure to send any pending acks first.
389 if (sc
->sc_ack_desc
.agd_subtype
) {
391 sc
->sc_ack_desc
.agd_subtype
+= GPN_ACK0
;
392 gemini_ipm_produce(&sc
->sc_ack_desc
, 1);
393 sc
->sc_ack_desc
.agd_subtype
= 0;
397 * Let's find out how many mbufs we are using.
399 for (m0
= m
, count
= 0; m0
; m0
= m0
->m_next
) {
406 * Make sure there is always enough room.
408 if (sc
->sc_free
< count
409 || sc
->sc_txactive
+ count
> MAX_TXACTIVE
) {
410 IF_PREPEND(&ifp
->if_snd
, m
);
411 ifp
->if_flags
|= IFF_OACTIVE
;
417 bpf_mtap(ifp
->if_bpf
, m
);
420 printf("%s: tx len=%d crc=%#x\n", ifp
->if_xname
,
421 m
->m_pkthdr
.len
, m_crc32_le(m
));
425 gd
.gd_tag
= IPM_TAG_GPN
;
426 gd
.gd_subtype
= GPN_SOF
;
427 gd
.gd_pktlen64
= (m
->m_pkthdr
.len
+ 63) >> 6;
428 for (; m
!= NULL
; m
= m0
) {
429 struct gpn_txinfo
*ti
;
443 gemini_ipm_produce(last_gd
, 1);
445 gd
.gd_subtype
= GPN_MOF
;
447 for (id
= sc
->sc_lastid
;
448 sc
->sc_txinfo
[id
].ti_mbuf
!= NULL
;) {
449 if (++id
== __arraycount(sc
->sc_txinfo
))
452 KASSERT(id
< MAX_TXACTIVE
);
453 ti
= sc
->sc_txinfo
+ id
;
455 error
= bus_dmamap_load(sc
->sc_dmat
, map
,
456 mtod(m
, void *), m
->m_len
, NULL
,
457 BUS_DMA_READ
|BUS_DMA_NOWAIT
);
463 bus_dmamap_sync(sc
->sc_dmat
, map
, 0,
464 m
->m_len
, BUS_DMASYNC_PREREAD
);
465 KASSERT(map
->dm_nsegs
> 0);
466 KASSERT(map
->dm_nsegs
<= 2);
467 KASSERT(map
->dm_segs
[0].ds_addr
!= 0);
468 gd
.gd_len1
= map
->dm_segs
[0].ds_len
;
469 gd
.gd_addr1
= map
->dm_segs
[0].ds_addr
;
470 if (map
->dm_nsegs
== 1) {
474 KASSERT(map
->dm_segs
[0].ds_addr
!= 0);
475 gd
.gd_len2
= map
->dm_segs
[1].ds_len
;
476 gd
.gd_addr2
= map
->dm_segs
[1].ds_addr
;
482 ifp
->if_obytes
+= m
->m_len
;
485 last_gd
->gd_subtype
|= GPN_EOF
;
488 gemini_ipm_produce(last_gd
, 1);
493 gpn_ipm_ifup(struct gpn_softc
*sc
)
495 sc
->sc_remoteup
= true;
496 if (sc
->sc_if
.if_flags
& IFF_UP
)
497 ifmedia_set(&sc
->sc_im
, IFM_ETHER
|IFM_1000_T
|IFM_FDX
);
501 gpn_ipm_ifdown(struct gpn_softc
*sc
)
503 struct gpn_txinfo
*ti
= sc
->sc_txinfo
;
504 struct gpn_txinfo
* const end_ti
= ti
+ __arraycount(sc
->sc_txinfo
);
507 m_freem(sc
->sc_rxmbuf
);
508 sc
->sc_rxmbuf
= NULL
;
511 IF_PURGE(&sc
->sc_if
.if_snd
);
513 for (; ti
< end_ti
; ti
++) {
514 if (ti
->ti_mbuf
== NULL
)
516 bus_dmamap_sync(sc
->sc_dmat
, ti
->ti_map
,
517 0, ti
->ti_mbuf
->m_len
, BUS_DMASYNC_POSTREAD
);
518 bus_dmamap_unload(sc
->sc_dmat
, ti
->ti_map
);
519 m_freem(ti
->ti_mbuf
);
523 ifmedia_set(&sc
->sc_im
, IFM_ETHER
|IFM_NONE
);
524 sc
->sc_remoteup
= false;
528 gpn_ipm_handler(void *arg
, const void *desc
)
530 struct gpn_softc
* const sc
= arg
;
531 const ipm_gpn_desc_t
* const gd
= desc
;
532 const ipm_gpn_ack_desc_t
* const agd
= desc
;
537 switch (gd
->gd_subtype
) {
538 case GPN_ACK14
: gpn_free_txid(sc
, agd
->agd_txids
[13]); /* FALLTHROUGH */
539 case GPN_ACK13
: gpn_free_txid(sc
, agd
->agd_txids
[12]); /* FALLTHROUGH */
540 case GPN_ACK12
: gpn_free_txid(sc
, agd
->agd_txids
[11]); /* FALLTHROUGH */
541 case GPN_ACK11
: gpn_free_txid(sc
, agd
->agd_txids
[10]); /* FALLTHROUGH */
542 case GPN_ACK10
: gpn_free_txid(sc
, agd
->agd_txids
[9]); /* FALLTHROUGH */
543 case GPN_ACK9
: gpn_free_txid(sc
, agd
->agd_txids
[8]); /* FALLTHROUGH */
544 case GPN_ACK8
: gpn_free_txid(sc
, agd
->agd_txids
[7]); /* FALLTHROUGH */
545 case GPN_ACK7
: gpn_free_txid(sc
, agd
->agd_txids
[6]); /* FALLTHROUGH */
546 case GPN_ACK6
: gpn_free_txid(sc
, agd
->agd_txids
[5]); /* FALLTHROUGH */
547 case GPN_ACK5
: gpn_free_txid(sc
, agd
->agd_txids
[4]); /* FALLTHROUGH */
548 case GPN_ACK4
: gpn_free_txid(sc
, agd
->agd_txids
[3]); /* FALLTHROUGH */
549 case GPN_ACK3
: gpn_free_txid(sc
, agd
->agd_txids
[2]); /* FALLTHROUGH */
550 case GPN_ACK2
: gpn_free_txid(sc
, agd
->agd_txids
[1]); /* FALLTHROUGH */
551 case GPN_ACK1
: gpn_free_txid(sc
, agd
->agd_txids
[0]); break;
556 gpn_process_data(sc
, gd
);
572 gpn_ifinit(struct ifnet
*ifp
)
574 struct gpn_softc
* const sc
= ifp
->if_softc
;
578 error
= gpn_alloc_dmamaps(sc
);
582 memset(&gd
, 0, sizeof(gd
));
583 gd
.gd_tag
= IPM_TAG_GPN
;
584 gd
.gd_subtype
= GPN_IFUP
;
585 KASSERT(sc
->sc_free
> 0);
587 gemini_ipm_produce(&gd
, 1);
590 ifmedia_set(&sc
->sc_im
, IFM_ETHER
|IFM_1000_T
|IFM_FDX
);
592 ifp
->if_flags
|= IFF_RUNNING
;
598 gpn_ifstop(struct ifnet
*ifp
, int disable
)
600 struct gpn_softc
* const sc
= ifp
->if_softc
;
603 memset(&gd
, 0, sizeof(gd
));
604 gd
.gd_tag
= IPM_TAG_GPN
;
605 gd
.gd_subtype
= GPN_IFDOWN
;
606 KASSERT(sc
->sc_free
> 0);
608 gemini_ipm_produce(&gd
, 1);
609 ifp
->if_flags
&= ~IFF_RUNNING
;
613 gpn_free_dmamaps(sc
);
618 gpn_ifioctl(struct ifnet
*ifp
, u_long cmd
, void *data
)
620 struct gpn_softc
* const sc
= ifp
->if_softc
;
621 struct ifreq
* const ifr
= data
;
622 struct ifaliasreq
* const ifra
= data
;
630 error
= ifmedia_ioctl(ifp
, ifr
, &sc
->sc_im
, cmd
);
632 case SIOCSIFPHYADDR
: {
633 const struct sockaddr_dl
*sdl
= satosdl(&ifra
->ifra_addr
);
635 if (sdl
->sdl_family
!= AF_LINK
) {
640 if_set_sadl(ifp
, CLLADDR(sdl
), ETHER_ADDR_LEN
, false);
645 error
= ether_ioctl(ifp
, cmd
, data
);
646 if (error
== ENETRESET
)
656 gpn_mediachange(struct ifnet
*ifp
)
662 gpn_mediastatus(struct ifnet
*ifp
, struct ifmediareq
*imr
)
664 struct gpn_softc
* const sc
= ifp
->if_softc
;
665 imr
->ifm_active
= sc
->sc_im
.ifm_cur
->ifm_media
;
669 gpn_match(device_t parent
, cfdata_t cf
, void *aux
)
671 return strcmp(gpn_cd
.cd_name
, aux
) == 0;
675 gpn_attach(device_t parent
, device_t self
, void *aux
)
677 struct gpn_softc
* const sc
= device_private(self
);
678 struct ifnet
* const ifp
= &sc
->sc_if
;
688 #elif defined(GEMINI_SLAVE)
691 #error not master nor slave
697 sc
->sc_dmat
= &gemini_bus_dma_tag
;
700 * Pretend we are full-duplex gigabit ethernet.
702 ifmedia_init(&sc
->sc_im
, 0, gpn_mediachange
, gpn_mediastatus
);
703 ifmedia_add(&sc
->sc_im
, IFM_ETHER
|IFM_1000_T
|IFM_FDX
, 0, NULL
);
704 ifmedia_add(&sc
->sc_im
, IFM_ETHER
|IFM_NONE
, 0, NULL
);
705 ifmedia_set(&sc
->sc_im
, IFM_ETHER
|IFM_NONE
);
707 strlcpy(ifp
->if_xname
, device_xname(self
), sizeof(ifp
->if_xname
));
709 ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
| IFF_MULTICAST
;
710 ifp
->if_ioctl
= gpn_ifioctl
;
711 ifp
->if_start
= gpn_ifstart
;
712 ifp
->if_init
= gpn_ifinit
;
713 ifp
->if_stop
= gpn_ifstop
;
715 IFQ_SET_READY(&ifp
->if_snd
);
717 sc
->sc_ec
.ec_capabilities
= ETHERCAP_VLAN_MTU
| ETHERCAP_JUMBO_MTU
;
720 ether_ifattach(ifp
, enaddr
);
722 sc
->sc_free
= MAX_TXACTIVE
*2;
723 sc
->sc_ih
= gemini_ipm_register(IPM_TAG_GPN
, IPL_SOFTNET
, sc
->sc_free
,
724 gpn_ipm_handler
, gpn_ipm_rebate
, sc
);
727 sc
->sc_ack_desc
.agd_tag
= IPM_TAG_GPN
;
730 void gpn_print_gd(ipm_gpn_desc_t
*);
732 gpn_print_gd(ipm_gpn_desc_t
*gd
)
734 printf("%s: %p\n", __FUNCTION__
, gd
);
735 printf("\ttag %d, subtype %d, id %d, pktlen64 %d\n",
736 gd
->gd_tag
, gd
->gd_subtype
, gd
->gd_txid
, gd
->gd_pktlen64
);
737 printf("\tlen1 %d, len2 %d, addr1 %#x, addr2 %#x\n",
738 gd
->gd_len1
, gd
->gd_len2
, gd
->gd_addr1
, gd
->gd_addr2
);
741 CFATTACH_DECL_NEW(gpn
, sizeof(struct gpn_softc
),
742 gpn_match
, gpn_attach
, NULL
, NULL
);