if_en_pci.c revision 46024
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
35/*
36 *
37 * i f _ e n _ p c i . c
38 *
39 * author: Chuck Cranor <chuck@ccrc.wustl.edu>
40 * started: spring, 1996.
41 *
42 * FreeBSD PCI glue for the eni155p card.
43 * thanks to Matt Thomas for figuring out FreeBSD vs NetBSD vs etc.. diffs.
44 */
45
46#include "en.h"
47#include "pci.h"
48#if (NEN > 0) && (NPCI > 0)
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((int, void *));
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
117#ifdef COMPAT_PCI_DRIVER
118COMPAT_PCI_DRIVER (en, endevice);
119#else
120DATA_SET (pcidevice_set, endevice);
121#endif /* COMPAT_PCI_DRIVER */
122
123/*
124 * local defines (PCI specific stuff)
125 */
126
127/*
128 * address of config base memory address register in PCI config space
129 * (this is card specific)
130 */
131
132#define PCI_CBMA        0x10
133
134/*
135 * tonga (pci bridge).   ENI cards only!
136 */
137
138#define EN_TONGA        0x60            /* PCI config addr of tonga reg */
139
140#define TONGA_SWAP_DMA  0x80            /* endian swap control */
141#define TONGA_SWAP_BYTE 0x40
142#define TONGA_SWAP_WORD 0x20
143
144/*
145 * adaptec pci bridge.   ADP cards only!
146 */
147
148#define ADP_PCIREG      0x050040        /* PCI control register */
149
150#define ADP_PCIREG_RESET        0x1     /* reset card */
151#define ADP_PCIREG_IENABLE	0x2	/* interrupt enable */
152#define ADP_PCIREG_SWAP_WORD	0x4	/* swap byte on slave access */
153#define ADP_PCIREG_SWAP_DMA	0x8	/* swap byte on DMA */
154
155#define PCI_VENDOR_EFFICIENTNETS 0x111a			/* Efficent Networks */
156#define PCI_PRODUCT_EFFICIENTNETS_ENI155PF 0x0000	/* ENI-155P ATM */
157#define PCI_PRODUCT_EFFICIENTNETS_ENI155PA 0x0002	/* ENI-155P ATM */
158#define PCI_VENDOR_ADP 0x9004				/* adaptec */
159#define PCI_PRODUCT_ADP_AIC5900 0x5900
160#define PCI_PRODUCT_ADP_AIC5905 0x5905
161#define PCI_VENDOR(x)		((x) & 0xFFFF)
162#define PCI_CHIPID(x)		(((x) >> 16) & 0xFFFF)
163
164#if !defined(MIDWAY_ENIONLY)
165
166static void adp_busreset __P((void *));
167
168/*
169 * bus specific reset function [ADP only!]
170 */
171
172static void adp_busreset(v)
173
174void *v;
175
176{
177  struct en_softc *sc = (struct en_softc *) v;
178  u_int32_t dummy;
179
180  bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG, ADP_PCIREG_RESET);
181  DELAY(1000);  /* let it reset */
182  dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
183  bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG,
184		(ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA|ADP_PCIREG_IENABLE));
185  dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
186  if ((dummy & (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA)) !=
187		(ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA))
188    printf("adp_busreset: Adaptec ATM did NOT reset!\n");
189}
190#endif
191
192/***********************************************************************/
193
194/*
195 * autoconfig stuff
196 */
197
198static const char *en_pci_probe(config_id, device_id)
199
200pcici_t config_id;
201pcidi_t device_id;
202
203{
204#if !defined(MIDWAY_ADPONLY)
205  if (PCI_VENDOR(device_id) == PCI_VENDOR_EFFICIENTNETS &&
206      (PCI_CHIPID(device_id) == PCI_PRODUCT_EFFICIENTNETS_ENI155PF ||
207       PCI_CHIPID(device_id) == PCI_PRODUCT_EFFICIENTNETS_ENI155PA))
208    return "Efficient Networks ENI-155p";
209#endif
210
211#if !defined(MIDWAY_ENIONLY)
212  if (PCI_VENDOR(device_id) == PCI_VENDOR_ADP &&
213      (PCI_CHIPID(device_id) == PCI_PRODUCT_ADP_AIC5900 ||
214       PCI_CHIPID(device_id) == PCI_PRODUCT_ADP_AIC5905))
215    return "Adaptec 155 ATM";
216#endif
217
218  return 0;
219}
220
221static void en_pci_attach(config_id, unit)
222
223pcici_t config_id;
224int unit;
225
226{
227  struct en_softc *sc;
228  struct en_pci_softc *scp;
229  pcidi_t device_id;
230  int retval;
231  vm_offset_t pa;
232
233  if (unit >= NEN) {
234    printf("en%d: not configured; kernel is built for only %d device%s.\n",
235	unit, NEN, NEN == 1 ? "" : "s");
236    return;
237  }
238
239  scp = (struct en_pci_softc *) malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT);
240  if (scp == NULL)
241    return;
242  bzero(scp, sizeof(*scp));		/* zero */
243  sc = &scp->esc;
244
245  retval = pci_map_mem(config_id, PCI_CBMA, (vm_offset_t *) &sc->en_base, &pa);
246
247  if (!retval) {
248    free((caddr_t) scp, M_DEVBUF);
249    return;
250  }
251  enpcis[unit] = scp;			/* lock it in */
252  en_cd.cd_devs[unit] = sc;		/* fake a cfdriver structure */
253  en_cd.cd_ndevs = NEN;
254  snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname), "en%d", unit);
255  sc->enif.if_unit = unit;
256  sc->enif.if_name = "en";
257  scp->en_confid = config_id;
258
259  /*
260   * figure out if we are an adaptec card or not.
261   * XXX: why do we have to re-read PC_ID_REG when en_pci_probe already
262   * had that info?
263   */
264
265  device_id = pci_conf_read(config_id, PCI_ID_REG);
266  sc->is_adaptec = (PCI_VENDOR(device_id) == PCI_VENDOR_ADP) ? 1 : 0;
267
268  /*
269   * Add shutdown hook so that DMA is disabled prior to reboot. Not
270   * doing so could allow DMA to corrupt kernel memory during the
271   * reboot before the driver initializes.
272   */
273  at_shutdown(en_pci_shutdown, scp, SHUTDOWN_POST_SYNC);
274
275  if (!pci_map_int(config_id, en_intr, (void *) sc, &net_imask)) {
276    printf("%s: couldn't establish interrupt\n", sc->sc_dev.dv_xname);
277    return;
278  }
279  sc->ipl = 1; /* XXX */
280
281  /*
282   * set up pci bridge
283   */
284
285#if !defined(MIDWAY_ENIONLY)
286  if (sc->is_adaptec) {
287    adp_get_macaddr(scp);
288    sc->en_busreset = adp_busreset;
289    adp_busreset(sc);
290  }
291#endif
292
293#if !defined(MIDWAY_ADPONLY)
294  if (!sc->is_adaptec) {
295    eni_get_macaddr(scp);
296    sc->en_busreset = NULL;
297    pci_conf_write(config_id, EN_TONGA, (TONGA_SWAP_DMA|TONGA_SWAP_WORD));
298  }
299#endif
300
301  /*
302   * done PCI specific stuff
303   */
304
305  en_attach(sc);
306
307}
308
309static void
310en_pci_shutdown(
311	int howto,
312	void *sc)
313{
314    struct en_pci_softc *psc = (struct en_pci_softc *)sc;
315
316    en_reset(&psc->esc);
317    DELAY(10);
318}
319
320#if !defined(MIDWAY_ENIONLY)
321
322#if defined(sparc) || defined(__FreeBSD__)
323#define bus_space_read_1(t, h, o) \
324  		((void)t, (*(volatile u_int8_t *)((h) + (o))))
325#endif
326
327static void
328adp_get_macaddr(scp)
329     struct en_pci_softc *scp;
330{
331  struct en_softc * sc = (struct en_softc *)scp;
332  int lcv;
333
334  for (lcv = 0; lcv < sizeof(sc->macaddr); lcv++)
335    sc->macaddr[lcv] = bus_space_read_1(sc->en_memt, sc->en_base,
336					MID_ADPMACOFF + lcv);
337}
338
339#endif /* MIDWAY_ENIONLY */
340
341#if !defined(MIDWAY_ADPONLY)
342
343/*
344 * Read station (MAC) address from serial EEPROM.
345 * derived from linux drivers/atm/eni.c by Werner Almesberger, EPFL LRC.
346 */
347#define EN_PROM_MAGIC  0x0c
348#define EN_PROM_DATA   0x02
349#define EN_PROM_CLK    0x01
350#define EN_ESI         64
351
352static void
353eni_get_macaddr(scp)
354     struct en_pci_softc *scp;
355{
356  struct en_softc * sc = (struct en_softc *)scp;
357  pcici_t id = scp->en_confid;
358  int i, j, address, status;
359  u_int32_t data, t_data;
360  u_int8_t tmp;
361
362  t_data = pci_conf_read(id, EN_TONGA) & 0xffffff00;
363
364  data =  EN_PROM_MAGIC | EN_PROM_DATA | EN_PROM_CLK;
365  pci_conf_write(id, EN_TONGA, data);
366
367  for (i = 0; i < sizeof(sc->macaddr); i ++){
368    /* start operation */
369    data |= EN_PROM_DATA ;
370    pci_conf_write(id, EN_TONGA, data);
371    data |= EN_PROM_CLK ;
372    pci_conf_write(id, EN_TONGA, data);
373    data &= ~EN_PROM_DATA ;
374    pci_conf_write(id, EN_TONGA, data);
375    data &= ~EN_PROM_CLK ;
376    pci_conf_write(id, EN_TONGA, data);
377    /* send address with serial line */
378    address = ((i + EN_ESI) << 1) + 1;
379    for ( j = 7 ; j >= 0 ; j --){
380      data = (address >> j) & 1 ? data | EN_PROM_DATA :
381      data & ~EN_PROM_DATA;
382      pci_conf_write(id, EN_TONGA, data);
383      data |= EN_PROM_CLK ;
384      pci_conf_write(id, EN_TONGA, data);
385      data &= ~EN_PROM_CLK ;
386      pci_conf_write(id, EN_TONGA, data);
387    }
388    /* get ack */
389    data |= EN_PROM_DATA ;
390    pci_conf_write(id, EN_TONGA, data);
391    data |= EN_PROM_CLK ;
392    pci_conf_write(id, EN_TONGA, data);
393    data = pci_conf_read(id, EN_TONGA);
394    status = data & EN_PROM_DATA;
395    data &= ~EN_PROM_CLK ;
396    pci_conf_write(id, EN_TONGA, data);
397    data |= EN_PROM_DATA ;
398    pci_conf_write(id, EN_TONGA, data);
399
400    tmp = 0;
401
402    for ( j = 7 ; j >= 0 ; j --){
403      tmp <<= 1;
404      data |= EN_PROM_DATA ;
405      pci_conf_write(id, EN_TONGA, data);
406      data |= EN_PROM_CLK ;
407      pci_conf_write(id, EN_TONGA, data);
408      data = pci_conf_read(id, EN_TONGA);
409      if(data & EN_PROM_DATA) tmp |= 1;
410      data &= ~EN_PROM_CLK ;
411      pci_conf_write(id, EN_TONGA, data);
412      data |= EN_PROM_DATA ;
413      pci_conf_write(id, EN_TONGA, data);
414    }
415    /* get ack */
416    data |= EN_PROM_DATA ;
417    pci_conf_write(id, EN_TONGA, data);
418    data |= EN_PROM_CLK ;
419    pci_conf_write(id, EN_TONGA, data);
420    data = pci_conf_read(id, EN_TONGA);
421    status = data & EN_PROM_DATA;
422    data &= ~EN_PROM_CLK ;
423    pci_conf_write(id, EN_TONGA, data);
424    data |= EN_PROM_DATA ;
425    pci_conf_write(id, EN_TONGA, data);
426
427    sc->macaddr[i] = tmp;
428  }
429  /* stop operation */
430  data &=  ~EN_PROM_DATA;
431  pci_conf_write(id, EN_TONGA, data);
432  data |=  EN_PROM_CLK;
433  pci_conf_write(id, EN_TONGA, data);
434  data |=  EN_PROM_DATA;
435  pci_conf_write(id, EN_TONGA, data);
436  pci_conf_write(id, EN_TONGA, t_data);
437}
438
439#endif /* !MIDWAY_ADPONLY */
440
441#endif /* NEN > 0 && NPCI > 0 */
442