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 ([email protected]) 09/28/2003
17 Relocation support added by Ken Yap ([email protected]) 28/12/02
18 3c503 support added by Bill Paul ([email protected]) on 11/15/94
19 SMC8416 support added by Bill Paul ([email protected]) on 12/25/94
20 3c503 PIO support added by Jim Hague ([email protected]) on 2/17/98
21 RX overrun by Klaus Espenlaub ([email protected]) 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 ([email protected]) on 4/3/02
24 based on the Linux 8390 driver (by Donald Becker and Paul Gortmaker)
25
26 (C) Rudolf Marek <[email protected]> Simplify for RTL8029, Add coreboot glue logic
27
28 */
29
30 #include <arch/io.h>
31 #include <commonlib/bsd/ipchksum.h>
32 #include <console/ne2k.h>
33 #include <device/device.h>
34 #include <device/pci.h>
35 #include <device/pci_ops.h>
36
37 #include "ns8390.h"
38
39 #define ETH_ALEN 6 /* Size of Ethernet address */
40 #define ETH_HLEN 14 /* Size of ethernet header */
41 #define ETH_ZLEN 60 /* Minimum packet */
42 #define ETH_FRAME_LEN 1514 /* Maximum packet */
43 #define ETH_DATA_ALIGN 2 /* Amount needed to align the data after an ethernet header */
44 #define ETH_MAX_MTU (ETH_FRAME_LEN-ETH_HLEN)
45
46 #define MEM_SIZE MEM_32768
47 #define TX_START 64
48 #define RX_START (64 + D8390_TXBUF_SIZE)
49
get_count(unsigned int eth_nic_base)50 static unsigned int get_count(unsigned int eth_nic_base)
51 {
52 unsigned int ret;
53 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
54 eth_nic_base + D8390_P0_COMMAND);
55
56 ret = inb(eth_nic_base + 8 + 0) | (inb(eth_nic_base + 8 + 1) << 8);
57
58 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
59 eth_nic_base + D8390_P0_COMMAND);
60 return ret;
61 }
62
set_count(unsigned int eth_nic_base,unsigned int what)63 static void set_count(unsigned int eth_nic_base, unsigned int what)
64 {
65 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
66 eth_nic_base + D8390_P0_COMMAND);
67
68 outb(what & 0xff,eth_nic_base + 8);
69 outb((what >> 8) & 0xff,eth_nic_base + 8 + 1);
70
71 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
72 eth_nic_base + D8390_P0_COMMAND);
73 }
74
eth_pio_write(unsigned char * src,unsigned int dst,unsigned int cnt,unsigned int eth_nic_base)75 static void eth_pio_write(unsigned char *src, unsigned int dst, unsigned int cnt,
76 unsigned int eth_nic_base)
77 {
78 outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
79 outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
80 outb(cnt, eth_nic_base + D8390_P0_RBCR0);
81 outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
82 outb(dst, eth_nic_base + D8390_P0_RSAR0);
83 outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
84 outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
85
86 while (cnt--) {
87 outb(*(src++), eth_nic_base + NE_ASIC_OFFSET + NE_DATA);
88 }
89 /*
90 #warning "Add timeout"
91 */
92 /* wait for operation finish */
93 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) != D8390_ISR_RDC)
94 ;
95 }
96
ne2k_append_data(unsigned char * d,int len,unsigned int base)97 void ne2k_append_data(unsigned char *d, int len, unsigned int base)
98 {
99 eth_pio_write(d, (TX_START << 8) + 42 + get_count(base), len, base);
100 set_count(base, get_count(base)+len);
101 }
102
str2ip(const char * str,unsigned char * ip)103 static void str2ip(const char *str, unsigned char *ip)
104 {
105 unsigned char c, i = 0;
106 int acc = 0;
107
108 do {
109 c = str[i];
110 if ((c >= '0') && (c <= '9')) {
111 acc *= 10;
112 acc += (c - '0');
113 } else {
114 *ip++ = acc;
115 acc = 0;
116 }
117 i++;
118 } while (c != '\0');
119 }
120
str2mac(const char * str,unsigned char * mac)121 static void str2mac(const char *str, unsigned char *mac)
122 {
123 unsigned char c, i = 0;
124 int acc = 0;
125
126 do {
127 c = str[i];
128 if ((c >= '0') && (c <= '9')) {
129 acc *= 16;
130 acc += (c - '0');
131 } else if ((c >= 'a') && (c <= 'f')) {
132 acc *= 16;
133 acc += ((c - 'a') + 10);
134 } else if ((c >= 'A') && (c <= 'F')) {
135 acc *= 16;
136 acc += ((c - 'A') + 10);
137 } else {
138 *mac++ = acc;
139 acc = 0;
140 }
141
142 i++;
143 } while (c != '\0');
144 }
145
ns8390_tx_header(unsigned int eth_nic_base,int pktlen)146 static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen)
147 {
148 unsigned short chksum;
149 unsigned char hdr[] = {
150 /* ETHERNET HDR */
151 /* destination macaddr */
152 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
153 /* source mac */
154 0x02, 0x00, 0x00, 0xC0, 0xFF, 0xEE,
155 /* ethtype (IP) */
156 0x08, 0x00,
157
158 /* IP HDR */
159 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
160 /* TTL, proto (UDP), chksum_hi, chksum_lo, IP0, IP1, IP2, IP3, */
161 0x40, 0x11, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1,
162 /* IP0, IP1, IP2, IP3 */
163 0xff, 0xff, 0xff, 0xff,
164
165 /* UDP HDR */
166 /* SRC PORT DST PORT (2 bytes each),
167 * ulen, uchksum (must be zero or correct */
168 0x1a, 0x0b, 0x1a, 0x0a, 0x00, 0x9, 0x00, 0x00,
169 };
170
171 str2mac(CONFIG_CONSOLE_NE2K_DST_MAC, &hdr[0]);
172 str2ip(CONFIG_CONSOLE_NE2K_DST_IP, &hdr[30]);
173 str2ip(CONFIG_CONSOLE_NE2K_SRC_IP, &hdr[26]);
174
175 /* zero checksum */
176 hdr[24] = 0;
177 hdr[25] = 0;
178
179 /* update IP packet len */
180 hdr[16] = ((28 + pktlen) >> 8) & 0xff;
181 hdr[17] = (28 + pktlen) & 0xff;
182
183 /* update UDP len */
184 hdr[38] = (8 + pktlen) >> 8;
185 hdr[39] = 8 + pktlen;
186
187 chksum = ipchksum(&hdr[14], 20);
188
189 hdr[25] = chksum >> 8;
190 hdr[24] = chksum;
191 eth_pio_write(hdr, (TX_START << 8), sizeof(hdr), eth_nic_base);
192 }
193
ne2k_transmit(unsigned int eth_nic_base)194 void ne2k_transmit(unsigned int eth_nic_base)
195 {
196 unsigned int pktsize;
197 unsigned int len = get_count(eth_nic_base);
198
199 // so place whole header inside chip buffer
200 ns8390_tx_header(eth_nic_base, len);
201
202 // commit sending now
203 outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
204
205 outb(TX_START, eth_nic_base + D8390_P0_TPSR);
206
207 pktsize = 42 + len;
208 if (pktsize < 64)
209 pktsize = 64;
210
211 outb(pktsize, eth_nic_base + D8390_P0_TBCR0);
212 outb(pktsize >> 8, eth_nic_base + D8390_P0_TBCR1);
213
214 outb(D8390_ISR_PTX, eth_nic_base + D8390_P0_ISR);
215
216 outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
217
218 /* wait for operation finish */
219 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_PTX) != D8390_ISR_PTX)
220 ;
221
222 set_count(eth_nic_base, 0);
223 }
224
ns8390_reset(unsigned int eth_nic_base)225 static void ns8390_reset(unsigned int eth_nic_base)
226 {
227 int i;
228
229 outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
230 D8390_COMMAND_STP, eth_nic_base + D8390_P0_COMMAND);
231
232 outb(0x48, eth_nic_base + D8390_P0_DCR);
233 outb(0, eth_nic_base + D8390_P0_RBCR0);
234 outb(0, eth_nic_base + D8390_P0_RBCR1);
235 outb(0x20, eth_nic_base + D8390_P0_RCR);
236 outb(2, eth_nic_base + D8390_P0_TCR);
237 outb(TX_START, eth_nic_base + D8390_P0_TPSR);
238 outb(RX_START, eth_nic_base + D8390_P0_PSTART);
239 outb(MEM_SIZE, eth_nic_base + D8390_P0_PSTOP);
240 outb(MEM_SIZE - 1, eth_nic_base + D8390_P0_BOUND);
241 outb(0xFF, eth_nic_base + D8390_P0_ISR);
242 outb(0, eth_nic_base + D8390_P0_IMR);
243
244 outb(D8390_COMMAND_PS1 |
245 D8390_COMMAND_RD2 | D8390_COMMAND_STP,
246 eth_nic_base + D8390_P0_COMMAND);
247
248 for (i = 0; i < ETH_ALEN; i++)
249 outb(0x0C, eth_nic_base + D8390_P1_PAR0 + i);
250
251 for (i = 0; i < ETH_ALEN; i++)
252 outb(0xFF, eth_nic_base + D8390_P1_MAR0 + i);
253
254 outb(RX_START, eth_nic_base + D8390_P1_CURR);
255 outb(D8390_COMMAND_PS0 |
256 D8390_COMMAND_RD2 | D8390_COMMAND_STA,
257 eth_nic_base + D8390_P0_COMMAND);
258 outb(0xFF, eth_nic_base + D8390_P0_ISR);
259 outb(0, eth_nic_base + D8390_P0_TCR);
260 outb(4, eth_nic_base + D8390_P0_RCR);
261 set_count(eth_nic_base, 0);
262 }
263
ne2k_init(unsigned int eth_nic_base)264 int ne2k_init(unsigned int eth_nic_base)
265 {
266 pci_devfn_t dev;
267 unsigned char c;
268
269 if (!ENV_ROMSTAGE_OR_BEFORE)
270 return 0;
271
272 /* For this to work, mainboard code must have configured
273 PCI bridges prior to calling console_init(). */
274 dev = pci_locate_device(PCI_ID(0x10ec, 0x8029), 0);
275 if (dev == PCI_DEV_INVALID)
276 return 0;
277
278 pci_s_write_config32(dev, 0x10, eth_nic_base | 1);
279 pci_s_write_config8(dev, 0x4, 0x1);
280
281 c = inb(eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
282 outb(c, eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
283
284 (void)inb(0x84);
285
286 outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
287 outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
288
289 outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
290 outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
291 outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
292
293 ns8390_reset(eth_nic_base);
294 return 1;
295 }
296
read_resources(struct device * dev)297 static void read_resources(struct device *dev)
298 {
299 struct resource *res;
300
301 res = new_resource(dev, PCI_BASE_ADDRESS_0);
302 res->base = CONFIG_CONSOLE_NE2K_IO_PORT;
303 res->size = 32;
304 res->align = 5;
305 res->gran = 5;
306 res->limit = res->base + res->size - 1;
307 res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_STORED |
308 IORESOURCE_ASSIGNED;
309 }
310
311 static struct device_operations ne2k_ops = {
312 .read_resources = read_resources,
313 .set_resources = pci_dev_set_resources,
314 .enable_resources = pci_dev_enable_resources,
315 };
316
317 static const struct pci_driver ne2k_driver __pci_driver = {
318 .ops = &ne2k_ops,
319 .vendor = 0x10ec,
320 .device = 0x8029,
321 };
322