]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c609719b WD |
2 | /* |
3 | * (C) Copyright 2002 Wolfgang Grandegger, [email protected]. | |
4 | * | |
5 | * This driver for AMD PCnet network controllers is derived from the | |
6 | * Linux driver pcnet32.c written 1996-1999 by Thomas Bogendoerfer. | |
c609719b WD |
7 | */ |
8 | ||
1eb69ae4 | 9 | #include <cpu_func.h> |
53019cf3 | 10 | #include <dm.h> |
f7ae49fc | 11 | #include <log.h> |
c609719b | 12 | #include <malloc.h> |
1c38c36e | 13 | #include <memalign.h> |
c609719b | 14 | #include <net.h> |
e3090534 | 15 | #include <netdev.h> |
90526e9f | 16 | #include <asm/cache.h> |
c609719b WD |
17 | #include <asm/io.h> |
18 | #include <pci.h> | |
c05ed00a | 19 | #include <linux/delay.h> |
c609719b | 20 | |
11ea26fd | 21 | #define PCNET_DEBUG_LEVEL 0 /* 0=off, 1=init, 2=rx/tx */ |
c609719b | 22 | |
138b6089 WD |
23 | #define PCNET_DEBUG1(fmt,args...) \ |
24 | debug_cond(PCNET_DEBUG_LEVEL > 0, fmt ,##args) | |
25 | #define PCNET_DEBUG2(fmt,args...) \ | |
26 | debug_cond(PCNET_DEBUG_LEVEL > 1, fmt ,##args) | |
c609719b | 27 | |
c609719b WD |
28 | /* |
29 | * Set the number of Tx and Rx buffers, using Log_2(# buffers). | |
30 | * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. | |
31 | * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). | |
32 | */ | |
33 | #define PCNET_LOG_TX_BUFFERS 0 | |
34 | #define PCNET_LOG_RX_BUFFERS 2 | |
35 | ||
36 | #define TX_RING_SIZE (1 << (PCNET_LOG_TX_BUFFERS)) | |
37 | #define TX_RING_LEN_BITS ((PCNET_LOG_TX_BUFFERS) << 12) | |
38 | ||
39 | #define RX_RING_SIZE (1 << (PCNET_LOG_RX_BUFFERS)) | |
40 | #define RX_RING_LEN_BITS ((PCNET_LOG_RX_BUFFERS) << 4) | |
41 | ||
42 | #define PKT_BUF_SZ 1544 | |
43 | ||
44 | /* The PCNET Rx and Tx ring descriptors. */ | |
45 | struct pcnet_rx_head { | |
11ea26fd WD |
46 | u32 base; |
47 | s16 buf_length; | |
48 | s16 status; | |
49 | u32 msg_length; | |
50 | u32 reserved; | |
c609719b WD |
51 | }; |
52 | ||
53 | struct pcnet_tx_head { | |
11ea26fd WD |
54 | u32 base; |
55 | s16 length; | |
56 | s16 status; | |
57 | u32 misc; | |
58 | u32 reserved; | |
c609719b WD |
59 | }; |
60 | ||
61 | /* The PCNET 32-Bit initialization block, described in databook. */ | |
62 | struct pcnet_init_block { | |
11ea26fd WD |
63 | u16 mode; |
64 | u16 tlen_rlen; | |
65 | u8 phys_addr[6]; | |
66 | u16 reserved; | |
67 | u32 filter[2]; | |
68 | /* Receive and transmit ring base, along with extra bits. */ | |
69 | u32 rx_ring; | |
70 | u32 tx_ring; | |
71 | u32 reserved2; | |
c609719b WD |
72 | }; |
73 | ||
f1ae382d | 74 | struct pcnet_uncached_priv { |
11ea26fd WD |
75 | struct pcnet_rx_head rx_ring[RX_RING_SIZE]; |
76 | struct pcnet_tx_head tx_ring[TX_RING_SIZE]; | |
77 | struct pcnet_init_block init_block; | |
1c38c36e | 78 | } __aligned(ARCH_DMA_MINALIGN); |
f1ae382d | 79 | |
97d5c145 | 80 | struct pcnet_priv { |
1c38c36e | 81 | struct pcnet_uncached_priv ucp; |
11ea26fd | 82 | /* Receive Buffer space */ |
1c38c36e MV |
83 | unsigned char rx_buf[RX_RING_SIZE][PKT_BUF_SZ + 4]; |
84 | struct pcnet_uncached_priv *uc; | |
59edb266 MV |
85 | struct udevice *dev; |
86 | const char *name; | |
59edb266 | 87 | void __iomem *iobase; |
1023a1e6 | 88 | u8 *enetaddr; |
dea9b601 | 89 | u16 status; |
11ea26fd WD |
90 | int cur_rx; |
91 | int cur_tx; | |
97d5c145 | 92 | }; |
c609719b | 93 | |
c609719b WD |
94 | /* Offsets from base I/O address for WIO mode */ |
95 | #define PCNET_RDP 0x10 | |
96 | #define PCNET_RAP 0x12 | |
97 | #define PCNET_RESET 0x14 | |
98 | #define PCNET_BDP 0x16 | |
99 | ||
3b2d63a7 | 100 | static u16 pcnet_read_csr(struct pcnet_priv *lp, int index) |
c609719b | 101 | { |
3b2d63a7 MV |
102 | writew(index, lp->iobase + PCNET_RAP); |
103 | return readw(lp->iobase + PCNET_RDP); | |
c609719b WD |
104 | } |
105 | ||
3b2d63a7 | 106 | static void pcnet_write_csr(struct pcnet_priv *lp, int index, u16 val) |
c609719b | 107 | { |
3b2d63a7 MV |
108 | writew(index, lp->iobase + PCNET_RAP); |
109 | writew(val, lp->iobase + PCNET_RDP); | |
c609719b WD |
110 | } |
111 | ||
3b2d63a7 | 112 | static u16 pcnet_read_bcr(struct pcnet_priv *lp, int index) |
c609719b | 113 | { |
3b2d63a7 MV |
114 | writew(index, lp->iobase + PCNET_RAP); |
115 | return readw(lp->iobase + PCNET_BDP); | |
c609719b WD |
116 | } |
117 | ||
3b2d63a7 | 118 | static void pcnet_write_bcr(struct pcnet_priv *lp, int index, u16 val) |
c609719b | 119 | { |
3b2d63a7 MV |
120 | writew(index, lp->iobase + PCNET_RAP); |
121 | writew(val, lp->iobase + PCNET_BDP); | |
c609719b WD |
122 | } |
123 | ||
3b2d63a7 | 124 | static void pcnet_reset(struct pcnet_priv *lp) |
c609719b | 125 | { |
3b2d63a7 | 126 | readw(lp->iobase + PCNET_RESET); |
c609719b WD |
127 | } |
128 | ||
3b2d63a7 | 129 | static int pcnet_check(struct pcnet_priv *lp) |
c609719b | 130 | { |
3b2d63a7 MV |
131 | writew(88, lp->iobase + PCNET_RAP); |
132 | return readw(lp->iobase + PCNET_RAP) == 88; | |
c609719b WD |
133 | } |
134 | ||
60074d9d | 135 | static inline pci_addr_t pcnet_virt_to_mem(struct pcnet_priv *lp, void *addr) |
df50b3b4 | 136 | { |
df50b3b4 DS |
137 | void *virt_addr = addr; |
138 | ||
59edb266 | 139 | return dm_pci_virt_to_mem(lp->dev, virt_addr); |
df50b3b4 | 140 | } |
c609719b WD |
141 | |
142 | static struct pci_device_id supported[] = { | |
e4797c31 | 143 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE) }, |
11ea26fd | 144 | {} |
c609719b WD |
145 | }; |
146 | ||
dea9b601 | 147 | static int pcnet_probe_common(struct pcnet_priv *lp) |
c609719b | 148 | { |
11ea26fd WD |
149 | int chip_version; |
150 | char *chipname; | |
11ea26fd | 151 | int i; |
c609719b | 152 | |
11ea26fd | 153 | /* Reset the PCnet controller */ |
3b2d63a7 | 154 | pcnet_reset(lp); |
11ea26fd WD |
155 | |
156 | /* Check if register access is working */ | |
3b2d63a7 | 157 | if (pcnet_read_csr(lp, 0) != 4 || !pcnet_check(lp)) { |
1023a1e6 | 158 | printf("%s: CSR register access check failed\n", lp->name); |
11ea26fd WD |
159 | return -1; |
160 | } | |
161 | ||
162 | /* Identify the chip */ | |
3b2d63a7 | 163 | chip_version = pcnet_read_csr(lp, 88) | (pcnet_read_csr(lp, 89) << 16); |
11ea26fd WD |
164 | if ((chip_version & 0xfff) != 0x003) |
165 | return -1; | |
166 | chip_version = (chip_version >> 12) & 0xffff; | |
167 | switch (chip_version) { | |
168 | case 0x2621: | |
169 | chipname = "PCnet/PCI II 79C970A"; /* PCI */ | |
170 | break; | |
11ea26fd WD |
171 | case 0x2625: |
172 | chipname = "PCnet/FAST III 79C973"; /* PCI */ | |
173 | break; | |
11ea26fd WD |
174 | case 0x2627: |
175 | chipname = "PCnet/FAST III 79C975"; /* PCI */ | |
176 | break; | |
11ea26fd | 177 | default: |
6011dabd | 178 | printf("%s: PCnet version %#x not supported\n", |
1023a1e6 | 179 | lp->name, chip_version); |
11ea26fd WD |
180 | return -1; |
181 | } | |
c609719b | 182 | |
6011dabd | 183 | PCNET_DEBUG1("AMD %s\n", chipname); |
c609719b | 184 | |
11ea26fd WD |
185 | /* |
186 | * In most chips, after a chip reset, the ethernet address is read from | |
187 | * the station address PROM at the base address and programmed into the | |
188 | * "Physical Address Registers" CSR12-14. | |
189 | */ | |
190 | for (i = 0; i < 3; i++) { | |
191 | unsigned int val; | |
192 | ||
3b2d63a7 | 193 | val = pcnet_read_csr(lp, i + 12) & 0x0ffff; |
11ea26fd | 194 | /* There may be endianness issues here. */ |
1023a1e6 MV |
195 | lp->enetaddr[2 * i] = val & 0x0ff; |
196 | lp->enetaddr[2 * i + 1] = (val >> 8) & 0x0ff; | |
11ea26fd | 197 | } |
c609719b | 198 | |
11ea26fd | 199 | return 0; |
c609719b WD |
200 | } |
201 | ||
dea9b601 | 202 | static int pcnet_init_common(struct pcnet_priv *lp) |
c609719b | 203 | { |
f1ae382d | 204 | struct pcnet_uncached_priv *uc; |
11ea26fd | 205 | int i, val; |
442d2e01 | 206 | unsigned long addr; |
c609719b | 207 | |
1023a1e6 | 208 | PCNET_DEBUG1("%s: %s...\n", lp->name, __func__); |
c609719b | 209 | |
11ea26fd | 210 | /* Switch pcnet to 32bit mode */ |
3b2d63a7 | 211 | pcnet_write_bcr(lp, 20, 2); |
c609719b | 212 | |
11ea26fd | 213 | /* Set/reset autoselect bit */ |
3b2d63a7 | 214 | val = pcnet_read_bcr(lp, 2) & ~2; |
11ea26fd | 215 | val |= 2; |
3b2d63a7 | 216 | pcnet_write_bcr(lp, 2, val); |
c609719b | 217 | |
11ea26fd | 218 | /* Enable auto negotiate, setup, disable fd */ |
3b2d63a7 | 219 | val = pcnet_read_bcr(lp, 32) & ~0x98; |
11ea26fd | 220 | val |= 0x20; |
3b2d63a7 | 221 | pcnet_write_bcr(lp, 32, val); |
c609719b | 222 | |
62715a2c PB |
223 | /* |
224 | * Enable NOUFLO on supported controllers, with the transmit | |
225 | * start point set to the full packet. This will cause entire | |
226 | * packets to be buffered by the ethernet controller before | |
227 | * transmission, eliminating underflows which are common on | |
228 | * slower devices. Controllers which do not support NOUFLO will | |
229 | * simply be left with a larger transmit FIFO threshold. | |
230 | */ | |
3b2d63a7 | 231 | val = pcnet_read_bcr(lp, 18); |
62715a2c | 232 | val |= 1 << 11; |
3b2d63a7 MV |
233 | pcnet_write_bcr(lp, 18, val); |
234 | val = pcnet_read_csr(lp, 80); | |
62715a2c | 235 | val |= 0x3 << 10; |
3b2d63a7 | 236 | pcnet_write_csr(lp, 80, val); |
62715a2c | 237 | |
f1ae382d PB |
238 | uc = lp->uc; |
239 | ||
240 | uc->init_block.mode = cpu_to_le16(0x0000); | |
241 | uc->init_block.filter[0] = 0x00000000; | |
242 | uc->init_block.filter[1] = 0x00000000; | |
11ea26fd WD |
243 | |
244 | /* | |
245 | * Initialize the Rx ring. | |
246 | */ | |
247 | lp->cur_rx = 0; | |
248 | for (i = 0; i < RX_RING_SIZE; i++) { | |
60074d9d | 249 | addr = pcnet_virt_to_mem(lp, lp->rx_buf[i]); |
df50b3b4 | 250 | uc->rx_ring[i].base = cpu_to_le32(addr); |
f1ae382d PB |
251 | uc->rx_ring[i].buf_length = cpu_to_le16(-PKT_BUF_SZ); |
252 | uc->rx_ring[i].status = cpu_to_le16(0x8000); | |
11ea26fd WD |
253 | PCNET_DEBUG1 |
254 | ("Rx%d: base=0x%x buf_length=0x%hx status=0x%hx\n", i, | |
f1ae382d PB |
255 | uc->rx_ring[i].base, uc->rx_ring[i].buf_length, |
256 | uc->rx_ring[i].status); | |
11ea26fd WD |
257 | } |
258 | ||
259 | /* | |
260 | * Initialize the Tx ring. The Tx buffer address is filled in as | |
261 | * needed, but we do need to clear the upper ownership bit. | |
262 | */ | |
c609719b | 263 | lp->cur_tx = 0; |
11ea26fd | 264 | for (i = 0; i < TX_RING_SIZE; i++) { |
f1ae382d PB |
265 | uc->tx_ring[i].base = 0; |
266 | uc->tx_ring[i].status = 0; | |
11ea26fd | 267 | } |
c609719b | 268 | |
11ea26fd WD |
269 | /* |
270 | * Setup Init Block. | |
271 | */ | |
f1ae382d | 272 | PCNET_DEBUG1("Init block at 0x%p: MAC", &lp->uc->init_block); |
c609719b | 273 | |
11ea26fd | 274 | for (i = 0; i < 6; i++) { |
1023a1e6 | 275 | lp->uc->init_block.phys_addr[i] = lp->enetaddr[i]; |
f1ae382d | 276 | PCNET_DEBUG1(" %02x", lp->uc->init_block.phys_addr[i]); |
11ea26fd WD |
277 | } |
278 | ||
f1ae382d | 279 | uc->init_block.tlen_rlen = cpu_to_le16(TX_RING_LEN_BITS | |
6011dabd | 280 | RX_RING_LEN_BITS); |
60074d9d | 281 | addr = pcnet_virt_to_mem(lp, uc->rx_ring); |
df50b3b4 | 282 | uc->init_block.rx_ring = cpu_to_le32(addr); |
60074d9d | 283 | addr = pcnet_virt_to_mem(lp, uc->tx_ring); |
df50b3b4 | 284 | uc->init_block.tx_ring = cpu_to_le32(addr); |
11ea26fd | 285 | |
6011dabd | 286 | PCNET_DEBUG1("\ntlen_rlen=0x%x rx_ring=0x%x tx_ring=0x%x\n", |
f1ae382d PB |
287 | uc->init_block.tlen_rlen, |
288 | uc->init_block.rx_ring, uc->init_block.tx_ring); | |
c609719b | 289 | |
c609719b | 290 | /* |
11ea26fd | 291 | * Tell the controller where the Init Block is located. |
c609719b | 292 | */ |
f1ae382d | 293 | barrier(); |
60074d9d | 294 | addr = pcnet_virt_to_mem(lp, &lp->uc->init_block); |
3b2d63a7 MV |
295 | pcnet_write_csr(lp, 1, addr & 0xffff); |
296 | pcnet_write_csr(lp, 2, (addr >> 16) & 0xffff); | |
11ea26fd | 297 | |
3b2d63a7 MV |
298 | pcnet_write_csr(lp, 4, 0x0915); |
299 | pcnet_write_csr(lp, 0, 0x0001); /* start */ | |
11ea26fd WD |
300 | |
301 | /* Wait for Init Done bit */ | |
302 | for (i = 10000; i > 0; i--) { | |
3b2d63a7 | 303 | if (pcnet_read_csr(lp, 0) & 0x0100) |
11ea26fd | 304 | break; |
6011dabd | 305 | udelay(10); |
c609719b | 306 | } |
11ea26fd | 307 | if (i <= 0) { |
1023a1e6 | 308 | printf("%s: TIMEOUT: controller init failed\n", lp->name); |
3b2d63a7 | 309 | pcnet_reset(lp); |
11ea26fd | 310 | return -1; |
c609719b | 311 | } |
c609719b | 312 | |
11ea26fd WD |
313 | /* |
314 | * Finally start network controller operation. | |
315 | */ | |
3b2d63a7 | 316 | pcnet_write_csr(lp, 0, 0x0002); |
11ea26fd WD |
317 | |
318 | return 0; | |
c609719b WD |
319 | } |
320 | ||
dea9b601 | 321 | static int pcnet_send_common(struct pcnet_priv *lp, void *packet, int pkt_len) |
c609719b | 322 | { |
11ea26fd | 323 | int i, status; |
df50b3b4 | 324 | u32 addr; |
f1ae382d | 325 | struct pcnet_tx_head *entry = &lp->uc->tx_ring[lp->cur_tx]; |
11ea26fd | 326 | |
6011dabd PB |
327 | PCNET_DEBUG2("Tx%d: %d bytes from 0x%p ", lp->cur_tx, pkt_len, |
328 | packet); | |
11ea26fd | 329 | |
f3ac866c PB |
330 | flush_dcache_range((unsigned long)packet, |
331 | (unsigned long)packet + pkt_len); | |
332 | ||
11ea26fd WD |
333 | /* Wait for completion by testing the OWN bit */ |
334 | for (i = 1000; i > 0; i--) { | |
6fb49e4a | 335 | status = readw(&entry->status); |
11ea26fd WD |
336 | if ((status & 0x8000) == 0) |
337 | break; | |
6011dabd PB |
338 | udelay(100); |
339 | PCNET_DEBUG2("."); | |
11ea26fd WD |
340 | } |
341 | if (i <= 0) { | |
6011dabd | 342 | printf("%s: TIMEOUT: Tx%d failed (status = 0x%x)\n", |
1023a1e6 | 343 | lp->name, lp->cur_tx, status); |
11ea26fd WD |
344 | pkt_len = 0; |
345 | goto failure; | |
346 | } | |
347 | ||
348 | /* | |
349 | * Setup Tx ring. Caution: the write order is important here, | |
350 | * set the status with the "ownership" bits last. | |
351 | */ | |
60074d9d | 352 | addr = pcnet_virt_to_mem(lp, packet); |
6fb49e4a PB |
353 | writew(-pkt_len, &entry->length); |
354 | writel(0, &entry->misc); | |
df50b3b4 | 355 | writel(addr, &entry->base); |
6fb49e4a | 356 | writew(0x8300, &entry->status); |
11ea26fd WD |
357 | |
358 | /* Trigger an immediate send poll. */ | |
3b2d63a7 | 359 | pcnet_write_csr(lp, 0, 0x0008); |
11ea26fd WD |
360 | |
361 | failure: | |
362 | if (++lp->cur_tx >= TX_RING_SIZE) | |
363 | lp->cur_tx = 0; | |
364 | ||
6011dabd | 365 | PCNET_DEBUG2("done\n"); |
11ea26fd WD |
366 | return pkt_len; |
367 | } | |
368 | ||
dea9b601 | 369 | static int pcnet_recv_common(struct pcnet_priv *lp, unsigned char **bufp) |
11ea26fd WD |
370 | { |
371 | struct pcnet_rx_head *entry; | |
a354ddc3 | 372 | unsigned char *buf; |
11ea26fd | 373 | int pkt_len = 0; |
dea9b601 | 374 | u16 err_status; |
11ea26fd | 375 | |
dea9b601 MV |
376 | entry = &lp->uc->rx_ring[lp->cur_rx]; |
377 | /* | |
378 | * If we own the next entry, it's a new packet. Send it up. | |
379 | */ | |
380 | lp->status = readw(&entry->status); | |
381 | if ((lp->status & 0x8000) != 0) | |
382 | return 0; | |
383 | err_status = lp->status >> 8; | |
384 | ||
385 | if (err_status != 0x03) { /* There was an error. */ | |
386 | printf("%s: Rx%d", lp->name, lp->cur_rx); | |
387 | PCNET_DEBUG1(" (status=0x%x)", err_status); | |
388 | if (err_status & 0x20) | |
389 | printf(" Frame"); | |
390 | if (err_status & 0x10) | |
391 | printf(" Overflow"); | |
392 | if (err_status & 0x08) | |
393 | printf(" CRC"); | |
394 | if (err_status & 0x04) | |
395 | printf(" Fifo"); | |
396 | printf(" Error\n"); | |
397 | lp->status &= 0x03ff; | |
398 | return 0; | |
399 | } | |
11ea26fd | 400 | |
dea9b601 MV |
401 | pkt_len = (readl(&entry->msg_length) & 0xfff) - 4; |
402 | if (pkt_len < 60) { | |
403 | printf("%s: Rx%d: invalid packet length %d\n", | |
404 | lp->name, lp->cur_rx, pkt_len); | |
405 | return 0; | |
11ea26fd | 406 | } |
dea9b601 MV |
407 | |
408 | *bufp = lp->rx_buf[lp->cur_rx]; | |
409 | invalidate_dcache_range((unsigned long)*bufp, | |
410 | (unsigned long)*bufp + pkt_len); | |
411 | ||
412 | PCNET_DEBUG2("Rx%d: %d bytes from 0x%p\n", | |
413 | lp->cur_rx, pkt_len, buf); | |
414 | ||
11ea26fd | 415 | return pkt_len; |
c609719b WD |
416 | } |
417 | ||
dea9b601 MV |
418 | static void pcnet_free_pkt_common(struct pcnet_priv *lp, unsigned int len) |
419 | { | |
420 | struct pcnet_rx_head *entry; | |
421 | ||
422 | entry = &lp->uc->rx_ring[lp->cur_rx]; | |
423 | ||
424 | lp->status |= 0x8000; | |
425 | writew(lp->status, &entry->status); | |
426 | ||
427 | if (++lp->cur_rx >= RX_RING_SIZE) | |
428 | lp->cur_rx = 0; | |
429 | } | |
430 | ||
431 | static void pcnet_halt_common(struct pcnet_priv *lp) | |
11ea26fd WD |
432 | { |
433 | int i; | |
434 | ||
1023a1e6 | 435 | PCNET_DEBUG1("%s: %s...\n", lp->name, __func__); |
11ea26fd WD |
436 | |
437 | /* Reset the PCnet controller */ | |
3b2d63a7 | 438 | pcnet_reset(lp); |
11ea26fd WD |
439 | |
440 | /* Wait for Stop bit */ | |
441 | for (i = 1000; i > 0; i--) { | |
3b2d63a7 | 442 | if (pcnet_read_csr(lp, 0) & 0x4) |
11ea26fd | 443 | break; |
6011dabd | 444 | udelay(10); |
11ea26fd | 445 | } |
6011dabd | 446 | if (i <= 0) |
1023a1e6 | 447 | printf("%s: TIMEOUT: controller reset failed\n", lp->name); |
11ea26fd | 448 | } |
69e08bd7 | 449 | |
59edb266 MV |
450 | static int pcnet_start(struct udevice *dev) |
451 | { | |
c69cda25 | 452 | struct eth_pdata *plat = dev_get_plat(dev); |
59edb266 MV |
453 | struct pcnet_priv *priv = dev_get_priv(dev); |
454 | ||
455 | memcpy(priv->enetaddr, plat->enetaddr, sizeof(plat->enetaddr)); | |
456 | ||
457 | return pcnet_init_common(priv); | |
458 | } | |
459 | ||
460 | static void pcnet_stop(struct udevice *dev) | |
461 | { | |
462 | struct pcnet_priv *priv = dev_get_priv(dev); | |
463 | ||
464 | pcnet_halt_common(priv); | |
465 | } | |
466 | ||
467 | static int pcnet_send(struct udevice *dev, void *packet, int length) | |
468 | { | |
469 | struct pcnet_priv *priv = dev_get_priv(dev); | |
470 | int ret; | |
471 | ||
472 | ret = pcnet_send_common(priv, packet, length); | |
473 | ||
474 | return ret ? 0 : -ETIMEDOUT; | |
475 | } | |
476 | ||
477 | static int pcnet_recv(struct udevice *dev, int flags, uchar **packetp) | |
478 | { | |
479 | struct pcnet_priv *priv = dev_get_priv(dev); | |
480 | ||
481 | return pcnet_recv_common(priv, packetp); | |
482 | } | |
483 | ||
484 | static int pcnet_free_pkt(struct udevice *dev, uchar *packet, int length) | |
485 | { | |
486 | struct pcnet_priv *priv = dev_get_priv(dev); | |
487 | ||
488 | pcnet_free_pkt_common(priv, length); | |
489 | ||
490 | return 0; | |
491 | } | |
492 | ||
493 | static int pcnet_bind(struct udevice *dev) | |
494 | { | |
495 | static int card_number; | |
496 | char name[16]; | |
497 | ||
498 | sprintf(name, "pcnet#%u", card_number++); | |
499 | ||
500 | return device_set_name(dev, name); | |
501 | } | |
502 | ||
503 | static int pcnet_probe(struct udevice *dev) | |
504 | { | |
c69cda25 | 505 | struct eth_pdata *plat = dev_get_plat(dev); |
59edb266 MV |
506 | struct pcnet_priv *lp = dev_get_priv(dev); |
507 | u16 command, status; | |
508 | u32 iobase; | |
509 | int ret; | |
510 | ||
511 | dm_pci_read_config32(dev, PCI_BASE_ADDRESS_1, &iobase); | |
512 | iobase &= ~0xf; | |
513 | ||
514 | lp->uc = map_physmem((phys_addr_t)&lp->ucp, | |
515 | sizeof(lp->ucp), MAP_NOCACHE); | |
516 | lp->dev = dev; | |
517 | lp->name = dev->name; | |
518 | lp->enetaddr = plat->enetaddr; | |
519 | lp->iobase = (void *)dm_pci_mem_to_phys(dev, iobase); | |
520 | ||
521 | flush_dcache_range((unsigned long)lp, | |
522 | (unsigned long)lp + sizeof(*lp)); | |
523 | ||
524 | command = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | |
525 | dm_pci_write_config16(dev, PCI_COMMAND, command); | |
526 | dm_pci_read_config16(dev, PCI_COMMAND, &status); | |
527 | if ((status & command) != command) { | |
528 | printf("%s: Couldn't enable IO access or Bus Mastering\n", | |
529 | lp->name); | |
530 | return -EINVAL; | |
531 | } | |
532 | ||
533 | dm_pci_write_config8(dev, PCI_LATENCY_TIMER, 0x20); | |
534 | ||
535 | ret = pcnet_probe_common(lp); | |
536 | if (ret) | |
537 | return ret; | |
538 | ||
539 | return 0; | |
540 | } | |
541 | ||
542 | static const struct eth_ops pcnet_ops = { | |
543 | .start = pcnet_start, | |
544 | .send = pcnet_send, | |
545 | .recv = pcnet_recv, | |
546 | .stop = pcnet_stop, | |
547 | .free_pkt = pcnet_free_pkt, | |
548 | }; | |
549 | ||
550 | U_BOOT_DRIVER(eth_pcnet) = { | |
551 | .name = "eth_pcnet", | |
552 | .id = UCLASS_ETH, | |
553 | .bind = pcnet_bind, | |
554 | .probe = pcnet_probe, | |
555 | .ops = &pcnet_ops, | |
41575d8e | 556 | .priv_auto = sizeof(struct pcnet_priv), |
caa4daa2 | 557 | .plat_auto = sizeof(struct eth_pdata), |
59edb266 MV |
558 | .flags = DM_UC_FLAG_ALLOC_PRIV_DMA, |
559 | }; | |
560 | ||
561 | U_BOOT_PCI_DEVICE(eth_pcnet, supported); |