Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / devs / networks / pcnet32 / pcnet32.c
blob78659d745522902602d42f504af43256221045fa
1 /*
2 * $Id$
3 */
5 /*
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston,
19 MA 02111-1307, USA.
22 #include <exec/types.h>
23 #include <exec/resident.h>
24 #include <exec/io.h>
25 #include <exec/ports.h>
27 #include <aros/libcall.h>
28 #include <aros/macros.h>
29 #include <aros/io.h>
31 #include <oop/oop.h>
33 #include <devices/sana2.h>
34 #include <devices/sana2specialstats.h>
36 #include <utility/utility.h>
37 #include <utility/tagitem.h>
38 #include <utility/hooks.h>
40 #include <hidd/pci.h>
41 #include <hidd/irq.h>
43 #include <proto/oop.h>
44 #include <proto/exec.h>
45 #include <proto/dos.h>
46 #include <proto/battclock.h>
48 #include <stdlib.h>
50 #include "pcnet32.h"
51 #include "unit.h"
52 #include LC_LIBDEFS_FILE
54 /* A bit fixed linux stuff here :) */
56 #undef LIBBASE
57 #define LIBBASE (dev->pcnu_device)
59 #define net_device PCN32Unit
61 #define TIMER_RPROK 3599597124UL
63 /* BYTE IO */
64 extern volatile UBYTE readb(APTR base);
65 extern volatile void writeb(UBYTE val, APTR base);
67 /* WORD IO */
68 extern volatile UWORD readw(APTR base);
69 extern volatile void writew(UWORD val, APTR base);
71 /* LONG IO */
72 extern volatile ULONG readl(APTR base);
73 extern volatile void writel(ULONG val, APTR base);
75 static ULONG usec2tick(ULONG usec)
77 ULONG ret, timer_rpr = TIMER_RPROK;
78 asm volatile("movl $0,%%eax; divl %2":"=a"(ret):"d"(usec),"m"(timer_rpr));
79 return ret;
82 void udelay(LONG usec)
84 int oldtick, tick;
85 usec = usec2tick(usec);
87 BYTEOUT(0x43, 0x80);
88 oldtick = BYTEIN(0x42);
89 oldtick += BYTEIN(0x42) << 8;
91 while (usec > 0)
93 BYTEOUT(0x43, 0x80);
94 tick = BYTEIN(0x42);
95 tick += BYTEIN(0x42) << 8;
97 usec -= (oldtick - tick);
98 if (tick > oldtick) usec -= 0x10000;
99 oldtick = tick;
103 static inline struct fe_priv *get_pcnpriv(struct net_device *dev)
105 return dev->pcnu_fe_priv;
108 static inline UBYTE *get_hwbase(struct net_device *dev)
110 return (UBYTE*)dev->pcnu_BaseMem;
113 static inline void pci_push(UBYTE *base)
115 /* force out pending posted writes */
116 readl(base);
119 static void pcn32_start_rx(struct net_device *dev)
121 struct fe_priv *np = get_pcnpriv(dev);
122 UBYTE *base = get_hwbase(dev);
124 D(bug("%s: pcn32_start_rx\n", dev->pcnu_name));
125 // Already running? Stop it.
126 #warning "TODO: Handle starting/stopping Rx"
129 static void pcn32_stop_rx(struct net_device *dev)
131 UBYTE *base = get_hwbase(dev);
133 D(bug("%s: pcn32_stop_rx\n", dev->pcnu_name));
134 #warning "TODO: Handle starting/stopping Rx"
137 static void pcn32_start_tx(struct net_device *dev)
139 UBYTE *base = get_hwbase(dev);
141 D(bug("%s: pcn32_start_tx()\n", dev->pcnu_name));
142 #warning "TODO: Handle starting/stopping Tx"
145 static void pcn32_stop_tx(struct net_device *dev)
147 UBYTE *base = get_hwbase(dev);
149 D(bug("%s: pcn32_stop_tx()\n", dev->pcnu_name));
150 #warning "TODO: Handle starting/stopping Tx"
153 static void pcn32_txrx_reset(struct net_device *dev)
155 struct fe_priv *np = get_pcnpriv(dev);
156 UBYTE *base = get_hwbase(dev);
158 D(bug("%s: pcn32_txrx_reset()\n", dev->pcnu_name));
162 * pcn32_set_multicast: dev->set_multicast function
163 * Called with dev->xmit_lock held.
165 static void pcn32_set_multicast(struct net_device *dev)
167 struct fe_priv *np = get_pcnpriv(dev);
168 UBYTE *base = get_hwbase(dev);
169 ULONG addr[2];
170 ULONG mask[2];
171 ULONG pff;
173 D(bug("%s: pcn32_set_multicast()\n", dev->pcnu_name));
175 memset(addr, 0, sizeof(addr));
176 memset(mask, 0, sizeof(mask));
179 static void pcnet32_deinitialize(struct net_device *dev)
181 dev->write_csr(dev->pcnu_BaseMem, 0, (1 << 2)); /* Stop the PCnet32 by setting the stop bit.. */
182 D(bug("%s: PCnet32 chipset STOPPED\n", dev->pcnu_name));
184 dev->write_bcr(dev->pcnu_BaseMem, 20, 4); /* Set pcnet32 into 16bit mode */
185 D(bug("%s: Chipset put into 16bit mode\n", dev->pcnu_name));
188 static void pcnet32_initialize(struct net_device *dev)
190 struct fe_priv *np = dev->pcnu_fe_priv;
191 UBYTE *base = get_hwbase(dev);
192 int i;
194 dev->reset(base); /* Cause the PCnet Chipset to reset */
195 D(bug("%s: Chipset RESET\n", dev->pcnu_name));
197 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
199 np->ring_addr = HIDD_PCIDriver_AllocPCIMem(
200 dev->pcnu_PCIDriver,
201 sizeof(struct rx_ring_desc) * (RX_RING_SIZE + TX_RING_SIZE));
203 np->fep_pcnet_init_block->rx_ring = AROS_LONG2LE(np->ring_addr);
204 np->fep_pcnet_init_block->tx_ring = AROS_LONG2LE(&((struct rx_ring_desc *)np->ring_addr)[RX_RING_SIZE]);
206 D(bug("%s: Allocated IO Rings [%d x Tx @ %x : %x] [%d x Rx @ %x : %x]\n",
207 dev->pcnu_name,
208 TX_RING_SIZE, np->ring_addr + (RX_RING_SIZE * sizeof(struct rx_ring_desc)), np->fep_pcnet_init_block->tx_ring,
209 RX_RING_SIZE, np->ring_addr, np->fep_pcnet_init_block->rx_ring));
211 dev->write_bcr(dev->pcnu_BaseMem, 20, 2); /* Set pcnet32 into 32bit mode */
212 D(bug("%s: PCnet Chipset put into 32bit mode\n", dev->pcnu_name));
214 np->orig_mac[0] = ( (readb(base + 0) << 24) | (readb(base + 1) << 16) | (readb(base + 2) << 8) | readb(base + 3) );
215 np->orig_mac[1] = ( (readb(base + 4) << 8) | readb(base + 5) );
217 dev->pcnu_dev_addr[0] = dev->pcnu_org_addr[0] = (np->orig_mac[0] >> 24) & 0xff;
218 dev->pcnu_dev_addr[1] = dev->pcnu_org_addr[1] = (np->orig_mac[0] >> 16) & 0xff;
219 dev->pcnu_dev_addr[2] = dev->pcnu_org_addr[2] = (np->orig_mac[0] >> 8) & 0xff;
220 dev->pcnu_dev_addr[3] = dev->pcnu_org_addr[3] = (np->orig_mac[0] >> 0) & 0xff;
222 dev->pcnu_dev_addr[4] = dev->pcnu_org_addr[4] = (np->orig_mac[1] >> 8) & 0xff;
223 dev->pcnu_dev_addr[5] = dev->pcnu_org_addr[5] = (np->orig_mac[1] >> 0) & 0xff;
225 D(bug("%s: MAC Address %02x:%02x:%02x:%02x:%02x:%02x\n", dev->pcnu_name,
226 dev->pcnu_dev_addr[0], dev->pcnu_dev_addr[1], dev->pcnu_dev_addr[2],
227 dev->pcnu_dev_addr[3], dev->pcnu_dev_addr[4], dev->pcnu_dev_addr[5]));
230 static void pcn32_drain_tx(struct net_device *dev)
232 struct fe_priv *np = get_pcnpriv(dev);
233 int i;
234 for (i = 0; i < TX_RING_SIZE; i++) {
235 #warning "TODO: pcn32_drain_tx does nothing atm."
236 // np->fep_pcnet_init_block->tx_ring[i].FlagLen = 0;
240 static void pcn32_drain_rx(struct net_device *dev)
242 struct fe_priv *np = get_pcnpriv(dev);
243 int i;
244 for (i = 0; i < RX_RING_SIZE; i++) {
245 #warning "TODO: pcn32_drain_rx does nothing atm."
246 // np->fep_pcnet_init_block->rx_ring[i].FlagLen = 0;
251 static void drain_ring(struct net_device *dev)
253 pcn32_drain_tx(dev);
254 pcn32_drain_rx(dev);
257 static int request_irq(struct net_device *dev)
259 OOP_Object *irq = OOP_NewObject(NULL, CLID_Hidd_IRQ, NULL);
260 BOOL ret;
262 D(bug("%s: request_irq()\n", dev->pcnu_name));
264 if (irq)
266 ret = HIDD_IRQ_AddHandler(irq, dev->pcnu_irqhandler, dev->pcnu_IRQ);
267 HIDD_IRQ_AddHandler(irq, dev->pcnu_touthandler, vHidd_IRQ_Timer);
269 D(bug("%s: request_irq: IRQ Handlers configured\n", dev->pcnu_name));
271 OOP_DisposeObject(irq);
273 if (ret)
275 return 0;
278 return 1;
281 static void free_irq(struct net_device *dev)
283 OOP_Object *irq = OOP_NewObject(NULL, CLID_Hidd_IRQ, NULL);
284 if (irq)
286 HIDD_IRQ_RemHandler(irq, dev->pcnu_irqhandler);
287 HIDD_IRQ_RemHandler(irq, dev->pcnu_touthandler);
288 OOP_DisposeObject(irq);
292 static void pcnet32_set_mac(struct net_device *dev)
294 UBYTE *base = get_hwbase(dev);
295 int i;
297 for (i = 0; i < 6; i++) // Copy MAC Address to init block
299 dev->pcnu_fe_priv->fep_pcnet_init_block->phys_addr[i] = dev->pcnu_dev_addr[i];
303 static int pcnet32_open(struct net_device *dev)
305 struct fe_priv *np = get_pcnpriv(dev);
306 UBYTE *base = get_hwbase(dev);
307 int ret, oom, i;
309 oom = 0;
311 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
313 np->rx_buffer = HIDD_PCIDriver_AllocPCIMem(
314 dev->pcnu_PCIDriver,
315 RX_RING_SIZE * RXTX_ALLOC_BUFSIZE);
317 if (np->rx_buffer == NULL)
318 oom = 1;
320 np->tx_buffer = HIDD_PCIDriver_AllocPCIMem(
321 dev->pcnu_PCIDriver,
322 TX_RING_SIZE * RXTX_ALLOC_BUFSIZE);
324 if (np->tx_buffer == NULL)
325 oom = 1;
327 D(bug("%s: pcnet32_open: begin\n",dev->pcnu_name));
329 if (oom == 0)
331 D(bug("%s: pcnet32_open: Allocated IO Buffers [ %d x Tx @ %x] [ %d x Rx @ %x]\n",dev->pcnu_name,
332 TX_RING_SIZE, np->tx_buffer,
333 RX_RING_SIZE, np->rx_buffer ));
336 np->fep_pcnet_init_block->mode = AROS_WORD2LE(0x0003); /* Disable Rx and Tx */
337 np->fep_pcnet_init_block->tlen_rlen = AROS_WORD2LE(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
339 D(bug("%s: pcnet32_open: Interrupts disabled\n",dev->pcnu_name));
341 pcnet32_set_mac(dev);
343 D(bug("%s: pcnet32_open: copied MAC address\n",dev->pcnu_name));
345 np->fep_pcnet_init_block->filter[0] = 0xffffffff;
346 np->fep_pcnet_init_block->filter[1] = 0xffffffff;
348 D(bug("%s: pcnet32_open: reset filter\n"));
350 ret = request_irq(dev);
351 if (ret)
352 goto out_drain;
354 for (i=0; i < TX_RING_SIZE; i++)
356 ((struct tx_ring_desc *)np->ring_addr)[i + RX_RING_SIZE].PacketBuffer = 0;
357 ((struct tx_ring_desc *)np->ring_addr)[i + RX_RING_SIZE].BufferStatus = 0;
360 D(bug("%s: pcnet32_open: Tx Ring initialised\n",dev->pcnu_name));
362 for (i=0; i < RX_RING_SIZE; i++)
364 ((struct rx_ring_desc *)np->ring_addr)[i].PacketBuffer = AROS_LONG2LE((IPTR)&np->rx_buffer[i]);
365 ((struct rx_ring_desc *)np->ring_addr)[i].BufferLength = AROS_WORD2LE(-RXTX_ALLOC_BUFSIZE);
366 ((struct rx_ring_desc *)np->ring_addr)[i].BufferStatus = AROS_WORD2LE((1 << 8)|(1 << 9)|(1 << 15));
369 D(bug("%s: pcnet32_open: Rx Ring initialised\n",dev->pcnu_name));
371 dev->write_bcr(dev->pcnu_BaseMem, 20, 2); /* Set pcnet32 into 32bit mode */
372 D(bug("%s: PCnet Chipset put into 32bit mode\n", dev->pcnu_name));
374 dev->write_csr(dev->pcnu_BaseMem, 1, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) & 0xffff)); /* Store the pointer to the pcnet32_init_block */
375 dev->write_csr(dev->pcnu_BaseMem, 2, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) >> 16));
377 D(bug("%s: pcnet32_open: set init_block (@ %x)\n",dev->pcnu_name,np->fep_pcnet_init_block));
379 dev->write_csr(dev->pcnu_BaseMem, 0, ((1 << 6)|(1 << 0))); /* Trigger an initialisation for the interrupt (INEA|INIT)*/
381 D(bug("%s: pcnet32_open: triggered init int\n",dev->pcnu_name));
383 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
385 dev->write_bcr(dev->pcnu_BaseMem, 20, 2); /* Set pcnet32 into 32bit mode */
386 D(bug("%s: PCnet Chipset put into 32bit mode\n", dev->pcnu_name));
388 dev->write_bcr(dev->pcnu_BaseMem, 2, ((dev->read_bcr(dev->pcnu_BaseMem, 2) & ~2) | 2)); /* Set autoselect bit */
390 D(bug("%s: pcnet32_open: autoselect bit set\n",dev->pcnu_name));
392 /** Handle Link setup **/
394 if ((dev->pcnu_pcnet_supported & support_mii) \
395 // && (!(option & enable_autoneg))
398 dev->write_bcr(dev->pcnu_BaseMem, 32, ((dev->read_bcr(dev->pcnu_BaseMem, 32) & ~0x38) | 0x10 | 0x08 ));
399 D(bug("%s: pcnet32_open: Chipset set into AutoNeg:OFF FullDuplex:ON LinkSpeed:100\n",dev->pcnu_name));
401 else
403 dev->write_bcr(dev->pcnu_BaseMem, 32, ((dev->read_bcr(dev->pcnu_BaseMem, 32) & ~0x98) | 0x20 ));
404 D(bug("%s: pcnet32_open: Chipset set into AutoNeg:ON\n",dev->pcnu_name));
407 /** Set GPSI bit in test register **/
409 /** Handle Transmit stop on underflow **/
410 if (dev->pcnu_pcnet_supported & support_dxsuflo)
412 dev->write_csr(dev->pcnu_BaseMem, 3, (dev->read_csr(dev->pcnu_BaseMem, 3) | 0x40));
413 D(bug("%s: pcnet32_open: Enabled Tx_STOP_ON_UNDERFLOW\n",dev->pcnu_name));
416 /** Enable TxDone intr inhibitor **/
417 if (dev->pcnu_pcnet_supported & support_ltint)
419 dev->write_csr(dev->pcnu_BaseMem, 5, (dev->read_csr(dev->pcnu_BaseMem, 5) | (1 << 14)));
420 D(bug("%s: pcnet32_open: Enabled Tx_DONE_INTR_INHIBITOR\n",dev->pcnu_name));
423 np->fep_pcnet_init_block->mode = 0x0000; /* Enable Rx and Tx */
425 D(bug("%s: pcnet32_open: Enable Rx & Tx\n",dev->pcnu_name));
427 dev->write_csr(dev->pcnu_BaseMem, 1, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) & 0xffff)); /* Re initialise the PCnet chipset */
428 dev->write_csr(dev->pcnu_BaseMem, 2, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) >> 16));
430 D(bug("%s: pcnet32_open: re-initialised chipset\n",dev->pcnu_name));
432 dev->write_csr(dev->pcnu_BaseMem, 4, 0x0915); /* ? */
433 dev->write_csr(dev->pcnu_BaseMem, 0, ((1 << 6)|(1 << 1)|(1 << 0)));
435 i = 0;
436 while (i++ < 100)
437 if (dev->read_csr(dev->pcnu_BaseMem, 0) & 0x0100)
438 break;
440 D(bug("%s: pcnet32_open: chipset ENABLED, csr[0] = %x\n",dev->pcnu_name, dev->read_csr(dev->pcnu_BaseMem, 0)));
442 dev->pcnu_flags |= IFF_UP;
443 ReportEvents(LIBBASE, dev, S2EVENT_ONLINE);
444 D(bug("%s: pcnet32_open: Device set as ONLINE\n",dev->pcnu_name));
446 return 0;
448 out_drain:
449 drain_ring(dev);
450 return ret;
453 static int pcn32_close(struct net_device *dev)
455 struct fe_priv *np = get_pcnpriv(dev);
456 UBYTE *base;
458 dev->pcnu_flags &= ~IFF_UP;
460 ObtainSemaphore(&np->lock);
461 np->in_shutdown = 1;
462 ReleaseSemaphore(&np->lock);
464 dev->pcnu_toutNEED = FALSE;
466 netif_stop_queue(dev);
467 ObtainSemaphore(&np->lock);
469 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
471 base = get_hwbase(dev);
473 ReleaseSemaphore(&np->lock);
475 free_irq(dev);
477 drain_ring(dev);
479 HIDD_PCIDriver_FreePCIMem(dev->pcnu_PCIDriver, np->rx_buffer);
480 HIDD_PCIDriver_FreePCIMem(dev->pcnu_PCIDriver, np->tx_buffer);
482 ReportEvents(LIBBASE, dev, S2EVENT_OFFLINE);
484 return 0;
488 void pcn32_get_functions(struct net_device *Unit)
490 Unit->initialize = pcnet32_initialize;
491 Unit->deinitialize = pcnet32_deinitialize;
492 Unit->start = pcnet32_open;
493 Unit->stop = pcn32_close;
494 Unit->set_mac_address = pcnet32_set_mac;
495 Unit->set_multicast = pcn32_set_multicast;