m5a99x-evo: sup @ ramstage :-)
[coreboot.git] / src / lib / ne2k.c
1 /*
2 ETHERBOOT -  BOOTP/TFTP Bootstrap Program
3
4 Author: Martin Renters
5   Date: May/94
6
7  This code is based heavily on David Greenman's if_ed.c driver
8
9  Copyright (C) 1993-1994, David Greenman, Martin Renters.
10   This software may be used, modified, copied, distributed, and sold, in
11   both source and binary form provided that the above copyright and these
12   terms are retained. Under no circumstances are the authors responsible for
13   the proper functioning of this software, nor do the authors assume any
14   responsibility for damages incurred with its use.
15
16 Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
17 Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
18 3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
19 SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
20 3c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98
21 RX overrun by Klaus Espenlaub (espenlaub@informatik.uni-ulm.de) on 3/10/99
22   parts taken from the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
23 SMC8416 PIO support added by Andrew Bettison (andrewb@zip.com.au) on 4/3/02
24   based on the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
25
26 (C) Rudolf Marek <r.marek@assembler.cz> Simplify for RTL8029, Add coreboot glue logic
27
28 */
29
30 #define ETH_ALEN                6       /* Size of Ethernet address */
31 #define ETH_HLEN                14      /* Size of ethernet header */
32 #define ETH_ZLEN                60      /* Minimum packet */
33 #define ETH_FRAME_LEN           1514    /* Maximum packet */
34 #define ETH_DATA_ALIGN          2       /* Amount needed to align the data after an ethernet header */
35 #define ETH_MAX_MTU             (ETH_FRAME_LEN-ETH_HLEN)
36
37 #include "ns8390.h"
38 #include <ip_checksum.h>
39 #include <console/ne2k.h>
40 #include <arch/io.h>
41 //#include <arch/romcc_io.h>
42
43 #define MEM_SIZE MEM_32768
44 #define TX_START 64
45 #define RX_START (64 + D8390_TXBUF_SIZE)
46
47 static unsigned int get_count(unsigned int eth_nic_base)
48 {
49         unsigned int ret;
50         outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
51              eth_nic_base + D8390_P0_COMMAND);
52
53         ret = inb(eth_nic_base + 8 + 0) | (inb(eth_nic_base + 8 + 1) << 8);
54
55         outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
56              eth_nic_base + D8390_P0_COMMAND);
57         return ret;
58 }
59
60 static void set_count(unsigned int eth_nic_base, unsigned int what)
61 {
62         outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
63              eth_nic_base + D8390_P0_COMMAND);
64
65         outb(what & 0xff,eth_nic_base + 8);
66         outb((what >> 8) & 0xff,eth_nic_base + 8 + 1);
67
68         outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
69              eth_nic_base + D8390_P0_COMMAND);
70 }
71
72 static void eth_pio_write(unsigned char *src, unsigned int dst, unsigned int cnt,
73                                 unsigned int eth_nic_base)
74 {
75         outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
76         outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
77         outb(cnt, eth_nic_base + D8390_P0_RBCR0);
78         outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
79         outb(dst, eth_nic_base + D8390_P0_RSAR0);
80         outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
81         outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
82
83         while (cnt--) {
84                         outb(*(src++), eth_nic_base + NE_ASIC_OFFSET + NE_DATA);
85         }
86         /*
87         #warning "Add timeout"
88         */
89         /* wait for operation finish */
90         while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) != D8390_ISR_RDC)
91                 ;
92 }
93
94 void ne2k_append_data(unsigned char *d, int len, unsigned int base)
95 {
96         eth_pio_write(d, (TX_START << 8) + 42 + get_count(base), len, base);
97         set_count(base, get_count(base)+len);
98 }
99
100 #ifdef __ROMCC__
101
102 void eth_pio_write_byte(int data, unsigned short dst, unsigned int eth_nic_base)
103 {
104         outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
105         outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
106         outb(1, eth_nic_base + D8390_P0_RBCR0);
107         outb(0, eth_nic_base + D8390_P0_RBCR1);
108         outb(dst, eth_nic_base + D8390_P0_RSAR0);
109         outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
110         outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
111         outb(data, eth_nic_base + NE_ASIC_OFFSET + NE_DATA);
112
113         while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) != D8390_ISR_RDC)
114                 ;
115 }
116
117 void ne2k_append_data_byte(int d, unsigned int base)
118 {
119         eth_pio_write_byte(d, (TX_START << 8) + 42 + get_count(base), base);
120         set_count(base, get_count(base)+1);
121 }
122
123 static unsigned char eth_pio_read_byte(unsigned int src,
124                                 unsigned int eth_nic_base)
125 {
126         outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
127         outb(0, eth_nic_base + D8390_P0_RBCR0);
128         outb(1, eth_nic_base + D8390_P0_RBCR1);
129         outb(src, eth_nic_base + D8390_P0_RSAR0);
130         outb(src >> 8, eth_nic_base + D8390_P0_RSAR1);
131         outb(D8390_COMMAND_RD0 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
132         return inb(eth_nic_base + NE_ASIC_OFFSET + NE_DATA);
133 }
134
135
136 /* varition of compute_ip_checksum which works on SRAM */
137 unsigned long compute_ip_checksum_from_sram(unsigned short offset, unsigned short length,
138                                         unsigned int eth_nic_base)
139 {
140         unsigned long sum;
141         unsigned long i;
142         /* In the most straight forward way possible,
143          * compute an ip style checksum.
144          */
145         sum = 0;
146         for(i = 0; i < length; i++) {
147                 unsigned long v;
148                 v = eth_pio_read_byte((TX_START << 8)+i+offset, eth_nic_base);
149                 if (i & 1) {
150                         v <<= 8;
151                 }
152                 /* Add the new value */
153                 sum += v;
154                 /* Wrap around the carry */
155                 if (sum > 0xFFFF) {
156                         sum = (sum + (sum >> 16)) & 0xFFFF;
157                 }
158         }
159         return   (~((sum & 0xff) | (((sum >> 8) & 0xff) << 8) )) & 0xffff;
160 }
161
162
163 static void str2ip_load(const char *str, unsigned short offset, unsigned int eth_nic_base)
164 #else
165 static void str2ip(const char *str, unsigned char *ip)
166 #endif
167 {
168         unsigned char c, i = 0;
169         int acc = 0;
170
171         do {
172                 c = str[i];
173                 if ((c >= '0') && (c <= '9')) {
174                         acc *= 10;
175                         acc += (c - '0');
176                 } else {
177 #ifdef __ROMCC__
178                         eth_pio_write_byte(acc, (TX_START << 8)+offset, eth_nic_base);
179                         offset++;
180 #else
181                         *ip++ = acc;
182 #endif
183                         acc = 0;
184                 }
185                 i++;
186         } while (c != '\0');
187 }
188
189 #ifdef __ROMCC__
190 static void str2mac_load(const char *str, unsigned short offset, unsigned int eth_nic_base)
191 #else
192 static void str2mac(const char *str, unsigned char *mac)
193 #endif
194 {
195         unsigned char c, i = 0;
196         int acc = 0;
197
198         do {
199
200                 c = str[i];
201                 if ((c >= '0') && (c <= '9')) {
202                         acc *= 16;
203                         acc += (c - '0');
204                 } else if ((c >= 'a') && (c <= 'f')) {
205                         acc *= 16;
206                         acc += ((c - 'a') + 10) ;
207                 } else if ((c >= 'A') && (c <= 'F')) {
208                         acc *= 16;
209                         acc += ((c - 'A') + 10) ;
210                 } else {
211 #ifdef __ROMCC__
212                         eth_pio_write_byte(acc, ((TX_START << 8)+offset), eth_nic_base);
213                         offset++;
214 #else
215                         *mac++ = acc;
216 #endif
217                         acc = 0;
218                 }
219
220                 i++;
221         } while (c != '\0');
222 }
223
224
225 #ifndef __ROMCC__
226 static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen) {
227         unsigned short chksum;
228         unsigned char hdr[] = {
229 #else
230 static const unsigned char hdr[] = {
231 #endif
232                 /*
233                  * ETHERNET HDR
234                  */
235
236                 // destination macaddr
237                 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
238                 /* source mac */
239                 0x02, 0x00, 0x00, 0xC0, 0xFF, 0xEE,
240                 /* ethtype (IP) */
241                 0x08, 0x00,
242                 /*
243                  * IP HDR
244                  */
245                 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
246                 /* TTL, proto (UDP), chksum_hi, chksum_lo, IP0, IP1, IP2, IP3, */
247                 0x40, 0x11, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1,
248                 /* IP0, IP1, IP2, IP3  */
249                 0xff, 0xff, 0xff, 0xff,
250                 /*
251                  * UDP HDR
252                  */
253                 /*  SRC PORT DST PORT  (2bytes each), ulen, uchksum (must be zero or correct */
254                 0x1a, 0x0b, 0x1a, 0x0a, 0x00, 0x9, 0x00, 0x00,
255         };
256
257 #ifndef __ROMCC__
258         str2mac(CONFIG_CONSOLE_NE2K_DST_MAC,  &hdr[0]);
259         str2ip(CONFIG_CONSOLE_NE2K_DST_IP, &hdr[30]);
260         str2ip(CONFIG_CONSOLE_NE2K_SRC_IP, &hdr[26]);
261
262         /* zero checksum */
263         hdr[24] = 0;
264         hdr[25] = 0;
265
266         /* update IP packet len */
267         hdr[16] = ((28 + pktlen) >> 8) & 0xff;
268         hdr[17] = (28 + pktlen) & 0xff;
269
270         /* update UDP len */
271         hdr[38] = (8 + pktlen) >> 8;
272         hdr[39] = 8 + pktlen;
273
274         chksum = compute_ip_checksum(&hdr[14], 20);
275
276         hdr[25] = chksum >> 8;
277         hdr[24] = chksum;
278         eth_pio_write(hdr, (TX_START << 8), sizeof(hdr), eth_nic_base);
279 }
280
281
282 #else
283
284 /* ROMCC madness */
285 static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen)
286 {
287         unsigned short chksum;
288
289         eth_pio_write(hdr, (TX_START << 8), sizeof(hdr), eth_nic_base);
290
291         str2mac_load(CONFIG_CONSOLE_NE2K_DST_MAC, 0, eth_nic_base);
292
293         str2ip_load(CONFIG_CONSOLE_NE2K_DST_IP, 30, eth_nic_base);
294         str2ip_load(CONFIG_CONSOLE_NE2K_SRC_IP, 26, eth_nic_base);
295         /* zero checksum */
296         eth_pio_write_byte(0, (TX_START << 8)+24, eth_nic_base);
297         eth_pio_write_byte(0, (TX_START << 8)+25, eth_nic_base);
298
299         /* update IP packet len */
300         eth_pio_write_byte(((28 + pktlen) >> 8) & 0xff, (TX_START << 8)+16, eth_nic_base);
301         eth_pio_write_byte( (28 + pktlen) & 0xff, (TX_START << 8)+17, eth_nic_base);
302
303         /* update UDP len */
304         eth_pio_write_byte((8 + pktlen) >> 8, (TX_START << 8)+38, eth_nic_base);
305         eth_pio_write_byte( 8 + pktlen, (TX_START << 8)+39, eth_nic_base);
306
307         chksum = compute_ip_checksum_from_sram(14, 20, eth_nic_base);
308
309         eth_pio_write_byte(chksum, (TX_START << 8)+24, eth_nic_base);
310         eth_pio_write_byte(chksum >> 8, (TX_START << 8)+25, eth_nic_base);
311 }
312
313 #endif
314
315 void ne2k_transmit(unsigned int eth_nic_base) {
316         unsigned int pktsize;
317         unsigned int len = get_count(eth_nic_base);
318
319         // so place whole header inside chip buffer
320         ns8390_tx_header(eth_nic_base, len);
321
322         // commit sending now
323         outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
324
325         outb(TX_START, eth_nic_base + D8390_P0_TPSR);
326
327         pktsize = 42 + len;
328         if (pktsize < 64)
329                 pktsize = 64;
330
331         outb(pktsize, eth_nic_base + D8390_P0_TBCR0);
332         outb(pktsize >> 8, eth_nic_base + D8390_P0_TBCR1);
333
334         outb(D8390_ISR_PTX, eth_nic_base + D8390_P0_ISR);
335
336         outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
337
338         /* wait for operation finish */
339         while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_PTX) != D8390_ISR_PTX) ;
340
341         set_count(eth_nic_base, 0);
342 }
343
344 #ifdef __PRE_RAM__
345
346 #include <arch/romcc_io.h>
347
348 static void ns8390_reset(unsigned int eth_nic_base)
349 {
350         int i;
351
352         outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
353              D8390_COMMAND_STP, eth_nic_base + D8390_P0_COMMAND);
354
355         outb(0x48, eth_nic_base + D8390_P0_DCR);
356         outb(0, eth_nic_base + D8390_P0_RBCR0);
357         outb(0, eth_nic_base + D8390_P0_RBCR1);
358         outb(0x20, eth_nic_base + D8390_P0_RCR);
359         outb(2, eth_nic_base + D8390_P0_TCR);
360         outb(TX_START, eth_nic_base + D8390_P0_TPSR);
361         outb(RX_START, eth_nic_base + D8390_P0_PSTART);
362         outb(MEM_SIZE, eth_nic_base + D8390_P0_PSTOP);
363         outb(MEM_SIZE - 1, eth_nic_base + D8390_P0_BOUND);
364         outb(0xFF, eth_nic_base + D8390_P0_ISR);
365         outb(0, eth_nic_base + D8390_P0_IMR);
366
367         outb(D8390_COMMAND_PS1 |
368              D8390_COMMAND_RD2 | D8390_COMMAND_STP,
369              eth_nic_base + D8390_P0_COMMAND);
370
371         for (i = 0; i < ETH_ALEN; i++)
372                 outb(0x0C, eth_nic_base + D8390_P1_PAR0 + i);
373
374         for (i = 0; i < ETH_ALEN; i++)
375                 outb(0xFF, eth_nic_base + D8390_P1_MAR0 + i);
376
377         outb(RX_START, eth_nic_base + D8390_P1_CURR);
378         outb(D8390_COMMAND_PS0 |
379              D8390_COMMAND_RD2 | D8390_COMMAND_STA,
380              eth_nic_base + D8390_P0_COMMAND);
381         outb(0xFF, eth_nic_base + D8390_P0_ISR);
382         outb(0, eth_nic_base + D8390_P0_TCR);
383         outb(4, eth_nic_base + D8390_P0_RCR);
384         set_count(eth_nic_base, 0);
385 }
386
387
388 int ne2k_init(unsigned int eth_nic_base) {
389
390         device_t dev;
391         unsigned char c;
392
393         /* Power management controller */
394         dev = pci_locate_device(PCI_ID(0x10ec,
395                                        0x8029), 0);
396
397         if (dev == PCI_DEV_INVALID)
398                 return 0;
399
400         pci_write_config32(dev, 0x10, eth_nic_base | 1 );
401         pci_write_config8(dev, 0x4, 0x1);
402
403         c = inb(eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
404         outb(c, eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
405
406         (void) inb(0x84);
407
408         outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
409         outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
410
411         outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
412         outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
413         outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
414
415         ns8390_reset(eth_nic_base);
416         return 1;
417 }
418
419 #else
420
421 #include <delay.h>
422 #include <stdlib.h>
423 #include <string.h>
424 #include <arch/io.h>
425
426 #include <console/console.h>
427 #include <device/device.h>
428 #include <device/pci.h>
429 #include <device/pci_ids.h>
430 #include <device/pci_ops.h>
431
432 static void read_resources(struct device *dev)
433 {
434         struct resource *res;
435
436         res = new_resource(dev, PCI_BASE_ADDRESS_0);
437         res->base = CONFIG_CONSOLE_NE2K_IO_PORT;
438         res->size = 32;
439         res->align = 5;
440         res->gran = 5;
441         res->limit = res->base + res->size - 1;
442         res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_STORED |
443                                 IORESOURCE_ASSIGNED;
444         return;
445 }
446
447 static struct device_operations ne2k_ops  = {
448         .read_resources   = read_resources,
449         .set_resources    = pci_dev_set_resources,
450         .enable_resources = pci_dev_enable_resources,
451         .init             = 0,
452         .scan_bus         = 0,
453 };
454
455 static const struct pci_driver ne2k_driver __pci_driver = {
456         .ops    = &ne2k_ops,
457         .vendor = 0x10ec,
458         .device = 0x8029,
459 };
460
461 #endif