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