1/*	$NetBSD: if_le_pci.c,v 1.43 2005/12/11 12:22:49 christos Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-2-Clause-NetBSD AND BSD-3-Clause
5 *
6 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
11 * Simulation Facility, NASA Ames Research Center.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35/*-
36 * Copyright (c) 1992, 1993
37 *	The Regents of the University of California.  All rights reserved.
38 *
39 * This code is derived from software contributed to Berkeley by
40 * Ralph Campbell and Rick Macklem.
41 *
42 * Redistribution and use in source and binary forms, with or without
43 * modification, are permitted provided that the following conditions
44 * are met:
45 * 1. Redistributions of source code must retain the above copyright
46 *    notice, this list of conditions and the following disclaimer.
47 * 2. Redistributions in binary form must reproduce the above copyright
48 *    notice, this list of conditions and the following disclaimer in the
49 *    documentation and/or other materials provided with the distribution.
50 * 3. Neither the name of the University nor the names of its contributors
51 *    may be used to endorse or promote products derived from this software
52 *    without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 *	@(#)if_le.c	8.2 (Berkeley) 11/16/93
67 */
68
69#include <sys/cdefs.h>
70__FBSDID("$FreeBSD: releng/12.0/sys/dev/le/if_le_pci.c 326255 2017-11-27 14:52:40Z pfg $");
71
72#include <sys/param.h>
73#include <sys/systm.h>
74#include <sys/bus.h>
75#include <sys/endian.h>
76#include <sys/kernel.h>
77#include <sys/lock.h>
78#include <sys/module.h>
79#include <sys/mutex.h>
80#include <sys/resource.h>
81#include <sys/rman.h>
82#include <sys/socket.h>
83
84#include <net/ethernet.h>
85#include <net/if.h>
86#include <net/if_media.h>
87
88#include <machine/bus.h>
89#include <machine/resource.h>
90
91#include <dev/pci/pcireg.h>
92#include <dev/pci/pcivar.h>
93
94#include <dev/le/lancereg.h>
95#include <dev/le/lancevar.h>
96#include <dev/le/am79900var.h>
97
98#define	AMD_VENDOR	0x1022
99#define	AMD_PCNET_PCI	0x2000
100#define	AMD_PCNET_HOME	0x2001
101#define	PCNET_MEMSIZE	(32*1024)
102#define	PCNET_PCI_RDP	0x10
103#define	PCNET_PCI_RAP	0x12
104#define	PCNET_PCI_BDP	0x16
105
106struct le_pci_softc {
107	struct am79900_softc	sc_am79900;	/* glue to MI code */
108
109	struct resource		*sc_rres;
110
111	struct resource		*sc_ires;
112	void			*sc_ih;
113
114	bus_dma_tag_t		sc_pdmat;
115	bus_dma_tag_t		sc_dmat;
116	bus_dmamap_t		sc_dmam;
117};
118
119static device_probe_t le_pci_probe;
120static device_attach_t le_pci_attach;
121static device_detach_t le_pci_detach;
122static device_resume_t le_pci_resume;
123static device_suspend_t le_pci_suspend;
124
125static device_method_t le_pci_methods[] = {
126	/* Device interface */
127	DEVMETHOD(device_probe,		le_pci_probe),
128	DEVMETHOD(device_attach,	le_pci_attach),
129	DEVMETHOD(device_detach,	le_pci_detach),
130	/* We can just use the suspend method here. */
131	DEVMETHOD(device_shutdown,	le_pci_suspend),
132	DEVMETHOD(device_suspend,	le_pci_suspend),
133	DEVMETHOD(device_resume,	le_pci_resume),
134
135	{ 0, 0 }
136};
137
138DEFINE_CLASS_0(le, le_pci_driver, le_pci_methods, sizeof(struct le_pci_softc));
139DRIVER_MODULE(le, pci, le_pci_driver, le_devclass, 0, 0);
140MODULE_DEPEND(le, ether, 1, 1, 1);
141
142static const int le_home_supmedia[] = {
143	IFM_MAKEWORD(IFM_ETHER, IFM_HPNA_1, 0, 0)
144};
145
146static const int le_pci_supmedia[] = {
147	IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0),
148	IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, IFM_FDX, 0),
149	IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0),
150	IFM_MAKEWORD(IFM_ETHER, IFM_10_T, IFM_FDX, 0),
151	IFM_MAKEWORD(IFM_ETHER, IFM_10_5, 0, 0),
152	IFM_MAKEWORD(IFM_ETHER, IFM_10_5, IFM_FDX, 0)
153};
154
155static void le_pci_wrbcr(struct lance_softc *, uint16_t, uint16_t);
156static uint16_t le_pci_rdbcr(struct lance_softc *, uint16_t);
157static void le_pci_wrcsr(struct lance_softc *, uint16_t, uint16_t);
158static uint16_t le_pci_rdcsr(struct lance_softc *, uint16_t);
159static int le_pci_mediachange(struct lance_softc *);
160static void le_pci_hwreset(struct lance_softc *);
161static bus_dmamap_callback_t le_pci_dma_callback;
162
163static void
164le_pci_wrbcr(struct lance_softc *sc, uint16_t port, uint16_t val)
165{
166	struct le_pci_softc *lesc = (struct le_pci_softc *)sc;
167
168	bus_write_2(lesc->sc_rres, PCNET_PCI_RAP, port);
169	bus_barrier(lesc->sc_rres, PCNET_PCI_RAP, 2, BUS_SPACE_BARRIER_WRITE);
170	bus_write_2(lesc->sc_rres, PCNET_PCI_BDP, val);
171}
172
173static uint16_t
174le_pci_rdbcr(struct lance_softc *sc, uint16_t port)
175{
176	struct le_pci_softc *lesc = (struct le_pci_softc *)sc;
177
178	bus_write_2(lesc->sc_rres, PCNET_PCI_RAP, port);
179	bus_barrier(lesc->sc_rres, PCNET_PCI_RAP, 2, BUS_SPACE_BARRIER_WRITE);
180	return (bus_read_2(lesc->sc_rres, PCNET_PCI_BDP));
181}
182
183static void
184le_pci_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val)
185{
186	struct le_pci_softc *lesc = (struct le_pci_softc *)sc;
187
188#ifdef __HAIKU__
189	HAIKU_INTR_REGISTER_STATE;
190	if (port == LE_CSR0)
191		HAIKU_INTR_REGISTER_ENTER();
192#endif
193	bus_write_2(lesc->sc_rres, PCNET_PCI_RAP, port);
194	bus_barrier(lesc->sc_rres, PCNET_PCI_RAP, 2, BUS_SPACE_BARRIER_WRITE);
195	bus_write_2(lesc->sc_rres, PCNET_PCI_RDP, val);
196#ifdef __HAIKU__
197	if (port == LE_CSR0)
198		HAIKU_INTR_REGISTER_LEAVE();
199#endif
200}
201
202static uint16_t
203le_pci_rdcsr(struct lance_softc *sc, uint16_t port)
204{
205	struct le_pci_softc *lesc = (struct le_pci_softc *)sc;
206
207#ifdef __HAIKU__
208	HAIKU_INTR_REGISTER_STATE;
209	uint16_t value;
210	if (port == LE_CSR0)
211		HAIKU_INTR_REGISTER_ENTER();
212#endif
213	bus_write_2(lesc->sc_rres, PCNET_PCI_RAP, port);
214	bus_barrier(lesc->sc_rres, PCNET_PCI_RAP, 2, BUS_SPACE_BARRIER_WRITE);
215#ifndef __HAIKU__
216	return (bus_read_2(lesc->sc_rres, PCNET_PCI_RDP));
217#else
218	value = bus_read_2(lesc->sc_rres, PCNET_PCI_RDP);
219	if (port == LE_CSR0)
220		HAIKU_INTR_REGISTER_LEAVE();
221	return value;
222#endif
223}
224
225static int
226le_pci_mediachange(struct lance_softc *sc)
227{
228	struct ifmedia *ifm = &sc->sc_media;
229	uint16_t reg;
230
231	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
232		return (EINVAL);
233
234	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1)
235		le_pci_wrbcr(sc, LE_BCR49,
236		    (le_pci_rdbcr(sc, LE_BCR49) & ~LE_B49_PHYSEL) | 0x1);
237	else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO)
238		le_pci_wrbcr(sc, LE_BCR2,
239		    le_pci_rdbcr(sc, LE_BCR2) | LE_B2_ASEL);
240	else {
241		le_pci_wrbcr(sc, LE_BCR2,
242		    le_pci_rdbcr(sc, LE_BCR2) & ~LE_B2_ASEL);
243
244		reg = le_pci_rdcsr(sc, LE_CSR15);
245		reg &= ~LE_C15_PORTSEL(LE_PORTSEL_MASK);
246		if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T)
247			reg |= LE_C15_PORTSEL(LE_PORTSEL_10T);
248		else
249			reg |= LE_C15_PORTSEL(LE_PORTSEL_AUI);
250		le_pci_wrcsr(sc, LE_CSR15, reg);
251	}
252
253	reg = le_pci_rdbcr(sc, LE_BCR9);
254	if (IFM_OPTIONS(ifm->ifm_media) & IFM_FDX) {
255		reg |= LE_B9_FDEN;
256		/*
257		 * Allow FDX on AUI only if explicitly chosen,
258		 * not in autoselect mode.
259		 */
260		if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_5)
261			reg |= LE_B9_AUIFD;
262		else
263			reg &= ~LE_B9_AUIFD;
264	} else
265		reg &= ~LE_B9_FDEN;
266	le_pci_wrbcr(sc, LE_BCR9, reg);
267
268	return (0);
269}
270
271static void
272le_pci_hwreset(struct lance_softc *sc)
273{
274
275	/*
276	 * Chip is stopped. Set software style to PCnet-PCI (32-bit).
277	 * Actually, am79900.c implements ILACC support (hence its
278	 * name) but unfortunately VMware does not. As far as this
279	 * driver is concerned that should not make a difference
280	 * though, as the settings used have the same meaning for
281	 * both, ILACC and PCnet-PCI (note that there would be a
282	 * difference for the ADD_FCS/NO_FCS bit if used).
283	 */
284	le_pci_wrbcr(sc, LE_BCR20, LE_B20_SSTYLE_PCNETPCI2);
285}
286
287static void
288le_pci_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
289{
290	struct lance_softc *sc = (struct lance_softc *)xsc;
291
292	if (error != 0)
293		return;
294	KASSERT(nsegs == 1, ("%s: bad DMA segment count", __func__));
295	sc->sc_addr = segs[0].ds_addr;
296}
297
298static int
299le_pci_probe(device_t dev)
300{
301
302	if (pci_get_vendor(dev) != AMD_VENDOR)
303		return (ENXIO);
304
305	switch (pci_get_device(dev)) {
306	case AMD_PCNET_PCI:
307		device_set_desc(dev, "AMD PCnet-PCI");
308		/* Let pcn(4) win. */
309		return (BUS_PROBE_LOW_PRIORITY);
310	case AMD_PCNET_HOME:
311		device_set_desc(dev, "AMD PCnet-Home");
312		/* Let pcn(4) win. */
313		return (BUS_PROBE_LOW_PRIORITY);
314	default:
315		return (ENXIO);
316	}
317}
318
319static int
320le_pci_attach(device_t dev)
321{
322	struct le_pci_softc *lesc;
323	struct lance_softc *sc;
324	int error, i;
325
326	lesc = device_get_softc(dev);
327	sc = &lesc->sc_am79900.lsc;
328
329	LE_LOCK_INIT(sc, device_get_nameunit(dev));
330
331	pci_enable_busmaster(dev);
332
333	i = PCIR_BAR(0);
334	lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
335	    &i, RF_ACTIVE);
336	if (lesc->sc_rres == NULL) {
337		device_printf(dev, "cannot allocate registers\n");
338		error = ENXIO;
339		goto fail_mtx;
340	}
341
342	i = 0;
343	if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ,
344	    &i, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
345		device_printf(dev, "cannot allocate interrupt\n");
346		error = ENXIO;
347		goto fail_rres;
348	}
349
350	error = bus_dma_tag_create(
351	    bus_get_dma_tag(dev),	/* parent */
352	    1, 0,			/* alignment, boundary */
353	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
354	    BUS_SPACE_MAXADDR,		/* highaddr */
355	    NULL, NULL,			/* filter, filterarg */
356	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
357	    0,				/* nsegments */
358	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
359	    0,				/* flags */
360	    NULL, NULL,			/* lockfunc, lockarg */
361	    &lesc->sc_pdmat);
362	if (error != 0) {
363		device_printf(dev, "cannot allocate parent DMA tag\n");
364		goto fail_ires;
365	}
366
367	sc->sc_memsize = PCNET_MEMSIZE;
368	/*
369	 * For Am79C970A, Am79C971 and Am79C978 the init block must be 2-byte
370	 * aligned and the ring descriptors must be 16-byte aligned when using
371	 * a 32-bit software style.
372	 */
373	error = bus_dma_tag_create(
374	    lesc->sc_pdmat,		/* parent */
375	    16, 0,			/* alignment, boundary */
376	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
377	    BUS_SPACE_MAXADDR,		/* highaddr */
378	    NULL, NULL,			/* filter, filterarg */
379	    sc->sc_memsize,		/* maxsize */
380	    1,				/* nsegments */
381	    sc->sc_memsize,		/* maxsegsize */
382	    0,				/* flags */
383	    NULL, NULL,			/* lockfunc, lockarg */
384	    &lesc->sc_dmat);
385	if (error != 0) {
386		device_printf(dev, "cannot allocate buffer DMA tag\n");
387		goto fail_pdtag;
388	}
389
390	error = bus_dmamem_alloc(lesc->sc_dmat, (void **)&sc->sc_mem,
391	    BUS_DMA_WAITOK | BUS_DMA_COHERENT, &lesc->sc_dmam);
392	if (error != 0) {
393		device_printf(dev, "cannot allocate DMA buffer memory\n");
394		goto fail_dtag;
395	}
396
397	sc->sc_addr = 0;
398	error = bus_dmamap_load(lesc->sc_dmat, lesc->sc_dmam, sc->sc_mem,
399	    sc->sc_memsize, le_pci_dma_callback, sc, 0);
400	if (error != 0 || sc->sc_addr == 0) {
401		device_printf(dev, "cannot load DMA buffer map\n");
402		goto fail_dmem;
403	}
404
405	sc->sc_flags = LE_BSWAP;
406	sc->sc_conf3 = 0;
407
408	sc->sc_mediastatus = NULL;
409	switch (pci_get_device(dev)) {
410	case AMD_PCNET_HOME:
411		sc->sc_mediachange = le_pci_mediachange;
412		sc->sc_supmedia = le_home_supmedia;
413		sc->sc_nsupmedia = sizeof(le_home_supmedia) / sizeof(int);
414		sc->sc_defaultmedia = le_home_supmedia[0];
415		break;
416	default:
417		sc->sc_mediachange = le_pci_mediachange;
418		sc->sc_supmedia = le_pci_supmedia;
419		sc->sc_nsupmedia = sizeof(le_pci_supmedia) / sizeof(int);
420		sc->sc_defaultmedia = le_pci_supmedia[0];
421	}
422
423	/*
424	 * Extract the physical MAC address from the ROM.
425	 */
426	bus_read_region_1(lesc->sc_rres, 0, sc->sc_enaddr,
427	    sizeof(sc->sc_enaddr));
428
429	sc->sc_copytodesc = lance_copytobuf_contig;
430	sc->sc_copyfromdesc = lance_copyfrombuf_contig;
431	sc->sc_copytobuf = lance_copytobuf_contig;
432	sc->sc_copyfrombuf = lance_copyfrombuf_contig;
433	sc->sc_zerobuf = lance_zerobuf_contig;
434
435	sc->sc_rdcsr = le_pci_rdcsr;
436	sc->sc_wrcsr = le_pci_wrcsr;
437	sc->sc_hwreset = le_pci_hwreset;
438	sc->sc_hwinit = NULL;
439	sc->sc_hwintr = NULL;
440	sc->sc_nocarrier = NULL;
441
442	error = am79900_config(&lesc->sc_am79900, device_get_name(dev),
443	    device_get_unit(dev));
444	if (error != 0) {
445		device_printf(dev, "cannot attach Am79900\n");
446		goto fail_dmap;
447	}
448
449	error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE,
450	    NULL, am79900_intr, sc, &lesc->sc_ih);
451	if (error != 0) {
452		device_printf(dev, "cannot set up interrupt\n");
453		goto fail_am79900;
454	}
455
456	return (0);
457
458 fail_am79900:
459	am79900_detach(&lesc->sc_am79900);
460 fail_dmap:
461	bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam);
462 fail_dmem:
463	bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam);
464 fail_dtag:
465	bus_dma_tag_destroy(lesc->sc_dmat);
466 fail_pdtag:
467	bus_dma_tag_destroy(lesc->sc_pdmat);
468 fail_ires:
469	bus_release_resource(dev, SYS_RES_IRQ,
470	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
471 fail_rres:
472	bus_release_resource(dev, SYS_RES_IOPORT,
473	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
474 fail_mtx:
475	LE_LOCK_DESTROY(sc);
476	return (error);
477}
478
479static int
480le_pci_detach(device_t dev)
481{
482	struct le_pci_softc *lesc;
483	struct lance_softc *sc;
484
485	lesc = device_get_softc(dev);
486	sc = &lesc->sc_am79900.lsc;
487
488	bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih);
489	am79900_detach(&lesc->sc_am79900);
490	bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam);
491	bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam);
492	bus_dma_tag_destroy(lesc->sc_dmat);
493	bus_dma_tag_destroy(lesc->sc_pdmat);
494	bus_release_resource(dev, SYS_RES_IRQ,
495	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
496	bus_release_resource(dev, SYS_RES_IOPORT,
497	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
498	LE_LOCK_DESTROY(sc);
499
500	return (0);
501}
502
503static int
504le_pci_suspend(device_t dev)
505{
506	struct le_pci_softc *lesc;
507
508	lesc = device_get_softc(dev);
509
510	lance_suspend(&lesc->sc_am79900.lsc);
511
512	return (0);
513}
514
515static int
516le_pci_resume(device_t dev)
517{
518	struct le_pci_softc *lesc;
519
520	lesc = device_get_softc(dev);
521
522	lance_resume(&lesc->sc_am79900.lsc);
523
524	return (0);
525}
526