1 /* National Semiconductor dp83815 driver
2 * Copyright (c) 2006 Urias McCullough (umccullough@gmail.com)
3 * Portions of code Copyright (c) 2003-2004, Niels Sascha Reedijk
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Portions of code based on dp83815 driver by: Antonio Carpio (BolivianTONE@nc.rr.com)
25 * Portions of code may be: Copyright (c) 1998, 1999 Be, Inc. All Rights Reserved under terms of Be Sample Code License.
28 #include <KernelExport.h>
36 #include "ether_driver.h"
38 #include "dp83815_regs.h"
40 #define kDevName "dp83815"
41 #define kDevDir "net/" kDevName "/"
43 #define MAX_DESC 128 /* eventually going into config file */
44 #define BUFFER_SIZE 2048 /* make it easy to do math and whatnot */
45 #define MAX_PACKET_SIZE 1518 /* can be 2046 if RXCFG has ALP set */
46 #define NONBLOCK_WAIT 1000
50 # define TRACE(x) dprintf x
55 typedef struct supported_device
{
61 static supported_device_t m_supported_devices
[] = {
62 { 0x100B, 0x0020, "National Semiconductor dp83815 (Netgear 311/312)" },
63 { 0, 0, NULL
} /* End of list */
66 int32 api_version
= B_CUR_DRIVER_API_VERSION
; //Procedure
68 typedef struct descriptor
{
69 uint32 link
; /* Physical address of next descriptor */
70 volatile uint32 cmd
; /* info to/from NIC */
71 volatile uint32 ptr
; /* Physical address of the buffer */
72 struct descriptor
*virt_next
; /* virtual address of next descriptor */
73 void *virt_buff
; /* virtual address of the buffer */
76 typedef struct desc_ring
{
77 descriptor_t
*Curr
; /* Current descriptor to be accessed */
78 sem_id Sem
; /* Queue for outstanding reads/writes */
79 spinlock Lock
; /* Spinlock used to grab a descriptor */
80 descriptor_t
*CurrInt
; /* Interrupt Hook's Current Descriptor */
83 typedef struct stats
{
95 typedef struct dp83815_properties
97 pci_info
*pcii
; /* Pointer to PCI Info for the device */
98 uint32 reg_base
; /* Base address for registers */
99 area_id ioarea
; /* PPC: Area where the mmaped registers are */
102 uint8 device_id
; /* Which device id this is... */
104 ether_address_t address
; /* holds the MAC address */
105 sem_id lock
; /* lock this structure: still interrupt */
106 int32 blockFlag
; /* for blocking or nonblocking reads */
110 } dp83815_properties_t
;
112 static pci_info
*m_devices
[MAX_CARDS
];
113 static pci_module_info
*m_pcimodule
= 0; //To call methods of pci
114 static char *dp83815_names
[MAX_CARDS
+ 1];
115 static int32 m_openmask
= 0; //Is the thing already opened?
116 static uint32
pages_needed(uint32 mem_size
);
117 static int32
dp83815_interrupt_hook(void *data
); /* interrupt handler */
119 static status_t
allocate_resources(dp83815_properties_t
*data
); /* allocate semaphores & spinlocks */
120 static void free_resources(dp83815_properties_t
*data
); /* deallocate semaphores & spinlocks */
121 static status_t
init_ring_buffers(dp83815_properties_t
*data
); /* allocate and initialize frame buffer rings */
122 static status_t
domulti(dp83815_properties_t
*data
, uint8
*addr
); /* add multicast address to hardware filter list */
123 static status_t
free_hook(void *cookie
);
124 static status_t
close_hook(void *);
127 #define write8( offset , value) (m_pcimodule->write_io_8 ((data->reg_base + (offset)), (value) ) )
128 #define write16( offset , value) (m_pcimodule->write_io_16((data->reg_base + (offset)), (value) ) )
129 #define write32( offset , value) (m_pcimodule->write_io_32((data->reg_base + (offset)), (value) ) )
131 #define read8( offset ) (m_pcimodule->read_io_8 ((data->reg_base + offset)))
132 #define read16( offset ) (m_pcimodule->read_io_16((data->reg_base + offset)))
133 #define read32( offset ) (m_pcimodule->read_io_32((data->reg_base + offset)))
136 dp83815_init_registers(dp83815_properties_t
*data
)
138 data
->reg_base
= data
->pcii
->u
.h0
.base_registers
[0];
141 #include <ByteOrder.h>
142 #define write8( offset , value) (*((volatile uint8 *)(data->reg_base + (offset))) = (value))
143 #define write16( offset , value) (*((volatile uint8 *)(data->reg_base + (offset))) = B_HOST_TO_LENDIAN_INT16(value))
144 #define write32( offset , value) (*((volatile uint8 *)(data->reg_base + (offset))) = B_HOST_TO_LENDIAN_INT32(value))
146 #define read8( offset ) (*((volatile uint8*)(data->reg_base + (offset))))
147 #define read16( offset ) B_LENDIAN_TO_HOST_INT16(*((volatile uint16*)(data->reg_base + (offset))))
148 #define read32( offset ) B_LENDIAN_TO_HOST_INT32(*((volatile uint32*)(data->reg_base + (offset))))
151 dp83815_init_registers(rtl8139_properties_t
*data
)
153 int32 base
, size
, offset
;
154 base
= data
->pcii
->u
.h0
.base_registers
[0];
155 size
= data
->pcii
->u
.h0
.base_register_sizes
[0];
157 /* Round down to nearest page boundary */
158 base
= base
& ~(B_PAGE_SIZE
- 1);
160 /* Adjust the size */
161 offset
= data
->pcii
->u
.h0
.base_registers
[0] - base
;
163 size
= (size
+ (B_PAGE_SIZE
- 1)) & ~(B_PAGE_SIZE
- 1);
165 TRACE(( kDevName
" _open_hook(): PCI base=%lx size=%lx offset=%lx\n",
166 base
, size
, offset
));
168 data
->ioarea
= map_physical_memory(kDevName
" Regs", base
, size
,
169 B_ANY_KERNEL_ADDRESS
, B_READ_AREA
| B_WRITE_AREA
,
170 (void **)&data
->reg_base
);
172 data
->reg_base
= data
->reg_base
+ offset
;
178 pages_needed(uint32 mem_size
)
180 uint32 pages
= mem_size
/ B_PAGE_SIZE
;
181 if (pages
% B_PAGE_SIZE
!= 0)
190 // Nielx: no special requirements here...
191 TRACE(( kDevName
": init_hardware\n" ));
199 status_t status
; //Storage for statuses
200 pci_info
*item
; //Storage used while looking through pci
201 int32 i
, found
; //Counter
203 TRACE(( kDevName
": init_driver()\n" ));
205 // Try if the PCI module is loaded (it would be weird if it wouldn't,
207 if ((status
= get_module(B_PCI_MODULE_NAME
, (module_info
**)&m_pcimodule
))
209 TRACE((kDevName
" init_driver(): Get PCI module failed! %lu \n",
215 item
= (pci_info
*)malloc(sizeof(pci_info
));
216 for (i
= found
= 0; m_pcimodule
->get_nth_pci_info(i
, item
) == B_OK
; i
++) {
217 supported_device_t
*supported
;
219 for (supported
= m_supported_devices
; supported
->name
; supported
++) {
220 if ((item
->vendor_id
== supported
->vendor_id
)
221 && (item
->device_id
== supported
->device_id
)) {
222 //Also done in etherpci sample code
223 if ((item
->u
.h0
.interrupt_line
== 0)
224 || (item
->u
.h0
.interrupt_line
== 0xFF)) {
225 TRACE(( kDevName
" init_driver(): found %s with invalid IRQ"
226 " - check IRQ assignement\n", supported
->name
));
230 TRACE(( kDevName
" init_driver(): found %s at IRQ %u \n",
231 supported
->name
, item
->u
.h0
.interrupt_line
));
232 m_devices
[found
] = item
;
233 item
= (pci_info
*)malloc(sizeof(pci_info
));
242 //Check if we have found any devices:
244 TRACE(( kDevName
" init_driver(): no device found\n" ));
245 put_module(B_PCI_MODULE_NAME
); //dereference module
249 //Create the devices list
253 for (i
= 0; i
< found
; i
++) {
254 snprintf(name
, 32, "%s%ld", kDevDir
, i
);
255 dp83815_names
[i
] = strdup(name
);
257 dp83815_names
[i
] = NULL
;
264 uninit_driver - optional function - called every time the driver
272 TRACE(( kDevName
": uninit_driver()\n" ));
274 for (index
= 0; (item
= dp83815_names
[index
]) != NULL
; index
++)
277 free(m_devices
[index
]);
280 put_module(B_PCI_MODULE_NAME
);
285 open_hook(const char *name
, uint32 flags
, void** cookie
)
288 dp83815_properties_t
*data
;
294 TRACE(( kDevName
" open_hook()\n" ));
296 // verify device access
301 // search for device name
302 for (temp8
= 0; (thisName
= dp83815_names
[temp8
]) != NULL
; temp8
++) {
303 if (!strcmp(name
, thisName
))
309 // check if device is already open
311 if (atomic_or(&m_openmask
, mask
) & mask
)
315 //Create a structure that contains the internals
316 if (!(*cookie
= data
=
317 (dp83815_properties_t
*)malloc(sizeof(dp83815_properties_t
)))) {
318 TRACE(( kDevName
" open_hook(): Out of memory\n" ));
322 //Set status to open:
323 m_openmask
&= ~(1L << temp8
);
326 memset(data
, 0, sizeof(dp83815_properties_t
));
329 data
->device_id
= temp8
;
332 data
->lock
= create_sem(1, kDevName
" data protect");
333 set_sem_owner(data
->lock
, B_SYSTEM_TEAM
);
334 data
->Rx
.Sem
= create_sem(0, kDevName
" read wait");
335 set_sem_owner(data
->Rx
.Sem
, B_SYSTEM_TEAM
);
336 data
->Tx
.Sem
= create_sem(1, kDevName
" write wait");
337 set_sem_owner(data
->Tx
.Sem
, B_SYSTEM_TEAM
);
340 data
->pcii
= m_devices
[data
->device_id
];
342 //Enable the registers
343 dp83815_init_registers(data
);
345 /* enable pci address access */
346 cmd
= m_pcimodule
->read_pci_config(data
->pcii
->bus
, data
->pcii
->device
,
347 data
->pcii
->function
, PCI_command
, 2);
348 cmd
= cmd
| PCI_command_io
| PCI_command_master
| PCI_command_memory
;
349 m_pcimodule
->write_pci_config(data
->pcii
->bus
, data
->pcii
->device
,
350 data
->pcii
->function
, PCI_command
, 2, cmd
);
352 if (allocate_resources(data
) != B_OK
)
355 /* We want interrupts! */
356 if (install_io_interrupt_handler(data
->pcii
->u
.h0
.interrupt_line
,
357 dp83815_interrupt_hook
, data
, 0) != B_OK
) {
358 TRACE((kDevName
" open_hook(): Error installing interrupt handler\n"));
363 temp32
= read32(REG_SRR
);
364 TRACE(( "SRR: %x\n", temp32
));
367 write32(REG_CR
, CR_RXR
|CR_TXR
); /* Reset Tx & Rx */
369 if (init_ring_buffers(data
) != B_OK
) /* Init ring buffers */
372 write32(REG_RFCR
, RFCR_RFEN
| RFCR_AAB
| RFCR_AAM
| RFCR_AAU
);
374 write32(REG_RXCFG
, RXCFG_ATP
| RXCFG_DRTH(31)); /* Set the drth */
376 write32(REG_TXCFG
, TXCFG_CSI
|
383 write32(REG_IMR
, ISR_RXIDLE
| ISR_TXOK
| ISR_RXOK
);
385 write32(REG_CR
, CR_RXE
); /* Enable Rx */
386 write32(REG_IER
, 1); /* Enable interrupts */
391 free_resources(data
);
398 read_hook (void* cookie
, off_t position
, void *buf
, size_t* num_bytes
)
400 dp83815_properties_t
*data
= cookie
;
405 TRACE(( kDevName
": read_hook()\n" ));
407 //if( !data->nonblocking )
408 acquire_sem_etc(data
->Rx
.Sem
, 1, B_CAN_INTERRUPT
| data
->blockFlag
,
412 former
= disable_interrupts();
413 acquire_spinlock(&data
->Rx
.Lock
);
414 desc
= data
->Rx
.Curr
;
415 data
->Rx
.Curr
= desc
->virt_next
;
416 release_spinlock(&data
->Rx
.Lock
);
417 restore_interrupts(former
);
420 length
= DESC_LENGTH
&desc
->cmd
;
422 if (desc
->cmd
& (DESC_RXA
| DESC_RXO
| DESC_LONG
| DESC_RUNT
| DESC_ISE
423 | DESC_CRCE
| DESC_FAE
| DESC_LBP
| DESC_COL
))
424 TRACE(( "desc cmd: %x\n", desc
->cmd
));
431 if (*num_bytes
< length
)
434 memcpy(buf
, desc
->virt_buff
, length
);
435 desc
->cmd
= DESC_LENGTH
&MAX_PACKET_SIZE
;
438 atomic_add(&data
->stats
.rx_att
, 1);
448 write_hook(void* cookie
, off_t position
, const void* buffer
, size_t* num_bytes
)
450 dp83815_properties_t
*data
= cookie
;
454 TRACE(( kDevName
" write_hook()\n" ));
456 acquire_sem(data
->lock
);
457 acquire_sem_etc(data
->Tx
.Sem
, 1, B_CAN_INTERRUPT
| data
->blockFlag
,
461 former
= disable_interrupts();
462 acquire_spinlock(&data
->Tx
.Lock
);
463 desc
= data
->Tx
.Curr
;
464 data
->Tx
.Curr
= desc
->virt_next
;
465 release_spinlock(&data
->Tx
.Lock
);
466 restore_interrupts(former
);
469 if (*num_bytes
> MAX_PACKET_SIZE
) { /* if needed */
470 TRACE(( "Had to truncate the packet from %d to %d\n", *num_bytes
,
472 *num_bytes
= MAX_PACKET_SIZE
; /* truncate the packet */
476 while (desc
->cmd
& DESC_OWN
) { /* make sure a buffer is available */
477 TRACE(( "spinning in the write hook\n"));
478 spin(1000); /* wait a while if not */
481 memcpy(desc
->virt_buff
, buffer
, *num_bytes
); /* now copy the data */
482 desc
->cmd
= DESC_OWN
| *num_bytes
; /* update the cmd bits */
484 write32(REG_CR
, CR_TXE
); /* tell the card to start tx */
485 atomic_add(&data
->stats
.tx_att
, 1);
487 release_sem_etc(data
->lock
, 1, B_DO_NOT_RESCHEDULE
);
494 control_hook (void* cookie
, uint32 op
, void* arg
, size_t len
)
496 dp83815_properties_t
*data
= (dp83815_properties_t
*)cookie
;
497 TRACE(( kDevName
" control_hook()\n" ));
501 TRACE((kDevName
" control_hook(): Wants us to init... ;-)\n"));
508 TRACE((kDevName
" control_hook(): Wants our address...\n"));
509 memcpy(arg
, (void *) &(data
->address
), sizeof(ether_address_t
));
513 return domulti(data
, (unsigned char *)arg
);
519 TRACE((kDevName
" control_hook(): Wants to set block/nonblock\n"));
522 data
->blockFlag
= B_RELATIVE_TIMEOUT
;
529 TRACE((kDevName
" control_hook(): Wants REMMULTI\n"));
532 case ETHER_SETPROMISC
:
533 TRACE((kDevName
" control_hook(): Wants PROMISC\n"));
536 case ETHER_GETFRAMESIZE
:
537 TRACE((kDevName
" control_hook(): Wants GETFRAMESIZE\n"));
538 *((unsigned int *)arg
) = 1514;
547 dp83815_interrupt_hook(void *cookie
)
549 dp83815_properties_t
*data
= (dp83815_properties_t
*) cookie
;
551 isr
= read32(REG_ISR
);
554 return B_UNHANDLED_INTERRUPT
;
556 if (isr
& ISR_RXOK
) {
558 descriptor_t
*curr
= data
->Rx
.CurrInt
;
560 while (curr
->cmd
& DESC_OWN
) {
561 curr
= curr
->virt_next
;
565 data
->Rx
.CurrInt
= curr
;
566 data
->stats
.rx_ok
+= num_packets
;
568 TRACE(( "received %d descriptors\n", num_packets
));
570 release_sem_etc(data
->Rx
.Sem
, num_packets
, B_DO_NOT_RESCHEDULE
);
573 if (isr
& ISR_TXOK
) {
575 release_sem_etc(data
->Tx
.Sem
, 1, B_DO_NOT_RESCHEDULE
);
578 if (isr
& ISR_RXIDLE
)
579 TRACE(("RX IS IDLE!\n"));
581 if (isr
& ~(ISR_TXIDLE
| ISR_TXOK
| ISR_RXOK
| ISR_RXIDLE
| ISR_RXEARLY
))
582 TRACE(("ISR: %x\n", isr
));
584 return B_INVOKE_SCHEDULER
;
589 close_hook(void* cookie
)
591 dp83815_properties_t
* data
= (dp83815_properties_t
*) cookie
;
593 write32(REG_IER
, 0); /* Disable interrupts */
594 write32(REG_CR
, CR_RXD
| CR_TXD
); /* Disable Rx & Tx */
601 free_hook(void* cookie
)
603 dp83815_properties_t
*data
= (dp83815_properties_t
*) cookie
;
605 TRACE(( kDevName
" free_hook()\n" ));
607 while (data
->Tx
.Lock
); /* wait for any current writes to finish */
608 while (data
->Rx
.Lock
); /* wait for any current reads to finish */
610 //Remove interrupt handler
611 remove_io_interrupt_handler(data
->pcii
->u
.h0
.interrupt_line
,
612 dp83815_interrupt_hook
, cookie
);
614 m_openmask
&= ~(1L << data
->device_id
);
616 free_resources(data
); /* unblock waiting threads */
618 //Finally, free the cookie
622 put_module(B_PCI_MODULE_NAME
);
628 device_hooks dp83815_hooks
= {
629 open_hook
, /* -> open entry point */
630 close_hook
, /* -> close entry point */
631 free_hook
, /* -> free cookie */
632 control_hook
, /* -> control entry point */
633 read_hook
, /* -> read entry point */
634 write_hook
/* -> write entry point */
641 return (const char **)dp83815_names
;
646 find_device(const char* name
)
648 return &dp83815_hooks
;
653 init_ring_buffers(dp83815_properties_t
*data
)
657 physical_entry map
[2];
660 descriptor_t
*RxDescRing
= NULL
;
661 descriptor_t
*TxDescRing
= NULL
;
663 descriptor_t
*desc_base_virt_addr
;
664 uint32 desc_base_phys_addr
;
666 void *buff_base_virt_addr
;
667 uint32 buff_base_phys_addr
;
672 #define NUM_BUFFS 2*MAX_DESC
674 pages
= pages_needed(2 * MAX_DESC
* sizeof(descriptor_t
)
675 + NUM_BUFFS
* BUFFER_SIZE
);
677 data
->mem_area
= create_area(kDevName
" desc buffer", (void**)&RxDescRing
,
678 B_ANY_KERNEL_ADDRESS
, pages
* B_PAGE_SIZE
, B_32_BIT_CONTIGUOUS
,
679 B_READ_AREA
| B_WRITE_AREA
);
680 if (data
->mem_area
< 0)
683 get_area_info(data
->mem_area
, &info
);
684 get_memory_map(info
.address
, info
.size
, map
, 4);
686 desc_base_phys_addr
= map
[0].address
+ NUM_BUFFS
* BUFFER_SIZE
;
687 desc_base_virt_addr
= info
.address
+ NUM_BUFFS
* BUFFER_SIZE
;
689 buff_base_phys_addr
= map
[0].address
;
690 buff_base_virt_addr
= info
.address
;
692 RxDescRing
= desc_base_virt_addr
;
693 for (i
= 0; i
< MAX_DESC
; i
++) {
694 RxDescRing
[i
].link
= desc_base_phys_addr
695 + ((i
+ 1) % MAX_DESC
) * sizeof(descriptor_t
);
696 RxDescRing
[i
].cmd
= MAX_PACKET_SIZE
;
697 RxDescRing
[i
].ptr
= buff_base_phys_addr
+ i
* BUFFER_SIZE
;
698 RxDescRing
[i
].virt_next
= &RxDescRing
[(i
+ 1) % MAX_DESC
];
699 RxDescRing
[i
].virt_buff
= buff_base_virt_addr
+ i
* BUFFER_SIZE
;
702 TxDescRing
= desc_base_virt_addr
+ MAX_DESC
;
703 for (i
= 0; i
< MAX_DESC
; i
++) {
704 TxDescRing
[i
].link
= desc_base_phys_addr
705 + MAX_DESC
* sizeof(descriptor_t
)
706 + ((i
+ 1) % MAX_DESC
) * sizeof(descriptor_t
);
707 TxDescRing
[i
].cmd
= MAX_PACKET_SIZE
;
708 TxDescRing
[i
].ptr
= buff_base_phys_addr
709 + ((i
+ MAX_DESC
) * BUFFER_SIZE
);
710 TxDescRing
[i
].virt_next
= &TxDescRing
[(i
+ 1) % MAX_DESC
];
711 TxDescRing
[i
].virt_buff
= buff_base_virt_addr
712 + ((i
+ MAX_DESC
) * BUFFER_SIZE
);
715 data
->Rx
.Curr
= RxDescRing
;
716 data
->Tx
.Curr
= TxDescRing
;
718 data
->Rx
.CurrInt
= RxDescRing
;
719 data
->Tx
.CurrInt
= TxDescRing
;
722 write32(REG_RXDP
, desc_base_phys_addr
); /* set the initial rx descriptor */
724 i
= desc_base_phys_addr
+ MAX_DESC
* sizeof(descriptor_t
);
725 write32(REG_TXDP
, i
); /* set the initial tx descriptor */
732 allocate_resources(dp83815_properties_t
*data
)
734 /* intialize rx semaphore with zero received packets */
735 if ((data
->Rx
.Sem
= create_sem(0, kDevName
" rx")) < 0) {
736 TRACE(( kDevName
" create rx sem failed %x \n", data
->Rx
.Sem
));
737 return (data
->Rx
.Sem
);
739 set_sem_owner(data
->Rx
.Sem
, B_SYSTEM_TEAM
);
742 /* intialize tx semaphore with the number of free tx buffers */
743 if ((data
->Tx
.Sem
= create_sem(MAX_DESC
, kDevName
" tx")) < 0) {
744 delete_sem(data
->Rx
.Sem
);
745 TRACE(( kDevName
" create read sem failed %x \n", data
->Tx
.Sem
));
746 return (data
->Tx
.Sem
);
749 set_sem_owner(data
->Tx
.Sem
, B_SYSTEM_TEAM
);
757 static void free_resources(dp83815_properties_t
*data
)
759 delete_sem(data
->Rx
.Sem
);
760 delete_sem(data
->Tx
.Sem
);
764 static status_t
domulti(dp83815_properties_t
*data
, uint8
*addr
)
766 TRACE(( "Set up multicast filter here\n"));