blob: 96f4d703a4e6825d0490d3ac0b67f00d950009bd [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>
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +030038#include <rules.h>
Edward O'Callaghan20141112014-10-31 12:37:49 +110039#include <stdlib.h>
40#include <string.h>
41#include <ip_checksum.h>
42
43#include "ns8390.h"
44
45
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000046#define ETH_ALEN 6 /* Size of Ethernet address */
47#define ETH_HLEN 14 /* Size of ethernet header */
48#define ETH_ZLEN 60 /* Minimum packet */
49#define ETH_FRAME_LEN 1514 /* Maximum packet */
50#define ETH_DATA_ALIGN 2 /* Amount needed to align the data after an ethernet header */
51#define ETH_MAX_MTU (ETH_FRAME_LEN-ETH_HLEN)
52
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000053#define MEM_SIZE MEM_32768
54#define TX_START 64
55#define RX_START (64 + D8390_TXBUF_SIZE)
56
Edward O'Callaghan20141112014-10-31 12:37:49 +110057
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000058static unsigned int get_count(unsigned int eth_nic_base)
59{
60 unsigned int ret;
61 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
62 eth_nic_base + D8390_P0_COMMAND);
63
64 ret = inb(eth_nic_base + 8 + 0) | (inb(eth_nic_base + 8 + 1) << 8);
65
66 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
67 eth_nic_base + D8390_P0_COMMAND);
68 return ret;
69}
70
71static void set_count(unsigned int eth_nic_base, unsigned int what)
72{
73 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
74 eth_nic_base + D8390_P0_COMMAND);
75
76 outb(what & 0xff,eth_nic_base + 8);
77 outb((what >> 8) & 0xff,eth_nic_base + 8 + 1);
78
79 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
80 eth_nic_base + D8390_P0_COMMAND);
81}
82
83static void eth_pio_write(unsigned char *src, unsigned int dst, unsigned int cnt,
84 unsigned int eth_nic_base)
85{
86 outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
87 outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
88 outb(cnt, eth_nic_base + D8390_P0_RBCR0);
89 outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
90 outb(dst, eth_nic_base + D8390_P0_RSAR0);
91 outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
92 outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
93
94 while (cnt--) {
95 outb(*(src++), eth_nic_base + NE_ASIC_OFFSET + NE_DATA);
96 }
97 /*
98 #warning "Add timeout"
99 */
100 /* wait for operation finish */
101 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) != D8390_ISR_RDC)
102 ;
103}
104
105void ne2k_append_data(unsigned char *d, int len, unsigned int base)
106{
107 eth_pio_write(d, (TX_START << 8) + 42 + get_count(base), len, base);
108 set_count(base, get_count(base)+len);
109}
110
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000111static void str2ip(const char *str, unsigned char *ip)
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000112{
113 unsigned char c, i = 0;
114 int acc = 0;
115
116 do {
117 c = str[i];
118 if ((c >= '0') && (c <= '9')) {
119 acc *= 10;
120 acc += (c - '0');
121 } else {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000122 *ip++ = acc;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000123 acc = 0;
124 }
125 i++;
126 } while (c != '\0');
127}
128
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000129static void str2mac(const char *str, unsigned char *mac)
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000130{
131 unsigned char c, i = 0;
132 int acc = 0;
133
134 do {
135
136 c = str[i];
137 if ((c >= '0') && (c <= '9')) {
138 acc *= 16;
139 acc += (c - '0');
140 } else if ((c >= 'a') && (c <= 'f')) {
141 acc *= 16;
142 acc += ((c - 'a') + 10) ;
143 } else if ((c >= 'A') && (c <= 'F')) {
144 acc *= 16;
145 acc += ((c - 'A') + 10) ;
146 } else {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000147 *mac++ = acc;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000148 acc = 0;
149 }
150
151 i++;
152 } while (c != '\0');
153}
154
155
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300156static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen)
157{
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000158 unsigned short chksum;
159 unsigned char hdr[] = {
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300160 /* ETHERNET HDR */
161 /* destination macaddr */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000162 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
163 /* source mac */
164 0x02, 0x00, 0x00, 0xC0, 0xFF, 0xEE,
165 /* ethtype (IP) */
166 0x08, 0x00,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300167
168 /* IP HDR */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000169 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
170 /* TTL, proto (UDP), chksum_hi, chksum_lo, IP0, IP1, IP2, IP3, */
171 0x40, 0x11, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1,
172 /* IP0, IP1, IP2, IP3 */
173 0xff, 0xff, 0xff, 0xff,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300174
175 /* UDP HDR */
176 /* SRC PORT DST PORT (2 bytes each),
177 * ulen, uchksum (must be zero or correct */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000178 0x1a, 0x0b, 0x1a, 0x0a, 0x00, 0x9, 0x00, 0x00,
179 };
180
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000181 str2mac(CONFIG_CONSOLE_NE2K_DST_MAC, &hdr[0]);
182 str2ip(CONFIG_CONSOLE_NE2K_DST_IP, &hdr[30]);
183 str2ip(CONFIG_CONSOLE_NE2K_SRC_IP, &hdr[26]);
184
185 /* zero checksum */
186 hdr[24] = 0;
187 hdr[25] = 0;
188
189 /* update IP packet len */
190 hdr[16] = ((28 + pktlen) >> 8) & 0xff;
191 hdr[17] = (28 + pktlen) & 0xff;
192
193 /* update UDP len */
194 hdr[38] = (8 + pktlen) >> 8;
195 hdr[39] = 8 + pktlen;
196
197 chksum = compute_ip_checksum(&hdr[14], 20);
198
199 hdr[25] = chksum >> 8;
200 hdr[24] = chksum;
201 eth_pio_write(hdr, (TX_START << 8), sizeof(hdr), eth_nic_base);
202}
203
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000204void ne2k_transmit(unsigned int eth_nic_base) {
205 unsigned int pktsize;
206 unsigned int len = get_count(eth_nic_base);
207
208 // so place whole header inside chip buffer
209 ns8390_tx_header(eth_nic_base, len);
210
211 // commit sending now
212 outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
213
214 outb(TX_START, eth_nic_base + D8390_P0_TPSR);
215
216 pktsize = 42 + len;
217 if (pktsize < 64)
218 pktsize = 64;
219
220 outb(pktsize, eth_nic_base + D8390_P0_TBCR0);
221 outb(pktsize >> 8, eth_nic_base + D8390_P0_TBCR1);
222
223 outb(D8390_ISR_PTX, eth_nic_base + D8390_P0_ISR);
224
225 outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
226
227 /* wait for operation finish */
228 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_PTX) != D8390_ISR_PTX) ;
229
230 set_count(eth_nic_base, 0);
231}
232
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300233#if !ENV_RAMSTAGE
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000234
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000235static void ns8390_reset(unsigned int eth_nic_base)
236{
237 int i;
238
239 outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
240 D8390_COMMAND_STP, eth_nic_base + D8390_P0_COMMAND);
241
242 outb(0x48, eth_nic_base + D8390_P0_DCR);
243 outb(0, eth_nic_base + D8390_P0_RBCR0);
244 outb(0, eth_nic_base + D8390_P0_RBCR1);
245 outb(0x20, eth_nic_base + D8390_P0_RCR);
246 outb(2, eth_nic_base + D8390_P0_TCR);
247 outb(TX_START, eth_nic_base + D8390_P0_TPSR);
248 outb(RX_START, eth_nic_base + D8390_P0_PSTART);
249 outb(MEM_SIZE, eth_nic_base + D8390_P0_PSTOP);
250 outb(MEM_SIZE - 1, eth_nic_base + D8390_P0_BOUND);
251 outb(0xFF, eth_nic_base + D8390_P0_ISR);
252 outb(0, eth_nic_base + D8390_P0_IMR);
253
254 outb(D8390_COMMAND_PS1 |
255 D8390_COMMAND_RD2 | D8390_COMMAND_STP,
256 eth_nic_base + D8390_P0_COMMAND);
257
258 for (i = 0; i < ETH_ALEN; i++)
259 outb(0x0C, eth_nic_base + D8390_P1_PAR0 + i);
260
261 for (i = 0; i < ETH_ALEN; i++)
262 outb(0xFF, eth_nic_base + D8390_P1_MAR0 + i);
263
264 outb(RX_START, eth_nic_base + D8390_P1_CURR);
265 outb(D8390_COMMAND_PS0 |
266 D8390_COMMAND_RD2 | D8390_COMMAND_STA,
267 eth_nic_base + D8390_P0_COMMAND);
268 outb(0xFF, eth_nic_base + D8390_P0_ISR);
269 outb(0, eth_nic_base + D8390_P0_TCR);
270 outb(4, eth_nic_base + D8390_P0_RCR);
271 set_count(eth_nic_base, 0);
272}
273
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000274int ne2k_init(unsigned int eth_nic_base) {
275
276 device_t dev;
277 unsigned char c;
278
279 /* Power management controller */
280 dev = pci_locate_device(PCI_ID(0x10ec,
281 0x8029), 0);
282
283 if (dev == PCI_DEV_INVALID)
284 return 0;
285
286 pci_write_config32(dev, 0x10, eth_nic_base | 1 );
287 pci_write_config8(dev, 0x4, 0x1);
288
289 c = inb(eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
290 outb(c, eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
291
292 (void) inb(0x84);
293
294 outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
295 outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
296
297 outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
298 outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
299 outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
300
301 ns8390_reset(eth_nic_base);
302 return 1;
303}
304
305#else
Edward O'Callaghan20a41ae2014-11-22 18:48:55 +1100306int ne2k_init(unsigned int eth_nic_base) { return 0; } // dummy symbol for ramstage
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000307
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000308static void read_resources(struct device *dev)
309{
310 struct resource *res;
311
312 res = new_resource(dev, PCI_BASE_ADDRESS_0);
313 res->base = CONFIG_CONSOLE_NE2K_IO_PORT;
314 res->size = 32;
315 res->align = 5;
316 res->gran = 5;
317 res->limit = res->base + res->size - 1;
318 res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_STORED |
Elyes HAOUAS0d8f1da2018-05-28 15:48:04 +0200319 IORESOURCE_ASSIGNED;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000320 return;
321}
322
Stefan Reinauer261f8422011-04-18 02:26:56 +0000323static struct device_operations ne2k_ops = {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000324 .read_resources = read_resources,
325 .set_resources = pci_dev_set_resources,
326 .enable_resources = pci_dev_enable_resources,
327 .init = 0,
328 .scan_bus = 0,
329};
330
Stefan Reinauer261f8422011-04-18 02:26:56 +0000331static const struct pci_driver ne2k_driver __pci_driver = {
Martin Rothb9810a42017-07-23 20:00:04 -0600332 .ops = &ne2k_ops,
333 .vendor = 0x10ec,
334 .device = 0x8029,
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000335};
336
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300337#endif /* !ENV_RAMSTAGE */