blob: 180ac2114bf163afc2ad24614605dd11fa9f444a [file] [log] [blame]
Rudolf Marek4aa93cc2010-07-16 20:02:09 +00001/*
2ETHERBOOT - BOOTP/TFTP Bootstrap Program
3
4Author: 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
16Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
17Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
183c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
19SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
203c503 PIO support added by Jim Hague (jim.hague@acm.org) on 2/17/98
21RX 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)
23SMC8416 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
47static 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
60static 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
72static 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
94void 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
102void 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
117void 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
123static 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 */
137unsigned 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
163static void str2ip_load(const char *str, unsigned short offset, unsigned int eth_nic_base)
164#else
165static 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__
190static void str2mac_load(const char *str, unsigned short offset, unsigned int eth_nic_base)
191#else
192static 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__
226static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen) {
227 unsigned short chksum;
228 unsigned char hdr[] = {
229#else
230static 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 */
285static 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
315void 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
348static 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
388int 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
432static 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
Stefan Reinauer261f8422011-04-18 02:26:56 +0000447static struct device_operations ne2k_ops = {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000448 .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
Stefan Reinauer261f8422011-04-18 02:26:56 +0000455static const struct pci_driver ne2k_driver __pci_driver = {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000456 .ops = &si_sata_ops,
457 .vendor = 0x10ec,
458 .device = 0x8029,
459};
460
461#endif