1/*-
2 * Copyright (c) 1994-2000
3 *	Paul Richards. All rights reserved.
4 *
5 * PC-98 port by Chiharu Shibata & FreeBSD(98) porting team.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer,
12 *    verbatim and that no modifications are made prior to this
13 *    point in the file.
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.
17 * 3. The name Paul Richards may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY PAUL RICHARDS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL PAUL RICHARDS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	from: FreeBSD: src/sys/dev/lnc/if_lnc_cbus.c,v 1.12 2005/11/12 19:14:21
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/bus.h>
41#include <sys/endian.h>
42#include <sys/kernel.h>
43#include <sys/lock.h>
44#include <sys/module.h>
45#include <sys/mutex.h>
46#include <sys/resource.h>
47#include <sys/rman.h>
48#include <sys/socket.h>
49
50#include <net/ethernet.h>
51#include <net/if.h>
52#include <net/if_media.h>
53
54#include <machine/bus.h>
55#include <machine/resource.h>
56
57#include <isa/isavar.h>
58
59#include <dev/le/lancereg.h>
60#include <dev/le/lancevar.h>
61#include <dev/le/am7990var.h>
62
63#define	LE_CBUS_MEMSIZE	(16*1024)
64#define	CNET98S_IOSIZE	32
65#define	CNET98S_RDP	0x10
66#define	CNET98S_RAP	0x12
67#define	CNET98S_RESET	0x14
68#define	CNET98S_BDP	0x16
69
70struct le_cbus_softc {
71	struct am7990_softc	sc_am7990;	/* glue to MI code */
72
73	struct resource		*sc_rres;
74
75	struct resource		*sc_ires;
76	void			*sc_ih;
77
78	bus_dma_tag_t		sc_pdmat;
79	bus_dma_tag_t		sc_dmat;
80	bus_dmamap_t		sc_dmam;
81};
82
83static device_probe_t le_cbus_probe;
84static device_attach_t le_cbus_attach;
85static device_detach_t le_cbus_detach;
86static device_resume_t le_cbus_resume;
87static device_suspend_t le_cbus_suspend;
88
89static device_method_t le_cbus_methods[] = {
90	/* Device interface */
91	DEVMETHOD(device_probe,		le_cbus_probe),
92	DEVMETHOD(device_attach,	le_cbus_attach),
93	DEVMETHOD(device_detach,	le_cbus_detach),
94	/* We can just use the suspend method here. */
95	DEVMETHOD(device_shutdown,	le_cbus_suspend),
96	DEVMETHOD(device_suspend,	le_cbus_suspend),
97	DEVMETHOD(device_resume,	le_cbus_resume),
98
99	{ 0, 0 }
100};
101
102DEFINE_CLASS_0(le, le_cbus_driver, le_cbus_methods, sizeof(struct le_cbus_softc));
103DRIVER_MODULE(le, isa, le_cbus_driver, le_devclass, 0, 0);
104MODULE_DEPEND(le, ether, 1, 1, 1);
105
106static bus_addr_t le_ioaddr_cnet98s[CNET98S_IOSIZE] = {
107	0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007,
108	0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f,
109	0x400, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407,
110	0x408, 0x409, 0x40a, 0x40b, 0x40c, 0x40d, 0x40e, 0x40f,
111};
112
113static void le_cbus_wrbcr(struct lance_softc *, uint16_t, uint16_t);
114#ifdef LEDEBUG
115static uint16_t le_cbus_rdbcr(struct lance_softc *, uint16_t);
116#endif
117static void le_cbus_wrcsr(struct lance_softc *, uint16_t, uint16_t);
118static uint16_t le_cbus_rdcsr(struct lance_softc *, uint16_t);
119static void le_cbus_hwreset(struct lance_softc *);
120static bus_dmamap_callback_t le_cbus_dma_callback;
121
122static void
123le_cbus_wrbcr(struct lance_softc *sc, uint16_t port, uint16_t val)
124{
125	struct le_cbus_softc *lesc = (struct le_cbus_softc *)sc;
126
127	bus_write_2(lesc->sc_rres, CNET98S_RAP, port);
128	bus_barrier(lesc->sc_rres, CNET98S_RAP, 2, BUS_SPACE_BARRIER_WRITE);
129	bus_write_2(lesc->sc_rres, CNET98S_BDP, val);
130}
131
132#ifdef LEDEBUG
133static uint16_t
134le_cbus_rdbcr(struct lance_softc *sc, uint16_t port)
135{
136	struct le_cbus_softc *lesc = (struct le_cbus_softc *)sc;
137
138	bus_write_2(lesc->sc_rres, CNET98S_RAP, port);
139	bus_barrier(lesc->sc_rres, CNET98S_RAP, 2, BUS_SPACE_BARRIER_WRITE);
140	return (bus_read_2(lesc->sc_rres, CNET98S_BDP));
141}
142#endif
143
144static void
145le_cbus_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val)
146{
147	struct le_cbus_softc *lesc = (struct le_cbus_softc *)sc;
148
149	bus_write_2(lesc->sc_rres, CNET98S_RAP, port);
150	bus_barrier(lesc->sc_rres, CNET98S_RAP, 2, BUS_SPACE_BARRIER_WRITE);
151	bus_write_2(lesc->sc_rres, CNET98S_RDP, val);
152}
153
154static uint16_t
155le_cbus_rdcsr(struct lance_softc *sc, uint16_t port)
156{
157	struct le_cbus_softc *lesc = (struct le_cbus_softc *)sc;
158
159	bus_write_2(lesc->sc_rres, CNET98S_RAP, port);
160	bus_barrier(lesc->sc_rres, CNET98S_RAP, 2, BUS_SPACE_BARRIER_WRITE);
161	return (bus_read_2(lesc->sc_rres, CNET98S_RDP));
162}
163
164static void
165le_cbus_hwreset(struct lance_softc *sc)
166{
167	struct le_cbus_softc *lesc = (struct le_cbus_softc *)sc;
168
169	/*
170	 * NB: These are Contec C-NET(98)S only.
171	 */
172
173	/* Reset the chip. */
174	bus_write_2(lesc->sc_rres, CNET98S_RESET,
175	    bus_read_2(lesc->sc_rres, CNET98S_RESET));
176	DELAY(500);
177
178	/* ISA bus configuration */
179	/* ISACSR0 - set Master Mode Read Active time to 300ns. */
180	le_cbus_wrbcr(sc, LE_BCR0, 0x0006);
181	/* ISACSR1 - set Master Mode Write Active time to 300ns. */
182	le_cbus_wrbcr(sc, LE_BCR1, 0x0006);
183#ifdef LEDEBUG
184	device_printf(dev, "ISACSR2=0x%x\n", le_cbus_rdbcr(sc, LE_BCR2));
185#endif
186	/* ISACSR5 - LED1 */
187	le_cbus_wrbcr(sc, LE_BCR5, LE_B4_PSE | LE_B4_XMTE);
188	/* ISACSR6 - LED2 */
189	le_cbus_wrbcr(sc, LE_BCR6, LE_B4_PSE | LE_B4_RCVE);
190	/* ISACSR7 - LED3 */
191	le_cbus_wrbcr(sc, LE_BCR7, LE_B4_PSE | LE_B4_COLE);
192}
193
194static void
195le_cbus_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
196{
197	struct lance_softc *sc = (struct lance_softc *)xsc;
198
199	if (error != 0)
200		return;
201	KASSERT(nsegs == 1, ("%s: bad DMA segment count", __func__));
202	sc->sc_addr = segs[0].ds_addr;
203}
204
205static int
206le_cbus_probe(device_t dev)
207{
208	struct le_cbus_softc *lesc;
209	struct lance_softc *sc;
210	int error, i;
211
212	/*
213	 * Skip PnP devices as some wedge when trying to probe them as
214	 * C-NET(98)S.
215	 */
216	if (isa_get_vendorid(dev))
217		return (ENXIO);
218
219	lesc = device_get_softc(dev);
220	sc = &lesc->sc_am7990.lsc;
221
222	i = 0;
223	lesc->sc_rres = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &i,
224	    le_ioaddr_cnet98s, CNET98S_IOSIZE, RF_ACTIVE);
225	if (lesc->sc_rres == NULL)
226		return (ENXIO);
227	isa_load_resourcev(lesc->sc_rres, le_ioaddr_cnet98s, CNET98S_IOSIZE);
228
229	/* Reset the chip. */
230	bus_write_2(lesc->sc_rres, CNET98S_RESET,
231	    bus_read_2(lesc->sc_rres, CNET98S_RESET));
232	DELAY(500);
233
234	/* Stop the chip and put it in a known state. */
235	le_cbus_wrcsr(sc, LE_CSR0, LE_C0_STOP);
236	DELAY(100);
237	if (le_cbus_rdcsr(sc, LE_CSR0) != LE_C0_STOP) {
238		error = ENXIO;
239		goto fail;
240	}
241	le_cbus_wrcsr(sc, LE_CSR3, 0);
242	device_set_desc(dev, "C-NET(98)S");
243	error = BUS_PROBE_DEFAULT;
244
245 fail:
246	bus_release_resource(dev, SYS_RES_IOPORT,
247	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
248	return (error);
249}
250
251static int
252le_cbus_attach(device_t dev)
253{
254	struct le_cbus_softc *lesc;
255	struct lance_softc *sc;
256	int error, i;
257
258	lesc = device_get_softc(dev);
259	sc = &lesc->sc_am7990.lsc;
260
261	LE_LOCK_INIT(sc, device_get_nameunit(dev));
262
263	i = 0;
264	lesc->sc_rres = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &i,
265	    le_ioaddr_cnet98s, CNET98S_IOSIZE, RF_ACTIVE);
266	if (lesc->sc_rres == NULL) {
267		device_printf(dev, "cannot allocate registers\n");
268		error = ENXIO;
269		goto fail_mtx;
270	}
271	isa_load_resourcev(lesc->sc_rres, le_ioaddr_cnet98s, CNET98S_IOSIZE);
272
273	i = 0;
274	if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ,
275	    &i, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
276		device_printf(dev, "cannot allocate interrupt\n");
277		error = ENXIO;
278		goto fail_rres;
279	}
280
281	error = bus_dma_tag_create(
282	    bus_get_dma_tag(dev),	/* parent */
283	    1, 0,			/* alignment, boundary */
284	    BUS_SPACE_MAXADDR_24BIT,	/* lowaddr */
285	    BUS_SPACE_MAXADDR,		/* highaddr */
286	    NULL, NULL,			/* filter, filterarg */
287	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
288	    0,				/* nsegments */
289	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
290	    0,				/* flags */
291	    NULL, NULL,			/* lockfunc, lockarg */
292	    &lesc->sc_pdmat);
293	if (error != 0) {
294		device_printf(dev, "cannot allocate parent DMA tag\n");
295		goto fail_ires;
296	}
297
298	sc->sc_memsize = LE_CBUS_MEMSIZE;
299	/*
300	 * For Am79C90, Am79C961 and Am79C961A the init block must be 2-byte
301	 * aligned and the ring descriptors must be 8-byte aligned.
302	 */
303	error = bus_dma_tag_create(
304	    lesc->sc_pdmat,		/* parent */
305	    8, 0,			/* alignment, boundary */
306	    BUS_SPACE_MAXADDR_24BIT,	/* lowaddr */
307	    BUS_SPACE_MAXADDR,		/* highaddr */
308	    NULL, NULL,			/* filter, filterarg */
309	    sc->sc_memsize,		/* maxsize */
310	    1,				/* nsegments */
311	    sc->sc_memsize,		/* maxsegsize */
312	    0,				/* flags */
313	    NULL, NULL,			/* lockfunc, lockarg */
314	    &lesc->sc_dmat);
315	if (error != 0) {
316		device_printf(dev, "cannot allocate buffer DMA tag\n");
317		goto fail_pdtag;
318	}
319
320	error = bus_dmamem_alloc(lesc->sc_dmat, (void **)&sc->sc_mem,
321	    BUS_DMA_WAITOK | BUS_DMA_COHERENT, &lesc->sc_dmam);
322	if (error != 0) {
323		device_printf(dev, "cannot allocate DMA buffer memory\n");
324		goto fail_dtag;
325	}
326
327	sc->sc_addr = 0;
328	error = bus_dmamap_load(lesc->sc_dmat, lesc->sc_dmam, sc->sc_mem,
329	    sc->sc_memsize, le_cbus_dma_callback, sc, 0);
330	if (error != 0 || sc->sc_addr == 0) {
331		device_printf(dev, "cannot load DMA buffer map\n");
332		goto fail_dmem;
333	}
334
335	sc->sc_flags = 0;
336	sc->sc_conf3 = 0;
337
338	/*
339	 * Extract the physical MAC address from the ROM.
340	 */
341	for (i = 0; i < sizeof(sc->sc_enaddr); i++)
342		sc->sc_enaddr[i] = bus_read_1(lesc->sc_rres, i * 2);
343
344	sc->sc_copytodesc = lance_copytobuf_contig;
345	sc->sc_copyfromdesc = lance_copyfrombuf_contig;
346	sc->sc_copytobuf = lance_copytobuf_contig;
347	sc->sc_copyfrombuf = lance_copyfrombuf_contig;
348	sc->sc_zerobuf = lance_zerobuf_contig;
349
350	sc->sc_rdcsr = le_cbus_rdcsr;
351	sc->sc_wrcsr = le_cbus_wrcsr;
352	sc->sc_hwreset = le_cbus_hwreset;
353	sc->sc_hwinit = NULL;
354	sc->sc_hwintr = NULL;
355	sc->sc_nocarrier = NULL;
356	sc->sc_mediachange = NULL;
357	sc->sc_mediastatus = NULL;
358	sc->sc_supmedia = NULL;
359
360	error = am7990_config(&lesc->sc_am7990, device_get_name(dev),
361	    device_get_unit(dev));
362	if (error != 0) {
363		device_printf(dev, "cannot attach Am7990\n");
364		goto fail_dmap;
365	}
366
367	error = bus_setup_intr(dev, lesc->sc_ires, INTR_TYPE_NET | INTR_MPSAFE,
368	    NULL, am7990_intr, sc, &lesc->sc_ih);
369	if (error != 0) {
370		device_printf(dev, "cannot set up interrupt\n");
371		goto fail_am7990;
372	}
373
374	return (0);
375
376 fail_am7990:
377	am7990_detach(&lesc->sc_am7990);
378 fail_dmap:
379	bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam);
380 fail_dmem:
381	bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam);
382 fail_dtag:
383	bus_dma_tag_destroy(lesc->sc_dmat);
384 fail_pdtag:
385	bus_dma_tag_destroy(lesc->sc_pdmat);
386 fail_ires:
387	bus_release_resource(dev, SYS_RES_IRQ,
388	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
389 fail_rres:
390	bus_release_resource(dev, SYS_RES_IOPORT,
391	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
392 fail_mtx:
393	LE_LOCK_DESTROY(sc);
394	return (error);
395}
396
397static int
398le_cbus_detach(device_t dev)
399{
400	struct le_cbus_softc *lesc;
401	struct lance_softc *sc;
402
403	lesc = device_get_softc(dev);
404	sc = &lesc->sc_am7990.lsc;
405
406	bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih);
407	am7990_detach(&lesc->sc_am7990);
408	bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam);
409	bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam);
410	bus_dma_tag_destroy(lesc->sc_dmat);
411	bus_dma_tag_destroy(lesc->sc_pdmat);
412	bus_release_resource(dev, SYS_RES_IRQ,
413	    rman_get_rid(lesc->sc_ires), lesc->sc_ires);
414	bus_release_resource(dev, SYS_RES_IOPORT,
415	    rman_get_rid(lesc->sc_rres), lesc->sc_rres);
416	LE_LOCK_DESTROY(sc);
417
418	return (0);
419}
420
421static int
422le_cbus_suspend(device_t dev)
423{
424	struct le_cbus_softc *lesc;
425
426	lesc = device_get_softc(dev);
427
428	lance_suspend(&lesc->sc_am7990.lsc);
429
430	return (0);
431}
432
433static int
434le_cbus_resume(device_t dev)
435{
436	struct le_cbus_softc *lesc;
437
438	lesc = device_get_softc(dev);
439
440	lance_resume(&lesc->sc_am7990.lsc);
441
442	return (0);
443}
444