blob: c7b53e4e7b790744b9f5b69ed3a3466f992047bb [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>
Edward O'Callaghan20141112014-10-31 12:37:49 +110031#include <console/ne2k.h>
Edward O'Callaghan20141112014-10-31 12:37:49 +110032#include <device/device.h>
33#include <device/pci.h>
34#include <device/pci_ids.h>
35#include <device/pci_ops.h>
36#include <stdlib.h>
Edward O'Callaghan20141112014-10-31 12:37:49 +110037#include <ip_checksum.h>
38
39#include "ns8390.h"
40
41
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000042#define ETH_ALEN 6 /* Size of Ethernet address */
43#define ETH_HLEN 14 /* Size of ethernet header */
44#define ETH_ZLEN 60 /* Minimum packet */
45#define ETH_FRAME_LEN 1514 /* Maximum packet */
46#define ETH_DATA_ALIGN 2 /* Amount needed to align the data after an ethernet header */
47#define ETH_MAX_MTU (ETH_FRAME_LEN-ETH_HLEN)
48
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000049#define MEM_SIZE MEM_32768
50#define TX_START 64
51#define RX_START (64 + D8390_TXBUF_SIZE)
52
Edward O'Callaghan20141112014-10-31 12:37:49 +110053
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000054static unsigned int get_count(unsigned int eth_nic_base)
55{
56 unsigned int ret;
57 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
58 eth_nic_base + D8390_P0_COMMAND);
59
60 ret = inb(eth_nic_base + 8 + 0) | (inb(eth_nic_base + 8 + 1) << 8);
61
62 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
63 eth_nic_base + D8390_P0_COMMAND);
64 return ret;
65}
66
67static void set_count(unsigned int eth_nic_base, unsigned int what)
68{
69 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1,
70 eth_nic_base + D8390_P0_COMMAND);
71
72 outb(what & 0xff,eth_nic_base + 8);
73 outb((what >> 8) & 0xff,eth_nic_base + 8 + 1);
74
75 outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0,
76 eth_nic_base + D8390_P0_COMMAND);
77}
78
79static void eth_pio_write(unsigned char *src, unsigned int dst, unsigned int cnt,
80 unsigned int eth_nic_base)
81{
82 outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
83 outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
84 outb(cnt, eth_nic_base + D8390_P0_RBCR0);
85 outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
86 outb(dst, eth_nic_base + D8390_P0_RSAR0);
87 outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
88 outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
89
90 while (cnt--) {
91 outb(*(src++), eth_nic_base + NE_ASIC_OFFSET + NE_DATA);
92 }
93 /*
94 #warning "Add timeout"
95 */
96 /* wait for operation finish */
97 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC) != D8390_ISR_RDC)
98 ;
99}
100
101void ne2k_append_data(unsigned char *d, int len, unsigned int base)
102{
103 eth_pio_write(d, (TX_START << 8) + 42 + get_count(base), len, base);
104 set_count(base, get_count(base)+len);
105}
106
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000107static void str2ip(const char *str, unsigned char *ip)
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000108{
109 unsigned char c, i = 0;
110 int acc = 0;
111
112 do {
113 c = str[i];
114 if ((c >= '0') && (c <= '9')) {
115 acc *= 10;
116 acc += (c - '0');
117 } else {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000118 *ip++ = acc;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000119 acc = 0;
120 }
121 i++;
122 } while (c != '\0');
123}
124
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000125static void str2mac(const char *str, unsigned char *mac)
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000126{
127 unsigned char c, i = 0;
128 int acc = 0;
129
130 do {
131
132 c = str[i];
133 if ((c >= '0') && (c <= '9')) {
134 acc *= 16;
135 acc += (c - '0');
136 } else if ((c >= 'a') && (c <= 'f')) {
137 acc *= 16;
138 acc += ((c - 'a') + 10) ;
139 } else if ((c >= 'A') && (c <= 'F')) {
140 acc *= 16;
141 acc += ((c - 'A') + 10) ;
142 } else {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000143 *mac++ = acc;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000144 acc = 0;
145 }
146
147 i++;
148 } while (c != '\0');
149}
150
151
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300152static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen)
153{
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000154 unsigned short chksum;
155 unsigned char hdr[] = {
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300156 /* ETHERNET HDR */
157 /* destination macaddr */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000158 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
159 /* source mac */
160 0x02, 0x00, 0x00, 0xC0, 0xFF, 0xEE,
161 /* ethtype (IP) */
162 0x08, 0x00,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300163
164 /* IP HDR */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000165 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
166 /* TTL, proto (UDP), chksum_hi, chksum_lo, IP0, IP1, IP2, IP3, */
167 0x40, 0x11, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1,
168 /* IP0, IP1, IP2, IP3 */
169 0xff, 0xff, 0xff, 0xff,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300170
171 /* UDP HDR */
172 /* SRC PORT DST PORT (2 bytes each),
173 * ulen, uchksum (must be zero or correct */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000174 0x1a, 0x0b, 0x1a, 0x0a, 0x00, 0x9, 0x00, 0x00,
175 };
176
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000177 str2mac(CONFIG_CONSOLE_NE2K_DST_MAC, &hdr[0]);
178 str2ip(CONFIG_CONSOLE_NE2K_DST_IP, &hdr[30]);
179 str2ip(CONFIG_CONSOLE_NE2K_SRC_IP, &hdr[26]);
180
181 /* zero checksum */
182 hdr[24] = 0;
183 hdr[25] = 0;
184
185 /* update IP packet len */
186 hdr[16] = ((28 + pktlen) >> 8) & 0xff;
187 hdr[17] = (28 + pktlen) & 0xff;
188
189 /* update UDP len */
190 hdr[38] = (8 + pktlen) >> 8;
191 hdr[39] = 8 + pktlen;
192
193 chksum = compute_ip_checksum(&hdr[14], 20);
194
195 hdr[25] = chksum >> 8;
196 hdr[24] = chksum;
197 eth_pio_write(hdr, (TX_START << 8), sizeof(hdr), eth_nic_base);
198}
199
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300200void ne2k_transmit(unsigned int eth_nic_base)
201{
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000202 unsigned int pktsize;
203 unsigned int len = get_count(eth_nic_base);
204
205 // so place whole header inside chip buffer
206 ns8390_tx_header(eth_nic_base, len);
207
208 // commit sending now
209 outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
210
211 outb(TX_START, eth_nic_base + D8390_P0_TPSR);
212
213 pktsize = 42 + len;
214 if (pktsize < 64)
215 pktsize = 64;
216
217 outb(pktsize, eth_nic_base + D8390_P0_TBCR0);
218 outb(pktsize >> 8, eth_nic_base + D8390_P0_TBCR1);
219
220 outb(D8390_ISR_PTX, eth_nic_base + D8390_P0_ISR);
221
222 outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
223
224 /* wait for operation finish */
225 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_PTX) != D8390_ISR_PTX) ;
226
227 set_count(eth_nic_base, 0);
228}
229
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000230static void ns8390_reset(unsigned int eth_nic_base)
231{
232 int i;
233
234 outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
235 D8390_COMMAND_STP, eth_nic_base + D8390_P0_COMMAND);
236
237 outb(0x48, eth_nic_base + D8390_P0_DCR);
238 outb(0, eth_nic_base + D8390_P0_RBCR0);
239 outb(0, eth_nic_base + D8390_P0_RBCR1);
240 outb(0x20, eth_nic_base + D8390_P0_RCR);
241 outb(2, eth_nic_base + D8390_P0_TCR);
242 outb(TX_START, eth_nic_base + D8390_P0_TPSR);
243 outb(RX_START, eth_nic_base + D8390_P0_PSTART);
244 outb(MEM_SIZE, eth_nic_base + D8390_P0_PSTOP);
245 outb(MEM_SIZE - 1, eth_nic_base + D8390_P0_BOUND);
246 outb(0xFF, eth_nic_base + D8390_P0_ISR);
247 outb(0, eth_nic_base + D8390_P0_IMR);
248
249 outb(D8390_COMMAND_PS1 |
250 D8390_COMMAND_RD2 | D8390_COMMAND_STP,
251 eth_nic_base + D8390_P0_COMMAND);
252
253 for (i = 0; i < ETH_ALEN; i++)
254 outb(0x0C, eth_nic_base + D8390_P1_PAR0 + i);
255
256 for (i = 0; i < ETH_ALEN; i++)
257 outb(0xFF, eth_nic_base + D8390_P1_MAR0 + i);
258
259 outb(RX_START, eth_nic_base + D8390_P1_CURR);
260 outb(D8390_COMMAND_PS0 |
261 D8390_COMMAND_RD2 | D8390_COMMAND_STA,
262 eth_nic_base + D8390_P0_COMMAND);
263 outb(0xFF, eth_nic_base + D8390_P0_ISR);
264 outb(0, eth_nic_base + D8390_P0_TCR);
265 outb(4, eth_nic_base + D8390_P0_RCR);
266 set_count(eth_nic_base, 0);
267}
268
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300269int ne2k_init(unsigned int eth_nic_base)
270{
Elyes HAOUASc8a649c2018-06-10 23:36:44 +0200271 pci_devfn_t dev;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000272 unsigned char c;
273
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300274 /* FIXME: This console is not enabled for bootblock. */
275 if (!ENV_ROMSTAGE)
276 return 0;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000277
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300278 /* For this to work, mainboard code must have configured
279 PCI bridges prior to calling console_init(). */
280 dev = pci_locate_device(PCI_ID(0x10ec, 0x8029), 0);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000281 if (dev == PCI_DEV_INVALID)
282 return 0;
283
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300284 pci_s_write_config32(dev, 0x10, eth_nic_base | 1);
285 pci_s_write_config8(dev, 0x4, 0x1);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000286
287 c = inb(eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
288 outb(c, eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
289
290 (void) inb(0x84);
291
292 outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
293 outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
294
295 outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
296 outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
297 outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
298
299 ns8390_reset(eth_nic_base);
300 return 1;
301}
302
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000303static void read_resources(struct device *dev)
304{
305 struct resource *res;
306
307 res = new_resource(dev, PCI_BASE_ADDRESS_0);
308 res->base = CONFIG_CONSOLE_NE2K_IO_PORT;
309 res->size = 32;
310 res->align = 5;
311 res->gran = 5;
312 res->limit = res->base + res->size - 1;
313 res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_STORED |
Elyes HAOUAS0d8f1da2018-05-28 15:48:04 +0200314 IORESOURCE_ASSIGNED;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000315 return;
316}
317
Stefan Reinauer261f8422011-04-18 02:26:56 +0000318static struct device_operations ne2k_ops = {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000319 .read_resources = read_resources,
320 .set_resources = pci_dev_set_resources,
321 .enable_resources = pci_dev_enable_resources,
322 .init = 0,
323 .scan_bus = 0,
324};
325
Stefan Reinauer261f8422011-04-18 02:26:56 +0000326static const struct pci_driver ne2k_driver __pci_driver = {
Martin Rothb9810a42017-07-23 20:00:04 -0600327 .ops = &ne2k_ops,
328 .vendor = 0x10ec,
329 .device = 0x8029,
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000330};