blob: 635315b35968335bbb7c5f87161ba4e7022b5003 [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>
Edward O'Callaghan20141112014-10-31 12:37:49 +110034#include <device/pci_ops.h>
Edward O'Callaghan20141112014-10-31 12:37:49 +110035#include <ip_checksum.h>
36
37#include "ns8390.h"
38
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000039#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
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000046#define MEM_SIZE MEM_32768
47#define TX_START 64
48#define RX_START (64 + D8390_TXBUF_SIZE)
49
50static 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
63static 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
75static 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--) {
Elyes HAOUAS0bc5d9d2022-01-27 07:55:34 +010087 outb(*(src++), eth_nic_base + NE_ASIC_OFFSET + NE_DATA);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +000088 }
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
97void 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
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000103static void str2ip(const char *str, unsigned char *ip)
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000104{
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 {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000114 *ip++ = acc;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000115 acc = 0;
116 }
117 i++;
118 } while (c != '\0');
119}
120
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000121static void str2mac(const char *str, unsigned char *mac)
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000122{
123 unsigned char c, i = 0;
124 int acc = 0;
125
126 do {
127
128 c = str[i];
129 if ((c >= '0') && (c <= '9')) {
130 acc *= 16;
131 acc += (c - '0');
132 } else if ((c >= 'a') && (c <= 'f')) {
133 acc *= 16;
134 acc += ((c - 'a') + 10) ;
135 } else if ((c >= 'A') && (c <= 'F')) {
136 acc *= 16;
137 acc += ((c - 'A') + 10) ;
138 } else {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000139 *mac++ = acc;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000140 acc = 0;
141 }
142
143 i++;
144 } while (c != '\0');
145}
146
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300147static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen)
148{
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000149 unsigned short chksum;
150 unsigned char hdr[] = {
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300151 /* ETHERNET HDR */
152 /* destination macaddr */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000153 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
154 /* source mac */
155 0x02, 0x00, 0x00, 0xC0, 0xFF, 0xEE,
156 /* ethtype (IP) */
157 0x08, 0x00,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300158
159 /* IP HDR */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000160 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
161 /* TTL, proto (UDP), chksum_hi, chksum_lo, IP0, IP1, IP2, IP3, */
162 0x40, 0x11, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x1,
163 /* IP0, IP1, IP2, IP3 */
164 0xff, 0xff, 0xff, 0xff,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300165
166 /* UDP HDR */
167 /* SRC PORT DST PORT (2 bytes each),
168 * ulen, uchksum (must be zero or correct */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000169 0x1a, 0x0b, 0x1a, 0x0a, 0x00, 0x9, 0x00, 0x00,
170 };
171
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000172 str2mac(CONFIG_CONSOLE_NE2K_DST_MAC, &hdr[0]);
173 str2ip(CONFIG_CONSOLE_NE2K_DST_IP, &hdr[30]);
174 str2ip(CONFIG_CONSOLE_NE2K_SRC_IP, &hdr[26]);
175
176 /* zero checksum */
177 hdr[24] = 0;
178 hdr[25] = 0;
179
180 /* update IP packet len */
181 hdr[16] = ((28 + pktlen) >> 8) & 0xff;
182 hdr[17] = (28 + pktlen) & 0xff;
183
184 /* update UDP len */
185 hdr[38] = (8 + pktlen) >> 8;
186 hdr[39] = 8 + pktlen;
187
188 chksum = compute_ip_checksum(&hdr[14], 20);
189
190 hdr[25] = chksum >> 8;
191 hdr[24] = chksum;
192 eth_pio_write(hdr, (TX_START << 8), sizeof(hdr), eth_nic_base);
193}
194
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300195void ne2k_transmit(unsigned int eth_nic_base)
196{
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000197 unsigned int pktsize;
198 unsigned int len = get_count(eth_nic_base);
199
200 // so place whole header inside chip buffer
201 ns8390_tx_header(eth_nic_base, len);
202
203 // commit sending now
204 outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
205
206 outb(TX_START, eth_nic_base + D8390_P0_TPSR);
207
208 pktsize = 42 + len;
209 if (pktsize < 64)
210 pktsize = 64;
211
212 outb(pktsize, eth_nic_base + D8390_P0_TBCR0);
213 outb(pktsize >> 8, eth_nic_base + D8390_P0_TBCR1);
214
215 outb(D8390_ISR_PTX, eth_nic_base + D8390_P0_ISR);
216
217 outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
218
219 /* wait for operation finish */
220 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_PTX) != D8390_ISR_PTX) ;
221
222 set_count(eth_nic_base, 0);
223}
224
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000225static 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
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300264int ne2k_init(unsigned int eth_nic_base)
265{
Elyes HAOUASc8a649c2018-06-10 23:36:44 +0200266 pci_devfn_t dev;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000267 unsigned char c;
268
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300269 /* FIXME: This console is not enabled for bootblock. */
270 if (!ENV_ROMSTAGE)
271 return 0;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000272
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300273 /* For this to work, mainboard code must have configured
274 PCI bridges prior to calling console_init(). */
275 dev = pci_locate_device(PCI_ID(0x10ec, 0x8029), 0);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000276 if (dev == PCI_DEV_INVALID)
277 return 0;
278
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300279 pci_s_write_config32(dev, 0x10, eth_nic_base | 1);
280 pci_s_write_config8(dev, 0x4, 0x1);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000281
282 c = inb(eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
283 outb(c, eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
284
285 (void) inb(0x84);
286
287 outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base + D8390_P0_COMMAND);
288 outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
289
290 outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
291 outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
292 outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
293
294 ns8390_reset(eth_nic_base);
295 return 1;
296}
297
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000298static void read_resources(struct device *dev)
299{
300 struct resource *res;
301
302 res = new_resource(dev, PCI_BASE_ADDRESS_0);
303 res->base = CONFIG_CONSOLE_NE2K_IO_PORT;
304 res->size = 32;
305 res->align = 5;
306 res->gran = 5;
307 res->limit = res->base + res->size - 1;
308 res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_STORED |
Elyes HAOUAS0d8f1da2018-05-28 15:48:04 +0200309 IORESOURCE_ASSIGNED;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000310 return;
311}
312
Stefan Reinauer261f8422011-04-18 02:26:56 +0000313static struct device_operations ne2k_ops = {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000314 .read_resources = read_resources,
315 .set_resources = pci_dev_set_resources,
316 .enable_resources = pci_dev_enable_resources,
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000317};
318
Stefan Reinauer261f8422011-04-18 02:26:56 +0000319static const struct pci_driver ne2k_driver __pci_driver = {
Martin Rothb9810a42017-07-23 20:00:04 -0600320 .ops = &ne2k_ops,
321 .vendor = 0x10ec,
322 .device = 0x8029,
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000323};