Check for SYS/GL during library init. Reason is that
[AROS.git] / workbench / devs / networks / pcnet32 / pcnet32.c
blob1a18f7750b5b7f4b86364dce56e90b73e90b8f31
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>
42 #include <proto/oop.h>
43 #include <proto/exec.h>
44 #include <proto/dos.h>
45 #include <proto/battclock.h>
47 #include <hardware/intbits.h>
49 #include <stdlib.h>
51 #include "pcnet32.h"
52 #include "unit.h"
53 #include LC_LIBDEFS_FILE
55 /* A bit fixed linux stuff here :) */
57 #undef LIBBASE
58 #define LIBBASE (dev->pcnu_device)
60 #define net_device PCN32Unit
62 #define TIMER_RPROK 3599597124UL
64 /* BYTE IO */
65 extern volatile UBYTE readb(APTR base);
66 extern volatile void writeb(UBYTE val, APTR base);
68 /* WORD IO */
69 extern volatile UWORD readw(APTR base);
70 extern volatile void writew(UWORD val, APTR base);
72 /* LONG IO */
73 extern volatile ULONG readl(APTR base);
74 extern volatile void writel(ULONG val, APTR base);
76 static ULONG usec2tick(ULONG usec)
78 ULONG ret, timer_rpr = TIMER_RPROK;
79 asm volatile("movl $0,%%eax; divl %2":"=a"(ret):"d"(usec),"m"(timer_rpr));
80 return ret;
83 void udelay(LONG usec)
85 int oldtick, tick;
86 usec = usec2tick(usec);
88 BYTEOUT(0x43, 0x80);
89 oldtick = BYTEIN(0x42);
90 oldtick += BYTEIN(0x42) << 8;
92 while (usec > 0)
94 BYTEOUT(0x43, 0x80);
95 tick = BYTEIN(0x42);
96 tick += BYTEIN(0x42) << 8;
98 usec -= (oldtick - tick);
99 if (tick > oldtick) usec -= 0x10000;
100 oldtick = tick;
104 static inline struct fe_priv *get_pcnpriv(struct net_device *dev)
106 return dev->pcnu_fe_priv;
109 static inline UBYTE *get_hwbase(struct net_device *dev)
111 return (UBYTE*)dev->pcnu_BaseMem;
114 static inline void pci_push(UBYTE *base)
116 /* force out pending posted writes */
117 readl(base);
120 #if 0
121 static void pcn32_start_rx(struct net_device *dev)
123 // struct fe_priv *np = get_pcnpriv(dev);
124 // UBYTE *base = get_hwbase(dev);
126 D(bug("%s: pcn32_start_rx\n", dev->pcnu_name));
127 // Already running? Stop it.
128 /* TODO: Handle starting/stopping Rx */
131 static void pcn32_stop_rx(struct net_device *dev)
133 // UBYTE *base = get_hwbase(dev);
135 D(bug("%s: pcn32_stop_rx\n", dev->pcnu_name));
136 /* TODO: Handle starting/stopping Rx */
139 static void pcn32_start_tx(struct net_device *dev)
141 // UBYTE *base = get_hwbase(dev);
143 D(bug("%s: pcn32_start_tx()\n", dev->pcnu_name));
144 /* TODO: Handle starting/stopping Tx */
147 static void pcn32_stop_tx(struct net_device *dev)
149 // UBYTE *base = get_hwbase(dev);
151 D(bug("%s: pcn32_stop_tx()\n", dev->pcnu_name));
152 /* TODO: Handle starting/stopping Tx */
155 static void pcn32_txrx_reset(struct net_device *dev)
157 // struct fe_priv *np = get_pcnpriv(dev);
158 // UBYTE *base = get_hwbase(dev);
160 D(bug("%s: pcn32_txrx_reset()\n", dev->pcnu_name));
162 #endif
165 * pcn32_set_multicast: dev->set_multicast function
166 * Called with dev->xmit_lock held.
168 static void pcn32_set_multicast(struct net_device *dev)
170 // struct fe_priv *np = get_pcnpriv(dev);
171 // UBYTE *base = get_hwbase(dev);
172 ULONG addr[2];
173 ULONG mask[2];
174 // ULONG pff;
176 D(bug("%s: pcn32_set_multicast()\n", dev->pcnu_name));
178 memset(addr, 0, sizeof(addr));
179 memset(mask, 0, sizeof(mask));
182 static void pcnet32_deinitialize(struct net_device *dev)
184 dev->write_csr(dev->pcnu_BaseMem, 0, (1 << 2)); /* Stop the PCnet32 by setting the stop bit.. */
185 D(bug("%s: PCnet32 chipset STOPPED\n", dev->pcnu_name));
187 dev->write_bcr(dev->pcnu_BaseMem, 20, 4); /* Set pcnet32 into 16bit mode */
188 D(bug("%s: Chipset put into 16bit mode\n", dev->pcnu_name));
191 static void pcnet32_initialize(struct net_device *dev)
193 struct fe_priv *np = dev->pcnu_fe_priv;
194 UBYTE *base = get_hwbase(dev);
195 // int i;
197 dev->reset(base); /* Cause the PCnet Chipset to reset */
198 D(bug("%s: Chipset RESET\n", dev->pcnu_name));
200 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
202 np->ring_addr = HIDD_PCIDriver_AllocPCIMem(
203 dev->pcnu_PCIDriver,
204 sizeof(struct rx_ring_desc) * (RX_RING_SIZE + TX_RING_SIZE));
206 np->fep_pcnet_init_block->rx_ring = AROS_LONG2LE((IPTR)np->ring_addr);
207 np->fep_pcnet_init_block->tx_ring = AROS_LONG2LE((IPTR)&((struct rx_ring_desc *)np->ring_addr)[RX_RING_SIZE]);
209 D(bug("%s: Allocated IO Rings [%d x Tx @ %x : %x] [%d x Rx @ %x : %x]\n",
210 dev->pcnu_name,
211 TX_RING_SIZE, np->ring_addr + (RX_RING_SIZE * sizeof(struct rx_ring_desc)), np->fep_pcnet_init_block->tx_ring,
212 RX_RING_SIZE, np->ring_addr, np->fep_pcnet_init_block->rx_ring));
214 dev->write_bcr(dev->pcnu_BaseMem, 20, 2); /* Set pcnet32 into 32bit mode */
215 D(bug("%s: PCnet Chipset put into 32bit mode\n", dev->pcnu_name));
217 np->orig_mac[0] = ( (readb(base + 0) << 24) | (readb(base + 1) << 16) | (readb(base + 2) << 8) | readb(base + 3) );
218 np->orig_mac[1] = ( (readb(base + 4) << 8) | readb(base + 5) );
220 dev->pcnu_dev_addr[0] = dev->pcnu_org_addr[0] = (np->orig_mac[0] >> 24) & 0xff;
221 dev->pcnu_dev_addr[1] = dev->pcnu_org_addr[1] = (np->orig_mac[0] >> 16) & 0xff;
222 dev->pcnu_dev_addr[2] = dev->pcnu_org_addr[2] = (np->orig_mac[0] >> 8) & 0xff;
223 dev->pcnu_dev_addr[3] = dev->pcnu_org_addr[3] = (np->orig_mac[0] >> 0) & 0xff;
225 dev->pcnu_dev_addr[4] = dev->pcnu_org_addr[4] = (np->orig_mac[1] >> 8) & 0xff;
226 dev->pcnu_dev_addr[5] = dev->pcnu_org_addr[5] = (np->orig_mac[1] >> 0) & 0xff;
228 D(bug("%s: MAC Address %02x:%02x:%02x:%02x:%02x:%02x\n", dev->pcnu_name,
229 dev->pcnu_dev_addr[0], dev->pcnu_dev_addr[1], dev->pcnu_dev_addr[2],
230 dev->pcnu_dev_addr[3], dev->pcnu_dev_addr[4], dev->pcnu_dev_addr[5]));
233 static void pcn32_drain_tx(struct net_device *dev)
235 // struct fe_priv *np = get_pcnpriv(dev);
236 int i;
237 for (i = 0; i < TX_RING_SIZE; i++) {
238 /* TODO: pcn32_drain_tx does nothing atm. */
239 // np->fep_pcnet_init_block->tx_ring[i].FlagLen = 0;
243 static void pcn32_drain_rx(struct net_device *dev)
245 // struct fe_priv *np = get_pcnpriv(dev);
246 int i;
247 for (i = 0; i < RX_RING_SIZE; i++) {
248 /* TODO: pcn32_drain_rx does nothing atm. */
249 // np->fep_pcnet_init_block->rx_ring[i].FlagLen = 0;
254 static void drain_ring(struct net_device *dev)
256 pcn32_drain_tx(dev);
257 pcn32_drain_rx(dev);
260 static int request_irq(struct net_device *dev)
262 D(bug("%s: request_irq()\n", dev->pcnu_name));
264 if (!dev->pcnu_IntsAdded)
266 AddIntServer(INTB_KERNEL + dev->pcnu_IRQ, &dev->pcnu_irqhandler);
267 AddIntServer(INTB_VERTB, &dev->pcnu_touthandler);
268 dev->pcnu_IntsAdded = TRUE;
271 return 0;
274 static void free_irq(struct net_device *dev)
276 if (dev->pcnu_IntsAdded)
278 RemIntServer(INTB_KERNEL + dev->pcnu_IRQ, &dev->pcnu_irqhandler);
279 RemIntServer(INTB_VERTB, &dev->pcnu_touthandler);
280 dev->pcnu_IntsAdded = FALSE;
284 static void pcnet32_set_mac(struct net_device *dev)
286 // UBYTE *base = get_hwbase(dev);
287 int i;
289 for (i = 0; i < 6; i++) // Copy MAC Address to init block
291 dev->pcnu_fe_priv->fep_pcnet_init_block->phys_addr[i] = dev->pcnu_dev_addr[i];
295 static int pcnet32_open(struct net_device *dev)
297 struct fe_priv *np = get_pcnpriv(dev);
298 int ret, oom, i;
300 oom = 0;
302 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
304 np->rx_buffer = HIDD_PCIDriver_AllocPCIMem(
305 dev->pcnu_PCIDriver,
306 RX_RING_SIZE * RXTX_ALLOC_BUFSIZE);
308 if (np->rx_buffer == NULL)
309 oom = 1;
311 np->tx_buffer = HIDD_PCIDriver_AllocPCIMem(
312 dev->pcnu_PCIDriver,
313 TX_RING_SIZE * RXTX_ALLOC_BUFSIZE);
315 if (np->tx_buffer == NULL)
316 oom = 1;
318 D(bug("%s: pcnet32_open: begin\n",dev->pcnu_name));
320 if (oom == 0)
322 D(bug("%s: pcnet32_open: Allocated IO Buffers [ %d x Tx @ %x] [ %d x Rx @ %x]\n",dev->pcnu_name,
323 TX_RING_SIZE, np->tx_buffer,
324 RX_RING_SIZE, np->rx_buffer ));
327 np->fep_pcnet_init_block->mode = AROS_WORD2LE(0x0003); /* Disable Rx and Tx */
328 np->fep_pcnet_init_block->tlen_rlen = AROS_WORD2LE(TX_RING_LEN_BITS | RX_RING_LEN_BITS);
330 D(bug("%s: pcnet32_open: Interrupts disabled\n",dev->pcnu_name));
332 pcnet32_set_mac(dev);
334 D(bug("%s: pcnet32_open: copied MAC address\n",dev->pcnu_name));
336 np->fep_pcnet_init_block->filter[0] = 0xffffffff;
337 np->fep_pcnet_init_block->filter[1] = 0xffffffff;
339 D(bug("%s: pcnet32_open: reset filter\n"));
341 ret = request_irq(dev);
342 if (ret)
343 goto out_drain;
345 for (i=0; i < TX_RING_SIZE; i++)
347 ((struct tx_ring_desc *)np->ring_addr)[i + RX_RING_SIZE].PacketBuffer = 0;
348 ((struct tx_ring_desc *)np->ring_addr)[i + RX_RING_SIZE].BufferStatus = 0;
351 D(bug("%s: pcnet32_open: Tx Ring initialised\n",dev->pcnu_name));
353 for (i=0; i < RX_RING_SIZE; i++)
355 ((struct rx_ring_desc *)np->ring_addr)[i].PacketBuffer = AROS_LONG2LE((IPTR)&np->rx_buffer[i]);
356 ((struct rx_ring_desc *)np->ring_addr)[i].BufferLength = AROS_WORD2LE(-RXTX_ALLOC_BUFSIZE);
357 ((struct rx_ring_desc *)np->ring_addr)[i].BufferStatus = AROS_WORD2LE((1 << 8)|(1 << 9)|(1 << 15));
360 D(bug("%s: pcnet32_open: Rx Ring initialised\n",dev->pcnu_name));
362 dev->write_bcr(dev->pcnu_BaseMem, 20, 2); /* Set pcnet32 into 32bit mode */
363 D(bug("%s: PCnet Chipset put into 32bit mode\n", dev->pcnu_name));
365 dev->write_csr(dev->pcnu_BaseMem, 1, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) & 0xffff)); /* Store the pointer to the pcnet32_init_block */
366 dev->write_csr(dev->pcnu_BaseMem, 2, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) >> 16));
368 D(bug("%s: pcnet32_open: set init_block (@ %x)\n",dev->pcnu_name,np->fep_pcnet_init_block));
370 dev->write_csr(dev->pcnu_BaseMem, 0, ((1 << 6)|(1 << 0))); /* Trigger an initialisation for the interrupt (INEA|INIT)*/
372 D(bug("%s: pcnet32_open: triggered init int\n",dev->pcnu_name));
374 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
376 dev->write_bcr(dev->pcnu_BaseMem, 20, 2); /* Set pcnet32 into 32bit mode */
377 D(bug("%s: PCnet Chipset put into 32bit mode\n", dev->pcnu_name));
379 dev->write_bcr(dev->pcnu_BaseMem, 2, ((dev->read_bcr(dev->pcnu_BaseMem, 2) & ~2) | 2)); /* Set autoselect bit */
381 D(bug("%s: pcnet32_open: autoselect bit set\n",dev->pcnu_name));
383 /** Handle Link setup **/
385 if ((dev->pcnu_pcnet_supported & support_mii) \
386 // && (!(option & enable_autoneg))
389 dev->write_bcr(dev->pcnu_BaseMem, 32, ((dev->read_bcr(dev->pcnu_BaseMem, 32) & ~0x38) | 0x10 | 0x08 ));
390 D(bug("%s: pcnet32_open: Chipset set into AutoNeg:OFF FullDuplex:ON LinkSpeed:100\n",dev->pcnu_name));
392 else
394 dev->write_bcr(dev->pcnu_BaseMem, 32, ((dev->read_bcr(dev->pcnu_BaseMem, 32) & ~0x98) | 0x20 ));
395 D(bug("%s: pcnet32_open: Chipset set into AutoNeg:ON\n",dev->pcnu_name));
398 /** Set GPSI bit in test register **/
400 /** Handle Transmit stop on underflow **/
401 if (dev->pcnu_pcnet_supported & support_dxsuflo)
403 dev->write_csr(dev->pcnu_BaseMem, 3, (dev->read_csr(dev->pcnu_BaseMem, 3) | 0x40));
404 D(bug("%s: pcnet32_open: Enabled Tx_STOP_ON_UNDERFLOW\n",dev->pcnu_name));
407 /** Enable TxDone intr inhibitor **/
408 if (dev->pcnu_pcnet_supported & support_ltint)
410 dev->write_csr(dev->pcnu_BaseMem, 5, (dev->read_csr(dev->pcnu_BaseMem, 5) | (1 << 14)));
411 D(bug("%s: pcnet32_open: Enabled Tx_DONE_INTR_INHIBITOR\n",dev->pcnu_name));
414 np->fep_pcnet_init_block->mode = 0x0000; /* Enable Rx and Tx */
416 D(bug("%s: pcnet32_open: Enable Rx & Tx\n",dev->pcnu_name));
418 dev->write_csr(dev->pcnu_BaseMem, 1, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) & 0xffff)); /* Re initialise the PCnet chipset */
419 dev->write_csr(dev->pcnu_BaseMem, 2, (AROS_LONG2LE((IPTR)np->fep_pcnet_init_block) >> 16));
421 D(bug("%s: pcnet32_open: re-initialised chipset\n",dev->pcnu_name));
423 dev->write_csr(dev->pcnu_BaseMem, 4, 0x0915); /* ? */
424 dev->write_csr(dev->pcnu_BaseMem, 0, ((1 << 6)|(1 << 1)|(1 << 0)));
426 i = 0;
427 while (i++ < 100)
428 if (dev->read_csr(dev->pcnu_BaseMem, 0) & 0x0100)
429 break;
431 D(bug("%s: pcnet32_open: chipset ENABLED, csr[0] = %x\n",dev->pcnu_name, dev->read_csr(dev->pcnu_BaseMem, 0)));
433 dev->pcnu_flags |= IFF_UP;
434 ReportEvents(LIBBASE, dev, S2EVENT_ONLINE);
435 D(bug("%s: pcnet32_open: Device set as ONLINE\n",dev->pcnu_name));
437 return 0;
439 out_drain:
440 drain_ring(dev);
441 return ret;
444 static int pcn32_close(struct net_device *dev)
446 struct fe_priv *np = get_pcnpriv(dev);
448 dev->pcnu_flags &= ~IFF_UP;
450 ObtainSemaphore(&np->lock);
451 np->in_shutdown = 1;
452 ReleaseSemaphore(&np->lock);
454 dev->pcnu_toutNEED = FALSE;
456 netif_stop_queue(dev);
457 ObtainSemaphore(&np->lock);
459 pcnet32_deinitialize(dev); // Stop the chipset and set it in 16bit-mode
461 ReleaseSemaphore(&np->lock);
463 free_irq(dev);
465 drain_ring(dev);
467 HIDD_PCIDriver_FreePCIMem(dev->pcnu_PCIDriver, np->rx_buffer);
468 HIDD_PCIDriver_FreePCIMem(dev->pcnu_PCIDriver, np->tx_buffer);
470 ReportEvents(LIBBASE, dev, S2EVENT_OFFLINE);
472 return 0;
476 void pcn32_get_functions(struct net_device *Unit)
478 Unit->initialize = pcnet32_initialize;
479 Unit->deinitialize = pcnet32_deinitialize;
480 Unit->start = pcnet32_open;
481 Unit->stop = pcn32_close;
482 Unit->set_mac_address = pcnet32_set_mac;
483 Unit->set_multicast = pcn32_set_multicast;