blob: 4479a767424e2ed2521f86d96f9ae74afd8c2da7 [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 {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000127 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;
Elyes Haouas25a7af12023-08-13 12:47:36 +0200133 acc += ((c - 'a') + 10);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000134 } else if ((c >= 'A') && (c <= 'F')) {
135 acc *= 16;
Elyes Haouas25a7af12023-08-13 12:47:36 +0200136 acc += ((c - 'A') + 10);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000137 } else {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000138 *mac++ = acc;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000139 acc = 0;
140 }
141
142 i++;
143 } while (c != '\0');
144}
145
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300146static void ns8390_tx_header(unsigned int eth_nic_base, int pktlen)
147{
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000148 unsigned short chksum;
149 unsigned char hdr[] = {
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300150 /* ETHERNET HDR */
151 /* destination macaddr */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000152 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
153 /* source mac */
154 0x02, 0x00, 0x00, 0xC0, 0xFF, 0xEE,
155 /* ethtype (IP) */
156 0x08, 0x00,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300157
158 /* IP HDR */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000159 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,
Kyösti Mälkkic8cf5912018-06-04 06:02:01 +0300164
165 /* UDP HDR */
166 /* SRC PORT DST PORT (2 bytes each),
167 * ulen, uchksum (must be zero or correct */
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000168 0x1a, 0x0b, 0x1a, 0x0a, 0x00, 0x9, 0x00, 0x00,
169 };
170
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000171 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 = compute_ip_checksum(&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
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300194void ne2k_transmit(unsigned int eth_nic_base)
195{
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000196 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 */
Elyes Haouas25a7af12023-08-13 12:47:36 +0200219 while ((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_PTX) != D8390_ISR_PTX)
220 ;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000221
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
Arthur Heymans91e61172023-11-06 15:20:33 +0100269 if (!ENV_ROMSTAGE_OR_BEFORE)
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300270 return 0;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000271
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300272 /* 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);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000275 if (dev == PCI_DEV_INVALID)
276 return 0;
277
Kyösti Mälkki8f5138d2019-09-27 18:08:20 +0300278 pci_s_write_config32(dev, 0x10, eth_nic_base | 1);
279 pci_s_write_config8(dev, 0x4, 0x1);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000280
281 c = inb(eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
282 outb(c, eth_nic_base + NE_ASIC_OFFSET + NE_RESET);
283
Elyes Haouas1ef547e2022-11-18 15:05:39 +0100284 (void)inb(0x84);
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000285
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
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000297static 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 |
Elyes HAOUAS0d8f1da2018-05-28 15:48:04 +0200308 IORESOURCE_ASSIGNED;
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000309}
310
Stefan Reinauer261f8422011-04-18 02:26:56 +0000311static struct device_operations ne2k_ops = {
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000312 .read_resources = read_resources,
313 .set_resources = pci_dev_set_resources,
314 .enable_resources = pci_dev_enable_resources,
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000315};
316
Stefan Reinauer261f8422011-04-18 02:26:56 +0000317static const struct pci_driver ne2k_driver __pci_driver = {
Martin Rothb9810a42017-07-23 20:00:04 -0600318 .ops = &ne2k_ops,
319 .vendor = 0x10ec,
320 .device = 0x8029,
Rudolf Marek4aa93cc2010-07-16 20:02:09 +0000321};