#include <ll/i386/hw-instr.h>
#include <ll/i386/hw-data.h>
#include <ll/i386/hw-arch.h>
#include <ll/i386/hw-io.h>
#include <ll/i386/cons.h>
#include <ll/i386/mem.h>
#include <ll/stdlib.h>
#include <drivers/llpci.h>
#include <drivers/pci.h>
#include <drivers/linuxpci.h>
#include <kernel/log.h>
//#define DEBUG_PCISCAN
static struct pci_dev pci_devs
[N_MAX_DEVS
];
static struct pci_bus pci_root
;
static struct pci_dev
*pci_devices
= NULL
;
#if 0
/* scan the bus to find all the connected devices */
DWORD pci_scan_bus
(struct pci_bus
*bus
)
{
int ndev
;
DWORD tmp
;
DWORD max
, class
;
BYTE hdr
;
int ok
;
WORD dev
;
struct pci_dev
**bus_last
;
struct pci_dev
*device
= NULL
;
int present
;
max
= bus
->secondary
;
bus_last
= &bus
->devices
;
if (pcibios_present
() == 0) return -1;
ndev
= 0;
for (dev
= 0; dev
<= 0xFF; dev
++) {
present
= 0;
if ((dev
& 0x07) == 0) {
present
= pcibios_read_config_byte
(bus
->number
, dev
, 0x0e, &hdr
);
}
if (hdr
& 0x80) {
present
= 1;
}
if (present
) {
ok
= pcibios_read_config_dword
(bus
->number
, dev
, 0, &tmp
);
if ( ok
&& (((tmp
& 0xFFFF) != 0xFFFF) && ((tmp
>> 16) != 0xFFFF))) {
/* Got a PCI Device!!! */
device
= &(pci_devs
[ndev
++]);
device
->bus
= bus
;
device
->devfn
= dev
;
device
->vendor
= tmp
& 0xffff;
device
->device
= (tmp
>> 16) & 0xffff;
/* PJ: non-destructively determine if device can be a master: */
pcibios_read_config_byte
(bus
->number
, devfn
, PCI_COMMAND
, &cmd
);
pcibios_write_config_byte
(bus
->number
, devfn
, PCI_COMMAND
, cmd
| PCI_COMMAND_MASTER
);
pcibios_read_config_byte
(bus
->number
, devfn
, PCI_COMMAND
, &tmp
);
dev
->master
= ((tmp
& PCI_COMMAND_MASTER
) != 0);
pcibios_write_config_byte
(bus
->number
, devfn
, PCI_COMMAND
, cmd
);
/* This is by Luca... We need to set base_addr */
pcibios_read_config_dword
(bus
->number
, dev
, PCI_BASE_ADDRESS_0
, &(device
->base_address
[0]));
pcibios_read_config_byte
(bus
->number
, dev
, PCI_INTERRUPT_LINE
, &irq1
);
device
->irq
= irq1
;
pcibios_read_config_dword
(bus
->number
, dev
, PCI_CLASS_REVISION
, &class
);
class
>>= 8; /* upper 3 bytes */
//printk(KERN_INFO "pci_scan_bus: dev=%d, class=%ld", dev, class);
device
->class
= class
;
class
>>= 8;
device
->hdr_type
= hdr
;
#if 0
switch (hdr
& 0x7f) { /* header type */
case PCI_HEADER_TYPE_NORMAL
: /* standard header */
if (class
== PCI_CLASS_BRIDGE_PCI
)
goto bad
;
/*
* If the card generates interrupts, read IRQ number
* (some architectures change it during pcibios_fixup())
*/
pcibios_read_config_byte
(bus
->number
, device
->devfn
, PCI_INTERRUPT_PIN
, &irq1
);
if (irq1
)
pcibios_read_config_byte
(bus
->number
, device
->devfn
, PCI_INTERRUPT_LINE
, &irq1
);
device
->irq
= irq1
;
/*
* read base address registers, again pcibios_fixup() can
* tweak these
*/
pci_read_bases
(device
, 6);
pcibios_read_config_dword
(bus
->number
, dev
, PCI_ROM_ADDRESS
, &l
);
device
->rom_address
= (l
== 0xffffffff) ? 0 : l
;
break;
case PCI_HEADER_TYPE_BRIDGE
: /* bridge header */
if (class
!= PCI_CLASS_BRIDGE_PCI
)
goto bad
;
pci_read_bases
(device
, 2);
pcibios_read_config_dword
(bus
->number
, dev
, PCI_ROM_ADDRESS1
, &l
);
device
->rom_address
= (l
== 0xffffffff) ? 0 : l
;
break;
case PCI_HEADER_TYPE_CARDBUS
: /* CardBus bridge header */
if (class
!= PCI_CLASS_BRIDGE_CARDBUS
)
goto bad
;
pci_read_bases
(device
, 1);
break;
default: /* unknown header */
bad
:
printk
(KERN_ERR
"PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
bus
->number
, device
->devfn
, device
->vendor
, device
->device
, class
, hdr
);
continue;
}
#endif
//printk(KERN_DEBUG "PCI: %02x:%02x [%04x/%04x]\n", bus->number, device->devfn, device->vendor, device->device);
device
->next
= pci_devices
;
pci_devices
= device
;
/*
* Now insert it into the list of devices held
* by the parent bus.
*/
*bus_last
= device
;
bus_last
= &device
->sibling
;
}
}
}
// PJ: PCI bridges not supported???
#if 0
for(device
= bus
->devices
; device
; device
= device
->sibling
) {
/*
* If it's a bridge, scan the bus behind it.
*/
if ((dev
->class
>> 8) == PCI_CLASS_BRIDGE_PCI
) {
unsigned int buses
;
unsigned int devfn
= dev
->devfn
;
unsigned short cr
;
/*
* Insert it into the tree of buses.
*/
child
= kmalloc
(sizeof(*child
), GFP_ATOMIC
);
if(child
==NULL
) {
#ifdef DEBUG_PCISCAN
printk
(KERN_ERR
"pci: out of memory for bridge.\n");
#endif
continue;
}
memset(child
, 0, sizeof(*child
));
child
->next
= bus
->children
;
bus
->children
= child
;
child
->self
= dev
;
child
->parent
= bus
;
/*
* Set up the primary, secondary and subordinate
* bus numbers.
*/
child
->number
= child
->secondary
= ++max
;
child
->primary
= bus
->secondary
;
child
->subordinate
= 0xff;
/*
* Clear all status bits and turn off memory,
* I/O and master enables.
*/
pcibios_read_config_word
(bus
->number
, devfn
, PCI_COMMAND
, &cr
);
pcibios_write_config_word
(bus
->number
, devfn
, PCI_COMMAND
, 0x0000);
pcibios_write_config_word
(bus
->number
, devfn
, PCI_STATUS
, 0xffff);
/*
* Read the existing primary/secondary/subordinate bus
* number configuration to determine if the PCI bridge
* has already been configured by the system. If so,
* do not modify the configuration, merely note it.
*/
pcibios_read_config_dword
(bus
->number
, devfn
, PCI_PRIMARY_BUS
, &buses
);
if ((buses
& 0xFFFFFF) != 0) {
unsigned int cmax
;
child
->primary
= buses
& 0xFF;
child
->secondary
= (buses
>> 8) & 0xFF;
child
->subordinate
= (buses
>> 16) & 0xFF;
child
->number
= child
->secondary
;
cmax
= pci_scan_bus
(child
);
if (cmax
> max
) max
= cmax
;
} else {
/*
* Configure the bus numbers for this bridge:
*/
buses
&= 0xff000000;
buses
|= (((unsigned int)(child
->primary
) << 0) |
((unsigned int)(child
->secondary
) << 8) |
((unsigned int)(child
->subordinate
) << 16));
pcibios_write_config_dword
(bus
->number
, devfn
, PCI_PRIMARY_BUS
, buses
);
/*
* Now we can scan all subordinate buses:
*/
max
= pci_scan_bus
(child
);
/*
* Set the subordinate bus number to its real
* value:
*/
child
->subordinate
= max
;
buses
= (buses
& 0xff00ffff)
| ((unsigned int)(child
->subordinate
) << 16);
pcibios_write_config_dword
(bus
->number
, devfn
, PCI_PRIMARY_BUS
, buses
);
}
pcibios_write_config_word
(bus
->number
, devfn
, PCI_COMMAND
, cr
);
}
}
#endif
return max
;
}
#endif
/* scan the bus to find all the connected devices */
DWORD pci_scan_bus
(struct pci_bus
*bus
)
{
int ndev
;
DWORD tmp
;
DWORD max
, class
;
BYTE hdr
, irq1
;
int ok
;
WORD dev
;
struct pci_dev
**bus_last
;
struct pci_dev
*device
= NULL
;
int present
;
max
= bus
->secondary
;
bus_last
= &bus
->devices
;
if (pcibios_present
() == 0) return -1;
ndev
= 0;
for (dev
= 0; dev
<= 0xFF; dev
++) {
present
= 0;
if ((dev
& 0x07) == 0) {
present
= pcibios_read_config_byte
(bus
->number
, dev
, 0x0e, &hdr
);
}
if (hdr
& 0x80) {
present
= 1;
}
if (present
) {
ok
= pcibios_read_config_dword
(bus
->number
, dev
, 0, &tmp
);
if ( ok
&& (((tmp
& 0xFFFF) != 0xFFFF) && ((tmp
>> 16) != 0xFFFF))) {
/* Got a PCI Device!!! */
device
= &(pci_devs
[ndev
++]);
device
->bus
= bus
;
device
->devfn
= dev
;
device
->vendor
= tmp
& 0xffff;
device
->device
= (tmp
>> 16) & 0xffff;
/* This is by Luca... We need to set base_addr */
pcibios_read_config_dword
(bus
->number
, dev
, PCI_BASE_ADDRESS_0
, &(device
->base_address
[0]));
/* ...and also the interrupt number!!! */
pcibios_read_config_byte
(bus
->number
, dev
, PCI_INTERRUPT_LINE
, &irq1
);
device
->irq
= irq1
;
pcibios_read_config_dword
(bus
->number
, dev
, PCI_CLASS_REVISION
, &class
);
class
>>= 8; /* upper 3 bytes */
device
->class
= class
;
class
>>= 8;
device
->hdr_type
= hdr
;
#if 0
switch (hdr_type
& 0x7f) { /* header type */
case PCI_HEADER_TYPE_NORMAL
: /* standard header */
if (class
== PCI_CLASS_BRIDGE_PCI
)
goto bad
;
/*
* If the card generates interrupts, read IRQ number
* (some architectures change it during pcibios_fixup())
*/
pcibios_read_config_byte
(bus
->number
, dev
->devfn
, PCI_INTERRUPT_PIN
, &irq
);
if (irq
)
pcibios_read_config_byte
(bus
->number
, dev
->devfn
, PCI_INTERRUPT_LINE
, &irq
);
dev
->irq
= irq
;
/*
* read base address registers, again pcibios_fixup() can
* tweak these
*/
pci_read_bases
(dev
, 6);
pcibios_read_config_dword
(bus
->number
, devfn
, PCI_ROM_ADDRESS
, &l
);
dev
->rom_address
= (l
== 0xffffffff) ? 0 : l
;
break;
case PCI_HEADER_TYPE_BRIDGE
: /* bridge header */
if (class
!= PCI_CLASS_BRIDGE_PCI
)
goto bad
;
pci_read_bases
(dev
, 2);
pcibios_read_config_dword
(bus
->number
, devfn
, PCI_ROM_ADDRESS1
, &l
);
dev
->rom_address
= (l
== 0xffffffff) ? 0 : l
;
break;
case PCI_HEADER_TYPE_CARDBUS
: /* CardBus bridge header */
if (class
!= PCI_CLASS_BRIDGE_CARDBUS
)
goto bad
;
pci_read_bases
(dev
, 1);
break;
default: /* unknown header */
bad
:
printk
(KERN_ERR
"PCI: %02x:%02x [%04x/%04x/%06x] has unknown header type %02x, ignoring.\n",
bus
->number
, dev
->devfn
, dev
->vendor
, dev
->device
, class
, hdr_type
);
continue;
}
DBG
("PCI: %02x:%02x [%04x/%04x]\n", bus
->number
, dev
->devfn
, dev
->vendor
, dev
->device
);
#endif
device
->next
= pci_devices
;
pci_devices
= device
;
/*
* Now insert it into the list of devices held
* by the parent bus.
*/
*bus_last
= device
;
bus_last
= &device
->sibling
;
}
}
}
for(device
= bus
->devices
; device
; device
= device
->sibling
) {
#if 0
/*
* If it's a bridge, scan the bus behind it.
*/
if ((dev
->class
>> 8) == PCI_CLASS_BRIDGE_PCI
) {
unsigned int buses
;
unsigned int devfn
= dev
->devfn
;
unsigned short cr
;
/*
* Insert it into the tree of buses.
*/
child
= kmalloc
(sizeof(*child
), GFP_ATOMIC
);
if(child
==NULL
) {
#ifdef DEBUG_PCISCAN
printk
(KERN_ERR
"pci: out of memory for bridge.\n");
#endif
continue;
}
memset(child
, 0, sizeof(*child
));
child
->next
= bus
->children
;
bus
->children
= child
;
child
->self
= dev
;
child
->parent
= bus
;
/*
* Set up the primary, secondary and subordinate
* bus numbers.
*/
child
->number
= child
->secondary
= ++max
;
child
->primary
= bus
->secondary
;
child
->subordinate
= 0xff;
/*
* Clear all status bits and turn off memory,
* I/O and master enables.
*/
pcibios_read_config_word
(bus
->number
, devfn
, PCI_COMMAND
, &cr
);
pcibios_write_config_word
(bus
->number
, devfn
, PCI_COMMAND
, 0x0000);
pcibios_write_config_word
(bus
->number
, devfn
, PCI_STATUS
, 0xffff);
/*
* Read the existing primary/secondary/subordinate bus
* number configuration to determine if the PCI bridge
* has already been configured by the system. If so,
* do not modify the configuration, merely note it.
*/
pcibios_read_config_dword
(bus
->number
, devfn
, PCI_PRIMARY_BUS
, &buses
);
if ((buses
& 0xFFFFFF) != 0) {
unsigned int cmax
;
child
->primary
= buses
& 0xFF;
child
->secondary
= (buses
>> 8) & 0xFF;
child
->subordinate
= (buses
>> 16) & 0xFF;
child
->number
= child
->secondary
;
cmax
= pci_scan_bus
(child
);
if (cmax
> max
) max
= cmax
;
} else {
/*
* Configure the bus numbers for this bridge:
*/
buses
&= 0xff000000;
buses
|= (((unsigned int)(child
->primary
) << 0) |
((unsigned int)(child
->secondary
) << 8) |
((unsigned int)(child
->subordinate
) << 16));
pcibios_write_config_dword
(bus
->number
, devfn
, PCI_PRIMARY_BUS
, buses
);
/*
* Now we can scan all subordinate buses:
*/
max
= pci_scan_bus
(child
);
/*
* Set the subordinate bus number to its real
* value:
*/
child
->subordinate
= max
;
buses
= (buses
& 0xff00ffff)
| ((unsigned int)(child
->subordinate
) << 16);
pcibios_write_config_dword
(bus
->number
, devfn
, PCI_PRIMARY_BUS
, buses
);
}
pcibios_write_config_word
(bus
->number
, devfn
, PCI_COMMAND
, cr
);
}
#endif
}
return max
;
}
void linuxpci_init
(void)
{
pcibios_init
();
if (!pci_present
()) {
#ifdef DEBUG_PCISCAN
printk
("PCI: No PCI bus detected\n");
#endif
return;
}
#ifdef DEBUG_PCISCAN
printk
("PCI: Probing PCI hardware\n");
#endif
memset(&pci_root
, 0, sizeof(pci_root
));
pci_root.
subordinate = pci_scan_bus
(&pci_root
);
}
struct pci_dev
*pci_find_device
(unsigned int vendor
, unsigned int device
, struct pci_dev
*from
)
{
if (!from
)
from
= pci_devices
;
else
from
= from
->next
;
while (from
&& (from
->vendor
!= vendor
|| from
->device
!= device
))
from
= from
->next
;
return from
;
}
struct pci_dev
*pci_find_class
(unsigned int class
, struct pci_dev
*from
)
{
if (!from
)
from
= pci_devices
;
else
from
= from
->next
;
while (from
&& from
->class
!= class
)
from
= from
->next
;
cprintf
("PCI_FIND_CLASS: found %p at bus %d\n", from
, from
->bus
->number
);
return from
;
}
struct pci_dev
*
pci_find_slot
(unsigned int bus
, unsigned int devfn
)
{
struct pci_dev
*dev
;
for(dev
=pci_devices
; dev
; dev
=dev
->next
)
if (dev
->bus
->number
== bus
&& dev
->devfn
== devfn
) {
cprintf
("FOUND!!!!!!!!!!\n");
break;
} else {
cprintf
("NOT FOUND! %d!=%d %d!=%d\n", dev
->bus
->number
, bus
, dev
->devfn
, devfn
);
}
cprintf
("PCI_FIND_SLOT: found %p at bus %d\n", dev
, dev
->bus
->number
);
return dev
;
}
void
pci_set_master
(struct pci_dev
*dev
)
{
unsigned short cmd
; // was: u16
unsigned char lat
; // was: u8
pci_read_config_word
(dev
, PCI_COMMAND
, &cmd
);
if (! (cmd
& PCI_COMMAND_MASTER
)) {
printk
("PCI: Enabling bus mastering for device %02x:%02x\n",
dev
->bus
->number
, dev
->devfn
);
cmd
|= PCI_COMMAND_MASTER
;
pci_write_config_word
(dev
, PCI_COMMAND
, cmd
);
}
pci_read_config_byte
(dev
, PCI_LATENCY_TIMER
, &lat
);
if (lat
< 16) {
printk
("PCI: Increasing latency timer of device %02x:%02x to 64\n",
dev
->bus
->number
, dev
->devfn
);
pci_write_config_byte
(dev
, PCI_LATENCY_TIMER
, 64);
}
}