if_en_pci.c revision 50107
1/*	$NetBSD: if_en_pci.c,v 1.1 1996/06/22 02:00:31 chuck Exp $	*/
2
3/*
4 *
5 * Copyright (c) 1996 Charles D. Cranor and Washington University.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *      This product includes software developed by Charles D. Cranor and
19 *	Washington University.
20 * 4. The name of the author may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 * $FreeBSD: head/sys/dev/en/if_en_pci.c 50107 1999-08-21 06:24:40Z msmith $
35 */
36
37/*
38 *
39 * i f _ e n _ p c i . c
40 *
41 * author: Chuck Cranor <chuck@ccrc.wustl.edu>
42 * started: spring, 1996.
43 *
44 * FreeBSD PCI glue for the eni155p card.
45 * thanks to Matt Thomas for figuring out FreeBSD vs NetBSD vs etc.. diffs.
46 */
47
48#include "en.h"
49
50#include <sys/param.h>
51#include <sys/kernel.h>
52#include <sys/systm.h>
53#include <sys/malloc.h>
54#include <sys/socket.h>
55
56#include <machine/clock.h>		/* for DELAY */
57
58#include <net/if.h>
59
60#include <pci/pcivar.h>
61#include <pci/pcireg.h>
62
63#include <dev/en/midwayreg.h>
64#include <dev/en/midwayvar.h>
65
66
67/*
68 * prototypes
69 */
70
71static	void en_pci_attach __P((pcici_t, int));
72static	const char *en_pci_probe __P((pcici_t, pcidi_t));
73static void en_pci_shutdown __P((void *, int));
74
75/*
76 * local structures
77 */
78
79struct en_pci_softc {
80  /* bus independent stuff */
81  struct en_softc esc;		/* includes "device" structure */
82
83  /* PCI bus glue */
84  void *sc_ih;			/* interrupt handle */
85  pci_chipset_tag_t en_pc;	/* for PCI calls */
86  pcici_t en_confid;		/* config id */
87};
88
89#if !defined(MIDWAY_ENIONLY)
90static  void eni_get_macaddr __P((struct en_pci_softc *));
91#endif
92#if !defined(MIDWAY_ADPONLY)
93static  void adp_get_macaddr __P((struct en_pci_softc *));
94#endif
95
96/*
97 * pointers to softcs (we alloc)
98 */
99
100static struct en_pci_softc *enpcis[NEN] = {0};
101extern struct cfdriver en_cd;
102
103/*
104 * autoconfig structures
105 */
106
107static u_long en_pci_count;
108
109static struct pci_device endevice = {
110	"en",
111	en_pci_probe,
112	en_pci_attach,
113	&en_pci_count,
114	NULL,
115};
116
117COMPAT_PCI_DRIVER (en, endevice);
118
119/*
120 * local defines (PCI specific stuff)
121 */
122
123/*
124 * address of config base memory address register in PCI config space
125 * (this is card specific)
126 */
127
128#define PCI_CBMA        0x10
129
130/*
131 * tonga (pci bridge).   ENI cards only!
132 */
133
134#define EN_TONGA        0x60            /* PCI config addr of tonga reg */
135
136#define TONGA_SWAP_DMA  0x80            /* endian swap control */
137#define TONGA_SWAP_BYTE 0x40
138#define TONGA_SWAP_WORD 0x20
139
140/*
141 * adaptec pci bridge.   ADP cards only!
142 */
143
144#define ADP_PCIREG      0x050040        /* PCI control register */
145
146#define ADP_PCIREG_RESET        0x1     /* reset card */
147#define ADP_PCIREG_IENABLE	0x2	/* interrupt enable */
148#define ADP_PCIREG_SWAP_WORD	0x4	/* swap byte on slave access */
149#define ADP_PCIREG_SWAP_DMA	0x8	/* swap byte on DMA */
150
151#define PCI_VENDOR_EFFICIENTNETS 0x111a			/* Efficent Networks */
152#define PCI_PRODUCT_EFFICIENTNETS_ENI155PF 0x0000	/* ENI-155P ATM */
153#define PCI_PRODUCT_EFFICIENTNETS_ENI155PA 0x0002	/* ENI-155P ATM */
154#define PCI_VENDOR_ADP 0x9004				/* adaptec */
155#define PCI_PRODUCT_ADP_AIC5900 0x5900
156#define PCI_PRODUCT_ADP_AIC5905 0x5905
157#define PCI_VENDOR(x)		((x) & 0xFFFF)
158#define PCI_CHIPID(x)		(((x) >> 16) & 0xFFFF)
159
160#if !defined(MIDWAY_ENIONLY)
161
162static void adp_busreset __P((void *));
163
164/*
165 * bus specific reset function [ADP only!]
166 */
167
168static void adp_busreset(v)
169
170void *v;
171
172{
173  struct en_softc *sc = (struct en_softc *) v;
174  u_int32_t dummy;
175
176  bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG, ADP_PCIREG_RESET);
177  DELAY(1000);  /* let it reset */
178  dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
179  bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG,
180		(ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA|ADP_PCIREG_IENABLE));
181  dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
182  if ((dummy & (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA)) !=
183		(ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA))
184    printf("adp_busreset: Adaptec ATM did NOT reset!\n");
185}
186#endif
187
188/***********************************************************************/
189
190/*
191 * autoconfig stuff
192 */
193
194static const char *en_pci_probe(config_id, device_id)
195
196pcici_t config_id;
197pcidi_t device_id;
198
199{
200#if !defined(MIDWAY_ADPONLY)
201  if (PCI_VENDOR(device_id) == PCI_VENDOR_EFFICIENTNETS &&
202      (PCI_CHIPID(device_id) == PCI_PRODUCT_EFFICIENTNETS_ENI155PF ||
203       PCI_CHIPID(device_id) == PCI_PRODUCT_EFFICIENTNETS_ENI155PA))
204    return "Efficient Networks ENI-155p";
205#endif
206
207#if !defined(MIDWAY_ENIONLY)
208  if (PCI_VENDOR(device_id) == PCI_VENDOR_ADP &&
209      (PCI_CHIPID(device_id) == PCI_PRODUCT_ADP_AIC5900 ||
210       PCI_CHIPID(device_id) == PCI_PRODUCT_ADP_AIC5905))
211    return "Adaptec 155 ATM";
212#endif
213
214  return 0;
215}
216
217static void en_pci_attach(config_id, unit)
218
219pcici_t config_id;
220int unit;
221
222{
223  struct en_softc *sc;
224  struct en_pci_softc *scp;
225  pcidi_t device_id;
226  int retval;
227  vm_offset_t pa;
228
229  if (unit >= NEN) {
230    printf("en%d: not configured; kernel is built for only %d device%s.\n",
231	unit, NEN, NEN == 1 ? "" : "s");
232    return;
233  }
234
235  scp = (struct en_pci_softc *) malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT);
236  if (scp == NULL)
237    return;
238  bzero(scp, sizeof(*scp));		/* zero */
239  sc = &scp->esc;
240
241  retval = pci_map_mem(config_id, PCI_CBMA, (vm_offset_t *) &sc->en_base, &pa);
242
243  if (!retval) {
244    free((caddr_t) scp, M_DEVBUF);
245    return;
246  }
247  enpcis[unit] = scp;			/* lock it in */
248  en_cd.cd_devs[unit] = sc;		/* fake a cfdriver structure */
249  en_cd.cd_ndevs = NEN;
250  snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname), "en%d", unit);
251  sc->enif.if_unit = unit;
252  sc->enif.if_name = "en";
253  scp->en_confid = config_id;
254
255  /*
256   * figure out if we are an adaptec card or not.
257   * XXX: why do we have to re-read PC_ID_REG when en_pci_probe already
258   * had that info?
259   */
260
261  device_id = pci_conf_read(config_id, PCI_ID_REG);
262  sc->is_adaptec = (PCI_VENDOR(device_id) == PCI_VENDOR_ADP) ? 1 : 0;
263
264  /*
265   * Add shutdown hook so that DMA is disabled prior to reboot. Not
266   * doing so could allow DMA to corrupt kernel memory during the
267   * reboot before the driver initializes.
268   */
269  EVENTHANDLER_REGISTER(shutdown_post_sync, en_pci_shutdown, scp,
270			SHUTDOWN_PRI_DEFAULT);
271
272  if (!pci_map_int(config_id, en_intr, (void *) sc, &net_imask)) {
273    printf("%s: couldn't establish interrupt\n", sc->sc_dev.dv_xname);
274    return;
275  }
276  sc->ipl = 1; /* XXX */
277
278  /*
279   * set up pci bridge
280   */
281
282#if !defined(MIDWAY_ENIONLY)
283  if (sc->is_adaptec) {
284    adp_get_macaddr(scp);
285    sc->en_busreset = adp_busreset;
286    adp_busreset(sc);
287  }
288#endif
289
290#if !defined(MIDWAY_ADPONLY)
291  if (!sc->is_adaptec) {
292    eni_get_macaddr(scp);
293    sc->en_busreset = NULL;
294    pci_conf_write(config_id, EN_TONGA, (TONGA_SWAP_DMA|TONGA_SWAP_WORD));
295  }
296#endif
297
298  /*
299   * done PCI specific stuff
300   */
301
302  en_attach(sc);
303
304}
305
306static void
307en_pci_shutdown(
308	void *sc,
309	int howto)
310{
311    struct en_pci_softc *psc = (struct en_pci_softc *)sc;
312
313    en_reset(&psc->esc);
314    DELAY(10);
315}
316
317#if !defined(MIDWAY_ENIONLY)
318
319#if defined(sparc) || defined(__FreeBSD__)
320#define bus_space_read_1(t, h, o) \
321  		((void)t, (*(volatile u_int8_t *)((h) + (o))))
322#endif
323
324static void
325adp_get_macaddr(scp)
326     struct en_pci_softc *scp;
327{
328  struct en_softc * sc = (struct en_softc *)scp;
329  int lcv;
330
331  for (lcv = 0; lcv < sizeof(sc->macaddr); lcv++)
332    sc->macaddr[lcv] = bus_space_read_1(sc->en_memt, sc->en_base,
333					MID_ADPMACOFF + lcv);
334}
335
336#endif /* MIDWAY_ENIONLY */
337
338#if !defined(MIDWAY_ADPONLY)
339
340/*
341 * Read station (MAC) address from serial EEPROM.
342 * derived from linux drivers/atm/eni.c by Werner Almesberger, EPFL LRC.
343 */
344#define EN_PROM_MAGIC  0x0c
345#define EN_PROM_DATA   0x02
346#define EN_PROM_CLK    0x01
347#define EN_ESI         64
348
349static void
350eni_get_macaddr(scp)
351     struct en_pci_softc *scp;
352{
353  struct en_softc * sc = (struct en_softc *)scp;
354  pcici_t id = scp->en_confid;
355  int i, j, address, status;
356  u_int32_t data, t_data;
357  u_int8_t tmp;
358
359  t_data = pci_conf_read(id, EN_TONGA) & 0xffffff00;
360
361  data =  EN_PROM_MAGIC | EN_PROM_DATA | EN_PROM_CLK;
362  pci_conf_write(id, EN_TONGA, data);
363
364  for (i = 0; i < sizeof(sc->macaddr); i ++){
365    /* start operation */
366    data |= EN_PROM_DATA ;
367    pci_conf_write(id, EN_TONGA, data);
368    data |= EN_PROM_CLK ;
369    pci_conf_write(id, EN_TONGA, data);
370    data &= ~EN_PROM_DATA ;
371    pci_conf_write(id, EN_TONGA, data);
372    data &= ~EN_PROM_CLK ;
373    pci_conf_write(id, EN_TONGA, data);
374    /* send address with serial line */
375    address = ((i + EN_ESI) << 1) + 1;
376    for ( j = 7 ; j >= 0 ; j --){
377      data = (address >> j) & 1 ? data | EN_PROM_DATA :
378      data & ~EN_PROM_DATA;
379      pci_conf_write(id, EN_TONGA, data);
380      data |= EN_PROM_CLK ;
381      pci_conf_write(id, EN_TONGA, data);
382      data &= ~EN_PROM_CLK ;
383      pci_conf_write(id, EN_TONGA, data);
384    }
385    /* get ack */
386    data |= EN_PROM_DATA ;
387    pci_conf_write(id, EN_TONGA, data);
388    data |= EN_PROM_CLK ;
389    pci_conf_write(id, EN_TONGA, data);
390    data = pci_conf_read(id, EN_TONGA);
391    status = data & EN_PROM_DATA;
392    data &= ~EN_PROM_CLK ;
393    pci_conf_write(id, EN_TONGA, data);
394    data |= EN_PROM_DATA ;
395    pci_conf_write(id, EN_TONGA, data);
396
397    tmp = 0;
398
399    for ( j = 7 ; j >= 0 ; j --){
400      tmp <<= 1;
401      data |= EN_PROM_DATA ;
402      pci_conf_write(id, EN_TONGA, data);
403      data |= EN_PROM_CLK ;
404      pci_conf_write(id, EN_TONGA, data);
405      data = pci_conf_read(id, EN_TONGA);
406      if(data & EN_PROM_DATA) tmp |= 1;
407      data &= ~EN_PROM_CLK ;
408      pci_conf_write(id, EN_TONGA, data);
409      data |= EN_PROM_DATA ;
410      pci_conf_write(id, EN_TONGA, data);
411    }
412    /* get ack */
413    data |= EN_PROM_DATA ;
414    pci_conf_write(id, EN_TONGA, data);
415    data |= EN_PROM_CLK ;
416    pci_conf_write(id, EN_TONGA, data);
417    data = pci_conf_read(id, EN_TONGA);
418    status = data & EN_PROM_DATA;
419    data &= ~EN_PROM_CLK ;
420    pci_conf_write(id, EN_TONGA, data);
421    data |= EN_PROM_DATA ;
422    pci_conf_write(id, EN_TONGA, data);
423
424    sc->macaddr[i] = tmp;
425  }
426  /* stop operation */
427  data &=  ~EN_PROM_DATA;
428  pci_conf_write(id, EN_TONGA, data);
429  data |=  EN_PROM_CLK;
430  pci_conf_write(id, EN_TONGA, data);
431  data |=  EN_PROM_DATA;
432  pci_conf_write(id, EN_TONGA, data);
433  pci_conf_write(id, EN_TONGA, t_data);
434}
435
436#endif /* !MIDWAY_ADPONLY */
437