2 Etherboot DEC Tulip driver
3 adapted by Ken Yap from
5 FreeBSD netboot DEC 21143 driver
10 Known to work on DEC DE500 using 21143-PC chipset.
11 Even on cards with the same chipset there can be
12 incompatablity problems with the way media selection
13 and status LED settings are done. See comments below.
15 Some code fragments were taken from verious places,
16 Ken Yap's etherboot, FreeBSD's if_de.c, and various
17 Linux related files. DEC's manuals for the 21143 and
18 SROM format were very helpful. The Linux de driver
19 development page has a number of links to useful
20 related information. Have a look at:
21 ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
25 #include "etherboot.h"
31 static unsigned short vendor
, dev_id
;
32 static unsigned short ioaddr
;
33 static unsigned int *membase
;
34 static unsigned char srom
[1024];
36 #define BUFLEN 1536 /* must be longword divisable */
37 /* buffers must be longword aligned */
39 /* transmit descriptor and buffer */
40 static struct txdesc txd
;
42 /* receive descriptor(s) and buffer(s) */
44 static struct rxdesc rxd
[NRXD
];
45 static int rxd_tail
= 0;
46 #ifdef USE_LOWMEM_BUFFER
47 #define rxb ((char *)0x10000 - NRXD * BUFLEN)
48 #define txb ((char *)0x10000 - NRXD * BUFLEN - BUFLEN)
50 static unsigned char rxb
[NRXD
* BUFLEN
];
51 static unsigned char txb
[BUFLEN
];
54 static unsigned char ehdr
[ETH_HLEN
]; /* buffer for ethernet header */
57 CSR0
=0, CSR1
=0x08, CSR2
=0x10, CSR3
=0x18, CSR4
=0x20, CSR5
=0x28,
58 CSR6
=0x30, CSR7
=0x38, CSR8
=0x40, CSR9
=0x48, CSR10
=0x50, CSR11
=0x58,
59 CSR12
=0x60, CSR13
=0x68, CSR14
=0x70, CSR15
=0x78 };
62 /***************************************************************************/
63 /* 21143 specific stuff */
64 /***************************************************************************/
66 /* XXX assume 33MHz PCI bus, this is not very accurate and should be
67 used only with gross over estimations of required delay times unless
68 you tune UADJUST to your specific processor and I/O subsystem */
71 static void udelay(unsigned long usec
) {
73 for (i
=((usec
*UADJUST
)/33)+1; i
>0; i
--) (void) TULIP_CSR_READ(csr_0
);
76 /* The following srom related code was taken from FreeBSD's if_de.c */
77 /* with minor alterations to make it work here. the Linux code is */
78 /* better but this was easier to use */
80 static void delay_300ns(void)
83 for (idx
= (300 / 33) + 1; idx
> 0; idx
--)
84 (void) TULIP_CSR_READ(csr_busmode
);
87 #define EMIT do { TULIP_CSR_WRITE(csr_srom_mii, csr); delay_300ns(); } while (0)
89 static void srom_idle(void)
94 csr
= SROMSEL
| SROMRD
; EMIT
;
96 csr
^= SROMCLKON
; EMIT
;
98 * Write 25 cycles of 0 which will force the SROM to be idle.
100 for (bit
= 3 + SROM_BITWIDTH
+ 16; bit
> 0; bit
--) {
101 csr
^= SROMCLKOFF
; EMIT
; /* clock low; data not valid */
102 csr
^= SROMCLKON
; EMIT
; /* clock high; data valid */
104 csr
^= SROMCLKOFF
; EMIT
;
109 static void srom_read(void)
112 const unsigned bitwidth
= SROM_BITWIDTH
;
113 const unsigned cmdmask
= (SROMCMD_RD
<< bitwidth
);
114 const unsigned msb
= 1 << (bitwidth
+ 3 - 1);
115 unsigned lastidx
= (1 << bitwidth
) - 1;
119 for (idx
= 0; idx
<= lastidx
; idx
++) {
120 unsigned lastbit
, data
, bits
, bit
, csr
;
121 csr
= SROMSEL
; EMIT
;
122 csr
= SROMSEL
| SROMRD
; EMIT
;
123 csr
^= SROMCSON
; EMIT
;
124 csr
^= SROMCLKON
; EMIT
;
127 for (bits
= idx
|cmdmask
, bit
= bitwidth
+ 3; bit
> 0; bit
--, bits
<<= 1)
129 const unsigned thisbit
= bits
& msb
;
130 csr
^= SROMCLKOFF
; EMIT
; /* clock low; data not valid */
131 if (thisbit
!= lastbit
) {
132 csr
^= SROMDOUT
; EMIT
; /* clock low; invert data */
136 csr
^= SROMCLKON
; EMIT
; /* clock high; data valid */
139 csr
^= SROMCLKOFF
; EMIT
;
141 for (data
= 0, bits
= 0; bits
< 16; bits
++) {
143 csr
^= SROMCLKON
; EMIT
; /* clock high; data valid */
144 data
|= TULIP_CSR_READ(csr_srom_mii
) & SROMDIN
? 1 : 0;
145 csr
^= SROMCLKOFF
; EMIT
; /* clock low; data not valid */
147 srom
[idx
*2] = data
& 0xFF;
148 srom
[idx
*2+1] = data
>> 8;
149 csr
= SROMSEL
| SROMRD
; EMIT
;
155 /**************************************************************************
156 ETH_RESET - Reset adapter
157 ***************************************************************************/
158 static void tulip_reset(struct nic
*nic
)
162 outl(0x00000001, ioaddr
+ CSR0
);
164 /* turn off reset and set cache align=16lword, burst=unlimit */
165 outl(0x01A08000, ioaddr
+ CSR0
);
167 /* for some reason the media selection does not take
168 the first time se it is repeated. */
171 /* stop TX,RX processes */
173 outl(0x32404000, ioaddr
+ CSR6
);
175 outl(0x32000040, ioaddr
+ CSR6
);
177 /* XXX - media selection is vendor specific and hard coded right
178 here. This should be fixed to use the hints in the SROM and
179 allow media selection by the user at runtime. MII support
180 should also be added. Support for chips other than the
181 21143 should be added here as well */
183 /* start set to 10Mbps half-duplex */
186 outl(0x0, ioaddr
+ CSR13
); /* reset SIA */
187 outl(0x7f3f, ioaddr
+ CSR14
);
188 outl(0x8000008, ioaddr
+ CSR15
);
189 outl(0x0, ioaddr
+ CSR13
);
190 outl(0x1, ioaddr
+ CSR13
);
191 outl(0x2404000, ioaddr
+ CSR6
);
194 outl(0x8af0008, ioaddr
+ CSR15
);
195 outl(0x50008, ioaddr
+ CSR15
);
197 /* end set to 10Mbps half-duplex */
199 if (vendor
== PCI_VENDOR_ID_MACRONIX
&& dev_id
== PCI_DEVICE_ID_MX987x5
) {
200 /* do stuff for MX98715 */
201 outl(0x01a80000, ioaddr
+ CSR6
);
202 outl(0xFFFFFFFF, ioaddr
+ CSR14
);
203 outl(0x00001000, ioaddr
+ CSR12
);
206 outl(0x0, ioaddr
+ CSR7
); /* disable interrupts */
208 /* construct setup packet which is used by the 21143 to
209 program its CAM to recognize interesting MAC addresses */
211 memset(&txd
, 0, sizeof(struct txdesc
));
212 txd
.buf1addr
= &txb
[0];
213 txd
.buf2addr
= &txb
[0]; /* just in case */
214 txd
.buf1sz
= 192; /* setup packet must be 192 bytes */
216 txd
.control
= 0x020; /* setup packet */
217 txd
.status
= 0x80000000; /* give ownership to 21143 */
219 /* construct perfect filter frame */
220 /* with mac address as first match */
221 /* and broadcast address for all others */
223 for(x
=0;x
<192;x
++) txb
[x
] = 0xff;
224 txb
[0] = nic
->node_addr
[0];
225 txb
[1] = nic
->node_addr
[1];
226 txb
[4] = nic
->node_addr
[2];
227 txb
[5] = nic
->node_addr
[3];
228 txb
[8] = nic
->node_addr
[4];
229 txb
[9] = nic
->node_addr
[5];
230 outl((unsigned long)&txd
, ioaddr
+ CSR4
); /* set xmit buf */
231 outl(0x2406000, ioaddr
+ CSR6
); /* start transmiter */
233 udelay(50000); /* wait for the setup packet to be processed */
237 /* setup receive descriptor */
240 for(x
=0;x
<NRXD
;x
++) {
241 memset(&rxd
[x
], 0, sizeof(struct rxdesc
));
242 rxd
[x
].buf1addr
= &rxb
[x
* BUFLEN
];
243 rxd
[x
].buf2addr
= 0; /* not used */
244 rxd
[x
].buf1sz
= BUFLEN
;
245 rxd
[x
].buf2sz
= 0; /* not used */
246 rxd
[x
].control
= 0x0;
247 rxd
[x
].status
= 0x80000000; /* give ownership it to 21143 */
249 rxd
[NRXD
- 1].control
= 0x008; /* Set Receive end of ring on la
254 /* tell DC211XX where to find rx descriptor list */
255 outl((unsigned long)&rxd
[0], ioaddr
+ CSR3
);
256 /* start the receiver */
257 outl(0x2406002, ioaddr
+ CSR6
);
261 /**************************************************************************
262 ETH_TRANSMIT - Transmit a frame
263 ***************************************************************************/
264 static const char padmap
[] = {
267 static void tulip_transmit(struct nic
*nic
, const char *d
, unsigned int t
, unsigned int s
, const char *p
)
271 /* setup ethernet header */
273 memcpy(ehdr
, d
, ETH_ALEN
);
274 memcpy(&ehdr
[ETH_ALEN
], nic
->node_addr
, ETH_ALEN
);
275 ehdr
[ETH_ALEN
*2] = (t
>> 8) & 0xff;
276 ehdr
[ETH_ALEN
*2+1] = t
& 0xff;
278 /* setup the transmit descriptor */
280 memset(&txd
, 0, sizeof(struct txdesc
));
282 txd
.buf1addr
= &ehdr
[0]; /* ethernet header */
283 txd
.buf1sz
= ETH_HLEN
;
285 txd
.buf2addr
= p
; /* packet to transmit */
288 txd
.control
= 0x188; /* LS+FS+TER */
290 txd
.status
= 0x80000000; /* give it to 21143 */
292 outl(inl(ioaddr
+ CSR6
) & ~0x00004000, ioaddr
+ CSR6
);
293 outl((unsigned long)&txd
, ioaddr
+ CSR4
);
294 outl(inl(ioaddr
+ CSR6
) | 0x00004000, ioaddr
+ CSR6
);
296 /* Wait for transmit to complete before returning. not well tested.
299 while(txd.status & 0x80000000) {
300 if (currticks() - time > 20) {
301 printf("transmit timeout.\n");
309 /**************************************************************************
310 ETH_POLL - Wait for a frame
311 ***************************************************************************/
312 static int tulip_poll(struct nic
*nic
)
314 if (rxd
[rxd_tail
].status
& 0x80000000) return 0;
316 nic
->packetlen
= (rxd
[rxd_tail
].status
& 0x3FFF0000) >> 16;
318 /* copy packet to working buffer */
319 /* XXX - this copy could be avoided with a little more work
320 but for now we are content with it because the optimised
321 memcpy(, , ) is quite fast */
323 memcpy(nic
->packet
, rxb
+ rxd_tail
* BUFLEN
, nic
->packetlen
);
325 /* return the descriptor and buffer to recieve ring */
326 rxd
[rxd_tail
].status
= 0x80000000;
328 if (rxd_tail
== NRXD
) rxd_tail
= 0;
333 static void tulip_disable(struct nic
*nic
)
335 /* nothing for the moment */
338 /**************************************************************************
339 ETH_PROBE - Look for an adapter
340 ***************************************************************************/
341 struct nic
*otulip_probe(struct nic
*nic
, unsigned short *io_addrs
, struct pci_device
*pci
)
345 if (io_addrs
== 0 || *io_addrs
== 0)
347 vendor
= pci
->vendor
;
348 dev_id
= pci
->dev_id
;
350 membase
= (unsigned int *)pci
->membase
;
353 pcibios_write_config_dword(pci
->bus
,pci
->devfn
,0x40,0x00000000);
355 /* Stop the chip's Tx and Rx processes. */
356 /* outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); */
357 /* Clear the missed-packet counter. */
358 /* (volatile int)inl(ioaddr + CSR8); */
362 for (i
=0; i
< ETH_ALEN
; i
++)
363 nic
->node_addr
[i
] = srom
[20+i
];
365 printf("Tulip %! at ioaddr %#hX\n", nic
->node_addr
, ioaddr
);
369 nic
->reset
= tulip_reset
;
370 nic
->poll
= tulip_poll
;
371 nic
->transmit
= tulip_transmit
;
372 nic
->disable
= tulip_disable
;