if_myx.c revision 1.1
1/*	$OpenBSD: if_myx.c,v 1.1 2007/05/31 18:23:42 reyk Exp $	*/
2
3/*
4 * Copyright (c) 2007 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * Driver for the Myricom Myri-10G Lanai-Z8E Ethernet chipsets.
21 */
22
23#include "bpfilter.h"
24
25#include <sys/param.h>
26#include <sys/systm.h>
27#include <sys/sockio.h>
28#include <sys/mbuf.h>
29#include <sys/kernel.h>
30#include <sys/socket.h>
31#include <sys/malloc.h>
32#include <sys/timeout.h>
33#include <sys/proc.h>
34#include <sys/device.h>
35#include <sys/sensors.h>
36
37#include <machine/bus.h>
38#include <machine/intr.h>
39
40#include <net/if.h>
41#include <net/if_dl.h>
42#include <net/if_media.h>
43#include <net/if_types.h>
44
45#if NBPFILTER > 0
46#include <net/bpf.h>
47#endif
48
49#ifdef INET
50#include <netinet/in.h>
51#include <netinet/if_ether.h>
52#endif
53
54#include <dev/pci/pcireg.h>
55#include <dev/pci/pcivar.h>
56#include <dev/pci/pcidevs.h>
57
58#include <dev/pci/if_myxreg.h>
59
60#ifdef MYX_DEBUG
61#define MYXDBG_INIT	(1<<0)	/* chipset initialization */
62#define MYXDBG_CMD	(2<<0)	/* commands */
63#define MYXDBG_INTR	(2<<0)	/* interrupts */
64#define MYXDBG_ALL	0xffff	/* enable all debugging messages */
65int myx_debug = 0;
66#define DPRINTF(_lvl, _arg...)	do {					\
67	if (myx_debug & (_lvl))						\
68		printf(_arg);						\
69} while (0)
70#else
71#define DPRINTF(_lvl, arg...)
72#endif
73
74#define DEVNAME(_s)	((_s)->_s##_dev.dv_xname)
75
76struct myx_dmamem {
77	bus_dmamap_t		 mxm_map;
78	bus_dma_segment_t	 mxm_seg;
79	int			 mxm_nsegs;
80	size_t			 mxm_size;
81	caddr_t			 mxm_kva;
82	const char		*mxm_name;
83};
84
85struct myx_softc {
86	struct device		 sc_dev;
87	struct arpcom		 sc_ac;
88
89	pci_chipset_tag_t	 sc_pc;
90	pcitag_t		 sc_tag;
91	u_int			 sc_function;
92
93	bus_dma_tag_t		 sc_dmat;
94	bus_space_tag_t		 sc_memt;
95	bus_space_handle_t	 sc_memh;
96	bus_size_t		 sc_mems;
97
98	struct myx_dmamem	 sc_cmddma;
99	struct myx_dmamem	 sc_paddma;
100
101	struct myx_dmamem	 sc_stsdma;
102	struct myx_status	*sc_sts;
103
104	struct myx_dmamem	 sc_rxdma;
105	struct myx_rxdesc	*sc_rxdesc;
106	int			 sc_rxactive;
107	int			 sc_rxidx;
108
109	void			*sc_irqh;
110	u_int32_t		 sc_irqcoaloff;
111	u_int32_t		 sc_irqclaimoff;
112	u_int32_t		 sc_irqdeassertoff;
113
114	u_int8_t		 sc_lladdr[ETHER_ADDR_LEN];
115	struct ifmedia		 sc_media;
116
117	int			 sc_txringsize;
118	int			 sc_rxringsize;
119	int			 sc_txndesc;
120	int			 sc_rxndesc;
121	size_t			 sc_rxdescsize;
122
123	u_int			 sc_phy;	/* PHY type (CX4/SR/LR) */
124	u_int			 sc_hwflags;
125#define  MYXFLAG_FLOW_CONTROL	 (1<<0)		/* Rx/Tx pause is enabled */
126#define  MYXFLAG_PROMISC	 (1<<1)		/* promisc mode is enabled */
127#define  MYXFLAG_ALLMULTI	 (1<<2)		/* allmulti is set */
128	u_int8_t		 sc_active;
129
130	struct timeout		 sc_tick;
131};
132
133int	 myx_match(struct device *, void *, void *);
134void	 myx_attach(struct device *, struct device *, void *);
135int	 myx_query(struct myx_softc *sc);
136u_int	 myx_ether_aton(char *, u_int8_t *, u_int);
137int	 myx_loadfirmware(struct myx_softc *, u_int8_t *, size_t,
138	    u_int32_t, int);
139void	 myx_attachhook(void *);
140void	 myx_read(struct myx_softc *, bus_size_t, u_int8_t *, bus_size_t);
141void	 myx_write(struct myx_softc *, bus_size_t, u_int8_t *, bus_size_t);
142int	 myx_cmd(struct myx_softc *, u_int32_t, struct myx_cmd *, u_int32_t *);
143int	 myx_boot(struct myx_softc *, u_int32_t, struct myx_bootcmd *);
144int	 myx_rdma(struct myx_softc *, u_int);
145int	 myx_reset(struct myx_softc *);
146int	 myx_dmamem_alloc(struct myx_softc *, struct myx_dmamem *,
147	    bus_size_t, u_int align, const char *);
148void	 myx_dmamem_free(struct myx_softc *, struct myx_dmamem *);
149int	 myx_media_change(struct ifnet *);
150void	 myx_media_status(struct ifnet *, struct ifmediareq *);
151void	 myx_link_state(struct myx_softc *);
152void	 myx_watchdog(struct ifnet *);
153void	 myx_tick(void *);
154int	 myx_ioctl(struct ifnet *, u_long, caddr_t);
155void	 myx_iff(struct myx_softc *);
156void	 myx_init(struct ifnet *);
157void	 myx_start(struct ifnet *);
158void	 myx_stop(struct ifnet *);
159int	 myx_setlladdr(struct myx_softc *, u_int8_t *);
160int	 myx_intr(void *);
161
162struct cfdriver myx_cd = {
163	0, "myx", DV_IFNET
164};
165struct cfattach myx_ca = {
166	sizeof(struct myx_softc), myx_match, myx_attach
167};
168
169const struct pci_matchid myx_devices[] = {
170	{ PCI_VENDOR_MYRICOM, PCI_PRODUCT_MYRICOM_Z8E }
171};
172
173int
174myx_match(struct device *parent, void *match, void *aux)
175{
176	return (pci_matchbyid((struct pci_attach_args *)aux,
177	    myx_devices, sizeof(myx_devices) / sizeof(myx_devices[0])));
178}
179
180void
181myx_attach(struct device *parent, struct device *self, void *aux)
182{
183	struct myx_softc	*sc = (struct myx_softc *)self;
184	struct pci_attach_args	*pa = aux;
185	pci_intr_handle_t	 ih;
186	pcireg_t		 memtype;
187	const char		*intrstr;
188	struct ifnet		*ifp;
189
190	sc->sc_pc = pa->pa_pc;
191	sc->sc_tag = pa->pa_tag;
192	sc->sc_dmat = pa->pa_dmat;
193	sc->sc_function = pa->pa_function;
194
195	memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, MYXBAR0);
196	switch (memtype) {
197	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
198	case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
199		break;
200	default:
201		printf(": invalid memory type: 0x%x\n", memtype);
202		return;
203	}
204
205	/* Map the PCI memory space */
206	if (pci_mapreg_map(pa, MYXBAR0, memtype, 0, &sc->sc_memt,
207	    &sc->sc_memh, NULL, &sc->sc_mems, 0) != 0) {
208		printf(": unable to map register memory\n");
209		return;
210	}
211
212	/* Get the board information and initialize the h/w */
213	if (myx_query(sc) != 0)
214		goto unmap;
215
216	/*
217	 * Allocate command DMA memory
218	 */
219	if (myx_dmamem_alloc(sc, &sc->sc_cmddma,
220	    sizeof(struct myx_cmd), MYXALIGN_CMD, "cmd") != 0) {
221		printf(": failed to allocate command DMA memory\n");
222		goto unmap;
223	}
224
225	if (myx_dmamem_alloc(sc, &sc->sc_paddma,
226	    MYXALIGN_CMD, MYXALIGN_CMD, "pad") != 0) {
227		printf(": failed to allocate pad DMA memory\n");
228		goto err3;
229	}
230
231	if (myx_dmamem_alloc(sc, &sc->sc_stsdma,
232	    sizeof(struct myx_status), MYXALIGN_CMD, "status") != 0) {
233		printf(": failed to allocate status DMA memory\n");
234		goto err2;
235	}
236	sc->sc_sts = (struct myx_status *)sc->sc_stsdma.mxm_kva;
237
238	sc->sc_rxdescsize = MYX_NRXDESC * sizeof(struct myx_rxdesc);
239	if (myx_dmamem_alloc(sc, &sc->sc_rxdma,
240	    sc->sc_rxdescsize, MYXALIGN_DATA, "rxdesc") != 0) {
241		printf(": failed to allocate Rx descriptor DMA memory\n");
242		goto err1;
243	}
244	sc->sc_rxdesc = (struct myx_rxdesc *)sc->sc_rxdma.mxm_kva;
245
246	/*
247	 * Map and establish the interrupt
248	 */
249	if (pci_intr_map(pa, &ih) != 0) {
250		printf(": unable to map interrupt\n");
251		goto err;
252	}
253	intrstr = pci_intr_string(pa->pa_pc, ih);
254	sc->sc_irqh = pci_intr_establish(pa->pa_pc, ih, IPL_NET,
255	    myx_intr, sc, DEVNAME(sc));
256	if (sc->sc_irqh == NULL) {
257		printf(": unable to establish interrupt %s\n", intrstr);
258		goto err;
259	}
260	printf(": %s, address %s\n", intrstr,
261	    ether_sprintf(sc->sc_ac.ac_enaddr));
262
263	ifp = &sc->sc_ac.ac_if;
264	ifp->if_softc = sc;
265	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
266	ifp->if_ioctl = myx_ioctl;
267	ifp->if_start = myx_start;
268	ifp->if_watchdog = myx_watchdog;
269	strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
270	IFQ_SET_MAXLEN(&ifp->if_snd, sc->sc_txndesc - 1);
271	IFQ_SET_READY(&ifp->if_snd);
272
273	ifp->if_capabilities = IFCAP_VLAN_MTU;
274#if 0
275	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
276	ifp->if_capabilities |= IFCAP_CSUM_IPv4 | IFCAP_CSUM_TCPv4 |
277		    IFCAP_CSUM_UDPv4;
278#endif
279	ifp->if_baudrate = ULONG_MAX;	/* XXX fix if_baudrate */
280
281	ifmedia_init(&sc->sc_media, 0,
282	    myx_media_change, myx_media_status);
283	ifmedia_add(&sc->sc_media, IFM_ETHER|sc->sc_phy, 0, NULL);
284	ifmedia_set(&sc->sc_media, IFM_ETHER|sc->sc_phy);
285
286	if_attach(ifp);
287	ether_ifattach(ifp);
288
289	timeout_set(&sc->sc_tick, myx_tick, sc);
290	timeout_add(&sc->sc_tick, hz);
291
292	mountroothook_establish(myx_attachhook, sc);
293
294	return;
295
296 err:
297	myx_dmamem_free(sc, &sc->sc_rxdma);
298 err1:
299	myx_dmamem_free(sc, &sc->sc_stsdma);
300 err2:
301	myx_dmamem_free(sc, &sc->sc_paddma);
302 err3:
303	myx_dmamem_free(sc, &sc->sc_cmddma);
304 unmap:
305	bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems);
306	sc->sc_mems = 0;
307}
308
309u_int
310myx_ether_aton(char *mac, u_int8_t *lladdr, u_int maxlen)
311{
312	u_int		i, j;
313	u_int8_t	digit;
314
315	bzero(lladdr, ETHER_ADDR_LEN);
316	for (i = j = 0; mac[i] != '\0' && i < maxlen; i++) {
317		if (mac[i] >= '0' && mac[i] <= '9')
318			digit = mac[i] - '0';
319		else if (mac[i] >= 'A' && mac[i] <= 'F')
320			digit = mac[i] - 'A' + 10;
321		else if (mac[i] >= 'a' && mac[i] <= 'f')
322			digit = mac[i] - 'a' + 10;
323		else
324			continue;
325		if ((j & 1) == 0)
326			digit <<= 4;
327		lladdr[j++/2] |= digit;
328	}
329
330	return (i);
331}
332
333int
334myx_query(struct myx_softc *sc)
335{
336	u_int8_t	eeprom[MYX_EEPROM_SIZE];
337	u_int		i, maxlen;
338
339	myx_read(sc, MYX_EEPROM, eeprom, MYX_EEPROM_SIZE);
340
341	for (i = 0; i < MYX_EEPROM_SIZE; i++) {
342		maxlen = MYX_EEPROM_SIZE - i;
343		if (eeprom[i] == '\0')
344			break;
345		if (maxlen > 4 && bcmp("MAC=", &eeprom[i], 4) == 0) {
346			i += 4;
347			i += myx_ether_aton(&eeprom[i],
348			    sc->sc_ac.ac_enaddr, maxlen);
349		}
350		for (; i < MYX_EEPROM_SIZE; i++)
351			if (eeprom[i] == '\0')
352				break;
353	}
354
355	return (0);
356}
357
358int
359myx_loadfirmware(struct myx_softc *sc, u_int8_t *fw, size_t fwlen,
360    u_int32_t fwhdroff, int reload)
361{
362	struct myx_firmware_hdr	*fwhdr;
363	u_int			 i, len, ret = 0;
364
365	fwhdr = (struct myx_firmware_hdr *)(fw + fwhdroff);
366	DPRINTF(MYXDBG_INIT, "%s(%s): "
367	    "fw hdr off %d, length %d, type 0x%x, version %s\n",
368	    DEVNAME(sc), __func__,
369	    fwhdroff, betoh32(fwhdr->fw_hdrlength),
370	    betoh32(fwhdr->fw_type),
371	    fwhdr->fw_version);
372
373	if (betoh32(fwhdr->fw_type) != MYXFW_TYPE_ETH ||
374	    bcmp(MYXFW_VER, fwhdr->fw_version, strlen(MYXFW_VER)) != 0) {
375		if (reload)
376			printf("%s: invalid firmware type 0x%x version %s\n",
377			    DEVNAME(sc), betoh32(fwhdr->fw_type),
378			    fwhdr->fw_version);
379		ret = 1;
380		goto done;
381	}
382
383	if (!reload)
384		goto done;
385
386	/* Write the firmware to the card's SRAM */
387	for (i = 0; i < fwlen; i += 256) {
388		len = min(256, fwlen - i);
389		myx_write(sc, i + MYX_FW, fw + i, min(256, fwlen - i));
390	}
391
392 done:
393	free(fw, M_DEVBUF);
394	return (ret);
395}
396
397void
398myx_attachhook(void *arg)
399{
400	struct myx_softc	*sc = (struct myx_softc *)arg;
401	size_t			 fwlen;
402	u_int8_t		*fw = NULL;
403	u_int32_t		 fwhdroff;
404	struct myx_bootcmd	 bc;
405
406	/*
407	 * First try the firmware found in the SRAM
408	 */
409	myx_read(sc, MYX_HEADER_POS,
410	    (u_int8_t *)&fwhdroff, sizeof(fwhdroff));
411	fwhdroff = betoh32(fwhdroff);
412	fwlen = sizeof(struct myx_firmware_hdr);
413	if ((fwhdroff + fwlen) > MYX_SRAM_SIZE)
414		goto load;
415
416	fw = malloc(fwlen, M_DEVBUF, M_WAIT);
417	myx_read(sc, MYX_HEADER_POS, fw, fwlen);
418
419	if (myx_loadfirmware(sc, fw, fwlen, fwhdroff, 0) == 0)
420		goto boot;
421
422 load:
423	/*
424	 * Now try the firmware stored on disk
425	 */
426	if (loadfirmware(MYXFW_UNALIGNED, &fw, &fwlen) != 0) {
427		printf("%s: could not load firmware\n", DEVNAME(sc));
428		return;
429	}
430	if (fwlen > MYX_SRAM_SIZE || fwlen < MYXFW_MIN_LEN) {
431		printf("%s: invalid firmware image size\n", DEVNAME(sc));
432		goto err;
433	}
434
435	bcopy(fw + MYX_HEADER_POS, &fwhdroff, sizeof(fwhdroff));
436	fwhdroff = betoh32(fwhdroff);
437	if ((fwhdroff + sizeof(struct myx_firmware_hdr)) > fwlen) {
438		printf("%s: invalid firmware image\n", DEVNAME(sc));
439		goto err;
440	}
441
442	if (myx_loadfirmware(sc, fw, fwlen, fwhdroff, 1) != 0) {
443		fw = NULL;
444		goto err;
445	}
446	fw = NULL;
447
448 boot:
449	bzero(&bc, sizeof(bc));
450	if (myx_boot(sc, fwlen, &bc) != 0) {
451		printf("%s: failed to bootstrap the device\n", DEVNAME(sc));
452		goto err;
453	}
454	if (myx_reset(sc) != 0)
455		goto err;
456
457	sc->sc_active = 1;
458	return;
459
460 err:
461	if (fw != NULL)
462		free(fw, M_DEVBUF);
463}
464
465void
466myx_read(struct myx_softc *sc, bus_size_t off, u_int8_t *ptr, bus_size_t len)
467{
468	bus_space_barrier(sc->sc_memt, sc->sc_memh, off, len,
469	    BUS_SPACE_BARRIER_READ);
470	bus_space_read_region_1(sc->sc_memt, sc->sc_memh, off, ptr, len);
471}
472
473void
474myx_write(struct myx_softc *sc, bus_size_t off, u_int8_t *ptr, bus_size_t len)
475{
476	bus_space_write_region_4(sc->sc_memt, sc->sc_memh, off, ptr, len);
477	bus_space_barrier(sc->sc_memt, sc->sc_memh, off, len,
478	    BUS_SPACE_BARRIER_WRITE);
479}
480
481int
482myx_dmamem_alloc(struct myx_softc *sc, struct myx_dmamem *mxm,
483    bus_size_t size, u_int align, const char *mname)
484{
485	mxm->mxm_size = size;
486
487	if (bus_dmamap_create(sc->sc_dmat, mxm->mxm_size, 1,
488	    mxm->mxm_size, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
489	    &mxm->mxm_map) != 0)
490		return (1);
491	if (bus_dmamem_alloc(sc->sc_dmat, mxm->mxm_size,
492	    align, 0, &mxm->mxm_seg, 1, &mxm->mxm_nsegs,
493	    BUS_DMA_NOWAIT) != 0)
494		goto destroy;
495	if (bus_dmamem_map(sc->sc_dmat, &mxm->mxm_seg, mxm->mxm_nsegs,
496	    mxm->mxm_size, &mxm->mxm_kva, BUS_DMA_NOWAIT) != 0)
497		goto free;
498	if (bus_dmamap_load(sc->sc_dmat, mxm->mxm_map, mxm->mxm_kva,
499	    mxm->mxm_size, NULL, BUS_DMA_NOWAIT) != 0)
500		goto unmap;
501
502	bzero(mxm->mxm_kva, mxm->mxm_size);
503	mxm->mxm_name = mname;
504
505	return (0);
506 unmap:
507	bus_dmamem_unmap(sc->sc_dmat, mxm->mxm_kva, mxm->mxm_size);
508 free:
509	bus_dmamem_free(sc->sc_dmat, &mxm->mxm_seg, 1);
510 destroy:
511	bus_dmamap_destroy(sc->sc_dmat, mxm->mxm_map);
512	return (1);
513}
514
515void
516myx_dmamem_free(struct myx_softc *sc, struct myx_dmamem *mxm)
517{
518	bus_dmamap_unload(sc->sc_dmat, mxm->mxm_map);
519	bus_dmamem_unmap(sc->sc_dmat, mxm->mxm_kva, mxm->mxm_size);
520	bus_dmamem_free(sc->sc_dmat, &mxm->mxm_seg, 1);
521	bus_dmamap_destroy(sc->sc_dmat, mxm->mxm_map);
522}
523
524int
525myx_cmd(struct myx_softc *sc, u_int32_t cmd, struct myx_cmd *mc, u_int32_t *r)
526{
527	bus_dmamap_t		 map = sc->sc_cmddma.mxm_map;
528	struct myx_response	*mr;
529	u_int			 i;
530
531	DPRINTF(MYXDBG_CMD, "%s(%s) command %d\n", DEVNAME(sc),
532	    __func__, cmd);
533
534	mc->mc_cmd = htobe32(cmd);
535	mc->mc_addr_high = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
536	mc->mc_addr_low = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
537
538	mr = (struct myx_response *)sc->sc_cmddma.mxm_kva;
539	mr->mr_result = 0xffffffff;
540
541	/* Send command */
542	myx_write(sc, MYX_CMD, (u_int8_t *)mc, sizeof(struct myx_cmd));
543
544	for (i = 0; i < 20; i++) {
545		bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
546		    BUS_DMASYNC_POSTREAD);
547		if (mr->mr_result != 0xffffffff)
548			break;
549		delay(1000);
550	}
551
552	DPRINTF(MYXDBG_CMD, "%s(%s): command %d completed, "
553	    "result %x, data %x\n", DEVNAME(sc), __func__, cmd,
554	    betoh32(mr->mr_result), betoh32(mr->mr_data));
555
556	if (betoh32(mr->mr_result) != 0)
557		return (-1);
558
559	if (r != NULL)
560		*r = betoh32(mr->mr_data);
561	return (0);
562}
563
564int
565myx_boot(struct myx_softc *sc, u_int32_t length, struct myx_bootcmd *bc)
566{
567	bus_dmamap_t		 map = sc->sc_cmddma.mxm_map;
568	u_int32_t		*status;
569	u_int			 i;
570
571	bc->bc_addr_high = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
572	bc->bc_addr_low = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
573	bc->bc_result = 0xffffffff;
574	bc->bc_offset = htobe32(MYX_FW_BOOT);
575	bc->bc_length = htobe32(length);
576	bc->bc_copyto = htobe32(8);
577	bc->bc_jumpto = htobe32(0);
578
579	status = (u_int32_t *)sc->sc_cmddma.mxm_kva;
580	*status = 0;
581
582	/* Send command */
583	myx_write(sc, MYX_BOOT, (u_int8_t *)bc, sizeof(struct myx_bootcmd));
584
585	for (i = 0; i < 2000; i++) {
586		bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
587		    BUS_DMASYNC_POSTREAD);
588		if (*status == 0xffffffff)
589			break;
590		delay(100);
591	}
592
593	DPRINTF(MYXDBG_CMD, "%s(%s): boot completed, result %x\n",
594	    DEVNAME(sc), __func__, betoh32(*status));
595
596	if (*status != 0xffffffff)
597		return (-1);
598
599	return (0);
600}
601
602int
603myx_rdma(struct myx_softc *sc, u_int do_enable)
604{
605	struct myx_rdmacmd	 rc;
606	bus_dmamap_t		 map = sc->sc_cmddma.mxm_map;
607	bus_dmamap_t		 pad = sc->sc_paddma.mxm_map;
608	u_int32_t		*status;
609	u_int			 i;
610
611	/*
612	 * It is required to setup a _dummy_ RDMA address. It also makes
613	 * some PCI-E chipsets resend dropped messages.
614	 */
615	rc.rc_addr_high = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
616	rc.rc_addr_low = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
617	rc.rc_result = 0xffffffff;
618	rc.rc_rdma_high = MYX_ADDRHIGH(pad->dm_segs[0].ds_addr);
619	rc.rc_rdma_low = MYX_ADDRLOW(pad->dm_segs[0].ds_addr);
620	rc.rc_enable = htobe32(do_enable);
621
622	status = (u_int32_t *)sc->sc_cmddma.mxm_kva;
623	*status = 0;
624
625	/* Send command */
626	myx_write(sc, MYX_RDMA, (u_int8_t *)&rc, sizeof(struct myx_rdmacmd));
627
628	for (i = 0; i < 200; i++) {
629		bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
630		    BUS_DMASYNC_POSTREAD);
631		if (*status == 0xffffffff)
632			break;
633		delay(1000);
634	}
635
636	DPRINTF(MYXDBG_CMD, "%s(%s): dummy RDMA %s, result %x\n",
637	    DEVNAME(sc), __func__,
638	    do_enable ? "enabled" : "disabled", betoh32(*status));
639
640	if (*status != 0xffffffff)
641		return (-1);
642
643	return (0);
644}
645
646int
647myx_reset(struct myx_softc *sc)
648{
649	struct myx_cmd		 mc;
650	u_int32_t		 result;
651	struct ifnet		*ifp = &sc->sc_ac.ac_if;
652	bus_dmamap_t		 map;
653
654	bzero(&mc, sizeof(mc));
655	if (myx_cmd(sc, MYXCMD_RESET, &mc, NULL) != 0) {
656		printf("%s: failed to reset the device\n", DEVNAME(sc));
657		return (-1);
658	}
659
660	if (myx_rdma(sc, MYXRDMA_ON) != 0) {
661		printf("%s: failed to enable dummy RDMA\n", DEVNAME(sc));
662		return (-1);
663	}
664
665	bzero(&mc, sizeof(mc));
666	mc.mc_data0 = htobe32(sc->sc_rxdescsize);
667	if (myx_cmd(sc, MYXCMD_SET_INTRQSZ, &mc, NULL) != 0) {
668		printf("%s: failed to set Rx DMA size\n", DEVNAME(sc));
669		return (-1);
670	}
671
672	bzero(&mc, sizeof(mc));
673	map = sc->sc_rxdma.mxm_map;
674	mc.mc_data0 = MYX_ADDRLOW(map->dm_segs[0].ds_addr);
675	mc.mc_data1 = MYX_ADDRHIGH(map->dm_segs[0].ds_addr);
676	if (myx_cmd(sc, MYXCMD_SET_INTRQSZ, &mc, NULL) != 0) {
677		printf("%s: failed to set Rx DMA address\n", DEVNAME(sc));
678		return (-1);
679	}
680
681	bzero(&mc, sizeof(mc));
682	if (myx_cmd(sc, MYXCMD_GET_INTRCOALDELAYOFF, &mc, &result) != 0) {
683		printf("%s: failed to get IRQ coal offset\n", DEVNAME(sc));
684		return (-1);
685	}
686	sc->sc_irqcoaloff = result;
687
688	bzero(&mc, sizeof(mc));
689	if (myx_cmd(sc, MYXCMD_GET_INTRACKOFF, &mc, &result) != 0) {
690		printf("%s: failed to get IRQ ack offset\n", DEVNAME(sc));
691		return (-1);
692	}
693	sc->sc_irqclaimoff = result;
694
695	bzero(&mc, sizeof(mc));
696	if (myx_cmd(sc, MYXCMD_GET_INTRDEASSERTOFF, &mc, &result) != 0) {
697		printf("%s: failed to get IRQ deassert offset\n", DEVNAME(sc));
698		return (-1);
699	}
700	sc->sc_irqdeassertoff = result;
701
702	/* XXX */
703	sc->sc_txndesc = 2;
704	sc->sc_rxndesc = 2;
705
706	if (myx_setlladdr(sc, LLADDR(ifp->if_sadl)) != 0)
707		return (-1);
708
709	return (0);
710}
711
712
713int
714myx_media_change(struct ifnet *ifp)
715{
716	return (EINVAL);
717}
718
719void
720myx_media_status(struct ifnet *ifp, struct ifmediareq *imr)
721{
722	struct myx_softc	*sc = (struct myx_softc *)ifp->if_softc;
723
724	imr->ifm_active = IFM_ETHER|sc->sc_phy;
725	imr->ifm_status = IFM_AVALID;
726	myx_link_state(sc);
727	if (!LINK_STATE_IS_UP(ifp->if_link_state))
728		return;
729	imr->ifm_active |= IFM_FDX;
730	imr->ifm_status |= IFM_ACTIVE;
731
732	/* Flow control */
733	if (sc->sc_hwflags & MYXFLAG_FLOW_CONTROL)
734		imr->ifm_active |= IFM_FLOW|IFM_ETH_RXPAUSE|IFM_ETH_TXPAUSE;
735}
736
737void
738myx_link_state(struct myx_softc *sc)
739{
740	struct ifnet		*ifp = &sc->sc_ac.ac_if;
741	int			 link_state = LINK_STATE_DOWN;
742
743	if (sc->sc_sts->ms_linkstate == MYXSTS_LINKUP)
744		link_state = LINK_STATE_FULL_DUPLEX;
745	if (ifp->if_link_state != link_state) {
746		ifp->if_link_state = link_state;
747		if_link_state_change(ifp);
748	}
749}
750
751void
752myx_watchdog(struct ifnet *ifp)
753{
754	return;
755}
756
757void
758myx_tick(void *arg)
759{
760	struct myx_softc	*sc = (struct myx_softc *)arg;
761
762	if (!sc->sc_active)
763		return;
764
765	myx_link_state(sc);
766	timeout_add(&sc->sc_tick, hz);
767}
768
769int
770myx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
771{
772	struct myx_softc	*sc = (struct myx_softc *)ifp->if_softc;
773	struct ifaddr		*ifa = (struct ifaddr *)data;
774	struct ifreq		*ifr = (struct ifreq *)data;
775	int			 s, error = 0;
776
777	s = splnet();
778	if ((error = ether_ioctl(ifp, &sc->sc_ac, cmd, data)) > 0) {
779		splx(s);
780		return (error);
781	}
782
783	switch (cmd) {
784	case SIOCSIFADDR:
785		ifp->if_flags |= IFF_UP;
786#ifdef INET
787		if (ifa->ifa_addr->sa_family == AF_INET)
788			arp_ifinit(&sc->sc_ac, ifa);
789#endif
790		/* FALLTHROUGH */
791	case SIOCSIFFLAGS:
792		if (ifp->if_flags & IFF_UP) {
793			if (ifp->if_flags & IFF_RUNNING)
794				myx_iff(sc);
795			else
796				myx_init(ifp);
797		} else {
798			if (ifp->if_flags & IFF_RUNNING)
799				myx_stop(ifp);
800		}
801		break;
802
803	case SIOCSIFMTU:
804		if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ifp->if_hardmtu)
805			error = EINVAL;
806		else if (ifp->if_mtu != ifr->ifr_mtu)
807			ifp->if_mtu = ifr->ifr_mtu;
808		break;
809
810	case SIOCADDMULTI:
811		error = ether_addmulti(ifr, &sc->sc_ac);
812		break;
813
814	case SIOCDELMULTI:
815		error = ether_delmulti(ifr, &sc->sc_ac);
816		break;
817
818	case SIOCGIFMEDIA:
819	case SIOCSIFMEDIA:
820		error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
821		break;
822
823	default:
824		error = ENOTTY;
825	}
826
827	if (error == ENETRESET) {
828		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
829		    (IFF_UP | IFF_RUNNING))
830			myx_iff(sc);
831		error = 0;
832	}
833
834	splx(s);
835
836	return (error);
837}
838
839void
840myx_iff(struct myx_softc *sc)
841{
842	/* XXX set multicast filters etc. */
843	return;
844}
845
846void
847myx_init(struct ifnet *ifp)
848{
849	struct myx_softc	*sc = (struct myx_softc *)ifp->if_softc;
850
851	if (myx_setlladdr(sc, LLADDR(ifp->if_sadl)) != 0)
852		return;
853
854	ifp->if_flags |= IFF_RUNNING;
855	ifp->if_flags &= ~IFF_OACTIVE;
856}
857
858void
859myx_start(struct ifnet *ifp)
860{
861}
862
863void
864myx_stop(struct ifnet *ifp)
865{
866	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
867}
868
869int
870myx_setlladdr(struct myx_softc *sc, u_int8_t *addr)
871{
872	struct myx_cmd		 mc;
873
874	bzero(&mc, sizeof(mc));
875	mc.mc_data0 = addr[3] | addr[2] << 8 | addr[1] << 16 | addr[0] << 24;
876	mc.mc_data1 = addr[5] | addr[4] << 8;
877	if (myx_cmd(sc, MYXCMD_SET_LLADDR, &mc, NULL) != 0) {
878		printf("%s: failed to set the lladdr\n", DEVNAME(sc));
879		return (-1);
880	}
881	return (0);
882}
883
884int
885myx_intr(void *arg)
886{
887	struct myx_softc	*sc = (struct myx_softc *)arg;
888
889	if (!sc->sc_active || sc->sc_sts->ms_isvalid == 0)
890		return (0);
891
892	DPRINTF(MYXDBG_INTR, "%s(%s): interrupt\n", DEVNAME(sc), __func__);
893	return (1);
894}
895