blob: 75f3357c1cdd36deab26357f255d5f9421ac268f [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
Elyes HAOUASc8a649c2018-06-10 23:36:44 +0200276#ifdef __SIMPLE_DEVICE__
277 pci_devfn_t dev;
278#else
279 struct device *dev;
280#endif
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000281 unsigned char c;
282
283 /* Power management controller */
284 dev = pci_locate_device(PCI_ID(0x10ec,
285 0x8029), 0);
286
287 if (dev == PCI_DEV_INVALID)
288 return 0;
289
290 pci_write_config32(dev, 0x10, eth_nic_base | 1 );
291 pci_write_config8(dev, 0x4, 0x1);
292
293 c = inb(eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
294 outb(c, eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
295
296 (void) inb(0x84);
297
298 outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
299 outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
300
301 outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
302 outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
303 outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
304
305 ns8390_reset(eth_nic_base);
306 return 1;
307}
308
309#else
Edward O'Callaghan20a41ae2014-11-22 18:48:55 +1100310int ne2k_init(unsigned int eth_nic_base) { return 0; } // dummy symbol for ramstage
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000311
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000312static void read_resources(struct device *dev)
313{
314 struct resource *res;
315
316 res = new_resource(dev, PCI_BASE_ADDRESS_0);
317 res->base = CONFIG_CONSOLE_NE2K_IO_PORT;
318 res->size = 32;
319 res->align = 5;
320 res->gran = 5;
321 res->limit = res->base + res->size - 1;
322 res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_STORED |
Elyes HAOUAS0d8f1da2018-05-28 15:48:04 +0200323 IORESOURCE_ASSIGNED;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000324 return;
325}
326
Stefan Reinauer261f8422011-04-18 02:26:56 +0000327static struct device_operations ne2k_ops = {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000328 .read_resources = read_resources,
329 .set_resources = pci_dev_set_resources,
330 .enable_resources = pci_dev_enable_resources,
331 .init = 0,
332 .scan_bus = 0,
333};
334
Stefan Reinauer261f8422011-04-18 02:26:56 +0000335static const struct pci_driver ne2k_driver __pci_driver = {
Martin Rothb9810a42017-07-23 20:00:04 -0600336 .ops = &ne2k_ops,
337 .vendor = 0x10ec,
338 .device = 0x8029,
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000339};
340
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300341#endif /* !ENV_RAMSTAGE */