minix/drivers/pci/pci.c

2725 lines
65 KiB
C
Raw Normal View History

#define USER_SPACE 1
2005-04-21 16:53:53 +02:00
/*
pci.c
#define USER_SPACE 1
2005-04-21 16:53:53 +02:00
Configure devices on the PCI bus
Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl>
*/
#include <minix/drivers.h>
#include <assert.h>
#include <machine/pci.h>
#include <machine/vm.h>
#include <minix/com.h>
#include <minix/syslib.h>
2005-04-21 16:53:53 +02:00
#include "pci.h"
#include "pci_amd.h"
#include "pci_intel.h"
#include "pci_sis.h"
#include "pci_via.h"
#if __minix_vmd
#include "config.h"
2005-04-21 16:53:53 +02:00
#endif
#if !__minix_vmd
#define irq_mode_pci(irq) ((void)0)
#endif
#include <stdlib.h>
#include <stdio.h>
#include <minix/sysutil.h>
2005-04-21 16:53:53 +02:00
#define PBT_INTEL_HOST 1
2005-04-21 16:53:53 +02:00
#define PBT_PCIBRIDGE 2
#define PBT_CARDBUS 3
#define BAM_NR 6 /* Number of base-address registers */
2005-04-21 16:53:53 +02:00
int debug= 0;
2005-08-09 13:23:41 +02:00
2005-04-21 16:53:53 +02:00
PRIVATE struct pcibus
{
int pb_type;
int pb_needinit;
2005-04-21 16:53:53 +02:00
int pb_isabridge_dev;
int pb_isabridge_type;
int pb_devind;
int pb_busnr;
2005-04-21 16:53:53 +02:00
u8_t (*pb_rreg8)(int busind, int devind, int port);
u16_t (*pb_rreg16)(int busind, int devind, int port);
u32_t (*pb_rreg32)(int busind, int devind, int port);
void (*pb_wreg8)(int busind, int devind, int port, U8_t value);
2005-04-21 16:53:53 +02:00
void (*pb_wreg16)(int busind, int devind, int port, U16_t value);
void (*pb_wreg32)(int busind, int devind, int port, u32_t value);
u16_t (*pb_rsts)(int busind);
void (*pb_wsts)(int busind, U16_t value);
} pcibus[NR_PCIBUS];
PRIVATE int nr_pcibus= 0;
PRIVATE struct pcidev
{
u8_t pd_busnr;
2005-04-21 16:53:53 +02:00
u8_t pd_dev;
u8_t pd_func;
u8_t pd_baseclass;
u8_t pd_subclass;
u8_t pd_infclass;
u16_t pd_vid;
u16_t pd_did;
u8_t pd_ilr;
2005-04-21 16:53:53 +02:00
u8_t pd_inuse;
endpoint_t pd_proc;
struct bar
{
int pb_flags;
int pb_nr;
u32_t pb_base;
u32_t pb_size;
} pd_bar[BAM_NR];
int pd_bar_nr;
2005-04-21 16:53:53 +02:00
} pcidev[NR_PCIDEV];
EXTERN struct pci_acl pci_acl[NR_DRIVERS];
Initialization protocol for system services. SYSLIB CHANGES: - SEF framework now supports a new SEF Init request type from RS. 3 different callbacks are available (init_fresh, init_lu, init_restart) to specify initialization code when a service starts fresh, starts after a live update, or restarts. SYSTEM SERVICE CHANGES: - Initialization code for system services is now enclosed in a callback SEF will automatically call at init time. The return code of the callback will tell RS whether the initialization completed successfully. - Each init callback can access information passed by RS to initialize. As of now, each system service has access to the public entries of RS's system process table to gather all the information required to initialize. This design eliminates many existing or potential races at boot time and provides a uniform initialization interface to system services. The same interface will be reused for the upcoming publish/subscribe model to handle dynamic registration / deregistration of system services. VM CHANGES: - Uniform privilege management for all system services. Every service uses the same call mask format. For boot services, VM copies the call mask from init data. For dynamic services, VM still receives the call mask via rs_set_priv call that will be soon replaced by the upcoming publish/subscribe model. RS CHANGES: - The system process table has been reorganized and split into private entries and public entries. Only the latter ones are exposed to system services. - VM call masks are now entirely configured in rs/table.c - RS has now its own slot in the system process table. Only kernel tasks and user processes not included in the boot image are now left out from the system process table. - RS implements the initialization protocol for system services. - For services in the boot image, RS blocks till initialization is complete and panics when failure is reported back. Services are initialized in their order of appearance in the boot image priv table and RS blocks to implements synchronous initialization for every system service having the flag SF_SYNCH_BOOT set. - For services started dynamically, the initialization protocol is implemented as though it were the first ping for the service. In this case, if the system service fails to report back (or reports failure), RS brings the service down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
/* pb_flags */
#define PBF_IO 1 /* I/O else memory */
#define PBF_INCOMPLETE 2 /* not allocated */
2005-04-21 16:53:53 +02:00
PRIVATE int nr_pcidev= 0;
FORWARD _PROTOTYPE( void pci_intel_init, (void) );
FORWARD _PROTOTYPE( void probe_bus, (int busind) );
FORWARD _PROTOTYPE( int is_duplicate, (U8_t busnr, U8_t dev, U8_t func) );
FORWARD _PROTOTYPE( void record_irq, (int devind) );
2010-01-13 11:52:47 +01:00
FORWARD _PROTOTYPE( void record_bars_normal, (int devind) );
FORWARD _PROTOTYPE( void record_bars_bridge, (int devind) );
FORWARD _PROTOTYPE( void record_bars_cardbus, (int devind) );
2010-01-13 11:52:47 +01:00
FORWARD _PROTOTYPE( void record_bars, (int devind, int last_reg) );
FORWARD _PROTOTYPE( int record_bar, (int devind, int bar_nr, int last) );
FORWARD _PROTOTYPE( void complete_bridges, (void) );
FORWARD _PROTOTYPE( void complete_bars, (void) );
FORWARD _PROTOTYPE( void update_bridge4dev_io, (int devind,
u32_t io_base, u32_t io_size) );
FORWARD _PROTOTYPE( int get_freebus, (void) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( int do_isabridge, (int busind) );
FORWARD _PROTOTYPE( void do_pcibridge, (int busind) );
FORWARD _PROTOTYPE( int get_busind, (int busnr) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( int do_piix, (int devind) );
FORWARD _PROTOTYPE( int do_amd_isabr, (int devind) );
FORWARD _PROTOTYPE( int do_sis_isabr, (int devind) );
FORWARD _PROTOTYPE( int do_via_isabr, (int devind) );
#if 0
FORWARD _PROTOTYPE( void report_vga, (int devind) );
#endif
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( char *pci_vid_name, (U16_t vid) );
FORWARD _PROTOTYPE( char *pci_baseclass_name, (U8_t baseclass) );
FORWARD _PROTOTYPE( char *pci_subclass_name, (U8_t baseclass,
U8_t subclass, U8_t infclass) );
FORWARD _PROTOTYPE( void ntostr, (unsigned n, char **str, char *end) );
FORWARD _PROTOTYPE( u8_t pci_attr_r8_u, (int devind, int port) );
FORWARD _PROTOTYPE( u32_t pci_attr_r32_u, (int devind, int port) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( u16_t pci_attr_rsts, (int devind) );
FORWARD _PROTOTYPE( void pci_attr_wsts, (int devind, u16_t value) );
FORWARD _PROTOTYPE( u16_t pcibr_std_rsts, (int busind) );
FORWARD _PROTOTYPE( void pcibr_std_wsts, (int busind, U16_t value) );
FORWARD _PROTOTYPE( u16_t pcibr_cb_rsts, (int busind) );
FORWARD _PROTOTYPE( void pcibr_cb_wsts, (int busind, U16_t value) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( u16_t pcibr_via_rsts, (int busind) );
FORWARD _PROTOTYPE( void pcibr_via_wsts, (int busind, U16_t value) );
FORWARD _PROTOTYPE( u8_t pcii_rreg8, (int busind, int devind, int port) );
FORWARD _PROTOTYPE( u16_t pcii_rreg16, (int busind, int devind,
int port) );
FORWARD _PROTOTYPE( u32_t pcii_rreg32, (int busind, int devind,
int port) );
FORWARD _PROTOTYPE( void pcii_wreg8, (int busind, int devind, int port,
U8_t value) );
2005-04-21 16:53:53 +02:00
FORWARD _PROTOTYPE( void pcii_wreg16, (int busind, int devind, int port,
U16_t value) );
FORWARD _PROTOTYPE( void pcii_wreg32, (int busind, int devind, int port,
u32_t value) );
FORWARD _PROTOTYPE( u16_t pcii_rsts, (int busind) );
FORWARD _PROTOTYPE( void pcii_wsts, (int busind, U16_t value) );
2006-03-06 16:19:51 +01:00
FORWARD _PROTOTYPE( void print_capabilities, (int devind) );
FORWARD _PROTOTYPE( int visible, (struct rs_pci *aclp, int devind) );
FORWARD _PROTOTYPE( void print_hyper_cap, (int devind, U8_t capptr) );
2005-04-21 16:53:53 +02:00
Initialization protocol for system services. SYSLIB CHANGES: - SEF framework now supports a new SEF Init request type from RS. 3 different callbacks are available (init_fresh, init_lu, init_restart) to specify initialization code when a service starts fresh, starts after a live update, or restarts. SYSTEM SERVICE CHANGES: - Initialization code for system services is now enclosed in a callback SEF will automatically call at init time. The return code of the callback will tell RS whether the initialization completed successfully. - Each init callback can access information passed by RS to initialize. As of now, each system service has access to the public entries of RS's system process table to gather all the information required to initialize. This design eliminates many existing or potential races at boot time and provides a uniform initialization interface to system services. The same interface will be reused for the upcoming publish/subscribe model to handle dynamic registration / deregistration of system services. VM CHANGES: - Uniform privilege management for all system services. Every service uses the same call mask format. For boot services, VM copies the call mask from init data. For dynamic services, VM still receives the call mask via rs_set_priv call that will be soon replaced by the upcoming publish/subscribe model. RS CHANGES: - The system process table has been reorganized and split into private entries and public entries. Only the latter ones are exposed to system services. - VM call masks are now entirely configured in rs/table.c - RS has now its own slot in the system process table. Only kernel tasks and user processes not included in the boot image are now left out from the system process table. - RS implements the initialization protocol for system services. - For services in the boot image, RS blocks till initialization is complete and panics when failure is reported back. Services are initialized in their order of appearance in the boot image priv table and RS blocks to implements synchronous initialization for every system service having the flag SF_SYNCH_BOOT set. - For services started dynamically, the initialization protocol is implemented as though it were the first ping for the service. In this case, if the system service fails to report back (or reports failure), RS brings the service down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
/*===========================================================================*
* sef_cb_init_fresh *
*===========================================================================*/
PUBLIC int sef_cb_init_fresh(int type, sef_init_info_t *info)
{
/* Initialize the pci driver. */
long v;
int i, r;
struct rprocpub rprocpub[NR_BOOT_PROCS];
v= 0;
env_parse("pci_debug", "d", 0, &v, 0, 1);
debug= v;
/* Only Intel (compatible) PCI controllers are supported at the
* moment.
*/
pci_intel_init();
/* Map all the services in the boot image. */
if((r = sys_safecopyfrom(RS_PROC_NR, info->rproctab_gid, 0,
(vir_bytes) rprocpub, sizeof(rprocpub), S)) != OK) {
panic("sys_safecopyfrom failed: %d", r);
Initialization protocol for system services. SYSLIB CHANGES: - SEF framework now supports a new SEF Init request type from RS. 3 different callbacks are available (init_fresh, init_lu, init_restart) to specify initialization code when a service starts fresh, starts after a live update, or restarts. SYSTEM SERVICE CHANGES: - Initialization code for system services is now enclosed in a callback SEF will automatically call at init time. The return code of the callback will tell RS whether the initialization completed successfully. - Each init callback can access information passed by RS to initialize. As of now, each system service has access to the public entries of RS's system process table to gather all the information required to initialize. This design eliminates many existing or potential races at boot time and provides a uniform initialization interface to system services. The same interface will be reused for the upcoming publish/subscribe model to handle dynamic registration / deregistration of system services. VM CHANGES: - Uniform privilege management for all system services. Every service uses the same call mask format. For boot services, VM copies the call mask from init data. For dynamic services, VM still receives the call mask via rs_set_priv call that will be soon replaced by the upcoming publish/subscribe model. RS CHANGES: - The system process table has been reorganized and split into private entries and public entries. Only the latter ones are exposed to system services. - VM call masks are now entirely configured in rs/table.c - RS has now its own slot in the system process table. Only kernel tasks and user processes not included in the boot image are now left out from the system process table. - RS implements the initialization protocol for system services. - For services in the boot image, RS blocks till initialization is complete and panics when failure is reported back. Services are initialized in their order of appearance in the boot image priv table and RS blocks to implements synchronous initialization for every system service having the flag SF_SYNCH_BOOT set. - For services started dynamically, the initialization protocol is implemented as though it were the first ping for the service. In this case, if the system service fails to report back (or reports failure), RS brings the service down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
}
for(i=0;i < NR_BOOT_PROCS;i++) {
if(rprocpub[i].in_use) {
if((r = map_service(&rprocpub[i])) != OK) {
panic("unable to map service: %d", r);
Initialization protocol for system services. SYSLIB CHANGES: - SEF framework now supports a new SEF Init request type from RS. 3 different callbacks are available (init_fresh, init_lu, init_restart) to specify initialization code when a service starts fresh, starts after a live update, or restarts. SYSTEM SERVICE CHANGES: - Initialization code for system services is now enclosed in a callback SEF will automatically call at init time. The return code of the callback will tell RS whether the initialization completed successfully. - Each init callback can access information passed by RS to initialize. As of now, each system service has access to the public entries of RS's system process table to gather all the information required to initialize. This design eliminates many existing or potential races at boot time and provides a uniform initialization interface to system services. The same interface will be reused for the upcoming publish/subscribe model to handle dynamic registration / deregistration of system services. VM CHANGES: - Uniform privilege management for all system services. Every service uses the same call mask format. For boot services, VM copies the call mask from init data. For dynamic services, VM still receives the call mask via rs_set_priv call that will be soon replaced by the upcoming publish/subscribe model. RS CHANGES: - The system process table has been reorganized and split into private entries and public entries. Only the latter ones are exposed to system services. - VM call masks are now entirely configured in rs/table.c - RS has now its own slot in the system process table. Only kernel tasks and user processes not included in the boot image are now left out from the system process table. - RS implements the initialization protocol for system services. - For services in the boot image, RS blocks till initialization is complete and panics when failure is reported back. Services are initialized in their order of appearance in the boot image priv table and RS blocks to implements synchronous initialization for every system service having the flag SF_SYNCH_BOOT set. - For services started dynamically, the initialization protocol is implemented as though it were the first ping for the service. In this case, if the system service fails to report back (or reports failure), RS brings the service down rather than trying to restart it.
2010-01-08 02:20:42 +01:00
}
}
}
return(OK);
}
/*===========================================================================*
* map_service *
*===========================================================================*/
PUBLIC int map_service(rpub)
struct rprocpub *rpub;
{
/* Map a new service by registering a new acl entry if required. */
int i;
/* Stop right now if no pci device or class is found. */
if(rpub->pci_acl.rsp_nr_device == 0
&& rpub->pci_acl.rsp_nr_class == 0) {
return(OK);
}
/* Find a free acl slot. */
for (i= 0; i<NR_DRIVERS; i++)
{
if (!pci_acl[i].inuse)
break;
}
if (i >= NR_DRIVERS)
{
printf("PCI: map_service: table is full\n");
return ENOMEM;
}
/* Initialize acl slot. */
pci_acl[i].inuse = 1;
pci_acl[i].acl = rpub->pci_acl;
return(OK);
}
/*===========================================================================*
* helper functions for I/O *
*===========================================================================*/
PUBLIC unsigned pci_inb(U16_t port) {
2005-10-24 16:02:45 +02:00
u32_t value;
int s;
if ((s=sys_inb(port, &value)) !=OK)
printf("PCI: warning, sys_inb failed: %d\n", s);
return value;
}
PUBLIC unsigned pci_inw(U16_t port) {
2005-12-02 15:45:10 +01:00
u32_t value;
int s;
if ((s=sys_inw(port, &value)) !=OK)
printf("PCI: warning, sys_inw failed: %d\n", s);
return value;
}
PUBLIC unsigned pci_inl(U16_t port) {
U32_t value;
int s;
if ((s=sys_inl(port, &value)) !=OK)
printf("PCI: warning, sys_inl failed: %d\n", s);
return value;
}
PUBLIC void pci_outb(U16_t port, U8_t value) {
int s;
if ((s=sys_outb(port, value)) !=OK)
printf("PCI: warning, sys_outb failed: %d\n", s);
}
PUBLIC void pci_outw(U16_t port, U16_t value) {
int s;
if ((s=sys_outw(port, value)) !=OK)
printf("PCI: warning, sys_outw failed: %d\n", s);
}
PUBLIC void pci_outl(U16_t port, U32_t value) {
int s;
if ((s=sys_outl(port, value)) !=OK)
printf("PCI: warning, sys_outl failed: %d\n", s);
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* pci_find_dev *
*===========================================================================*/
PUBLIC int pci_find_dev(bus, dev, func, devindp)
u8_t bus;
u8_t dev;
u8_t func;
int *devindp;
{
int devind;
for (devind= 0; devind < nr_pcidev; devind++)
{
if (pcidev[devind].pd_busnr == bus &&
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_dev == dev &&
pcidev[devind].pd_func == func)
{
break;
}
}
if (devind >= nr_pcidev)
return 0;
#if 0
2005-04-21 16:53:53 +02:00
if (pcidev[devind].pd_inuse)
return 0;
#endif
2005-04-21 16:53:53 +02:00
*devindp= devind;
return 1;
}
/*===========================================================================*
* pci_first_dev_a *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PUBLIC int pci_first_dev_a(aclp, devindp, vidp, didp)
struct rs_pci *aclp;
2005-04-21 16:53:53 +02:00
int *devindp;
u16_t *vidp;
u16_t *didp;
{
int i, devind;
2005-04-21 16:53:53 +02:00
for (devind= 0; devind < nr_pcidev; devind++)
{
#if 0
if (pcidev[devind].pd_inuse)
continue;
#endif
if (!visible(aclp, devind))
continue;
break;
2005-04-21 16:53:53 +02:00
}
if (devind >= nr_pcidev)
return 0;
*devindp= devind;
*vidp= pcidev[devind].pd_vid;
*didp= pcidev[devind].pd_did;
return 1;
}
/*===========================================================================*
* pci_next_dev *
*===========================================================================*/
PUBLIC int pci_next_dev_a(aclp, devindp, vidp, didp)
struct rs_pci *aclp;
2005-04-21 16:53:53 +02:00
int *devindp;
u16_t *vidp;
u16_t *didp;
{
int devind;
for (devind= *devindp+1; devind < nr_pcidev; devind++)
{
#if 0
if (pcidev[devind].pd_inuse)
continue;
#endif
if (!visible(aclp, devind))
continue;
break;
2005-04-21 16:53:53 +02:00
}
if (devind >= nr_pcidev)
return 0;
*devindp= devind;
*vidp= pcidev[devind].pd_vid;
*didp= pcidev[devind].pd_did;
return 1;
}
/*===========================================================================*
* pci_reserve2 *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PUBLIC int pci_reserve2(devind, proc)
2005-04-21 16:53:53 +02:00
int devind;
endpoint_t proc;
2005-04-21 16:53:53 +02:00
{
2006-01-27 14:18:07 +01:00
int i, r;
int ilr;
2006-01-27 14:18:07 +01:00
struct io_range ior;
struct mem_range mr;
if (devind < 0 || devind >= nr_pcidev)
{
printf("pci:pci_reserve2: bad devind: %d\n", devind);
return EINVAL;
}
New RS and new signal handling for system processes. UPDATING INFO: 20100317: /usr/src/etc/system.conf updated to ignore default kernel calls: copy it (or merge it) to /etc/system.conf. The hello driver (/dev/hello) added to the distribution: # cd /usr/src/commands/scripts && make clean install # cd /dev && MAKEDEV hello KERNEL CHANGES: - Generic signal handling support. The kernel no longer assumes PM as a signal manager for every process. The signal manager of a given process can now be specified in its privilege slot. When a signal has to be delivered, the kernel performs the lookup and forwards the signal to the appropriate signal manager. PM is the default signal manager for user processes, RS is the default signal manager for system processes. To enable ptrace()ing for system processes, it is sufficient to change the default signal manager to PM. This will temporarily disable crash recovery, though. - sys_exit() is now split into sys_exit() (i.e. exit() for system processes, which generates a self-termination signal), and sys_clear() (i.e. used by PM to ask the kernel to clear a process slot when a process exits). - Added a new kernel call (i.e. sys_update()) to swap two process slots and implement live update. PM CHANGES: - Posix signal handling is no longer allowed for system processes. System signals are split into two fixed categories: termination and non-termination signals. When a non-termination signaled is processed, PM transforms the signal into an IPC message and delivers the message to the system process. When a termination signal is processed, PM terminates the process. - PM no longer assumes itself as the signal manager for system processes. It now makes sure that every system signal goes through the kernel before being actually processes. The kernel will then dispatch the signal to the appropriate signal manager which may or may not be PM. SYSLIB CHANGES: - Simplified SEF init and LU callbacks. - Added additional predefined SEF callbacks to debug crash recovery and live update. - Fixed a temporary ack in the SEF init protocol. SEF init reply is now completely synchronous. - Added SEF signal event type to provide a uniform interface for system processes to deal with signals. A sef_cb_signal_handler() callback is available for system processes to handle every received signal. A sef_cb_signal_manager() callback is used by signal managers to process system signals on behalf of the kernel. - Fixed a few bugs with memory mapping and DS. VM CHANGES: - Page faults and memory requests coming from the kernel are now implemented using signals. - Added a new VM call to swap two process slots and implement live update. - The call is used by RS at update time and in turn invokes the kernel call sys_update(). RS CHANGES: - RS has been reworked with a better functional decomposition. - Better kernel call masks. com.h now defines the set of very basic kernel calls every system service is allowed to use. This makes system.conf simpler and easier to maintain. In addition, this guarantees a higher level of isolation for system libraries that use one or more kernel calls internally (e.g. printf). - RS is the default signal manager for system processes. By default, RS intercepts every signal delivered to every system process. This makes crash recovery possible before bringing PM and friends in the loop. - RS now supports fast rollback when something goes wrong while initializing the new version during a live update. - Live update is now implemented by keeping the two versions side-by-side and swapping the process slots when the old version is ready to update. - Crash recovery is now implemented by keeping the two versions side-by-side and cleaning up the old version only when the recovery process is complete. DS CHANGES: - Fixed a bug when the process doing ds_publish() or ds_delete() is not known by DS. - Fixed the completely broken support for strings. String publishing is now implemented in the system library and simply wraps publishing of memory ranges. Ideally, we should adopt a similar approach for other data types as well. - Test suite fixed. DRIVER CHANGES: - The hello driver has been added to the Minix distribution to demonstrate basic live update and crash recovery functionalities. - Other drivers have been adapted to conform the new SEF interface.
2010-03-17 02:15:29 +01:00
if(pcidev[devind].pd_inuse && pcidev[devind].pd_proc != proc)
return EBUSY;
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_inuse= 1;
pcidev[devind].pd_proc= proc;
2006-01-27 14:18:07 +01:00
for (i= 0; i<pcidev[devind].pd_bar_nr; i++)
{
if (pcidev[devind].pd_bar[i].pb_flags & PBF_INCOMPLETE)
{
printf("pci_reserve3: BAR %d is incomplete\n", i);
continue;
}
if (pcidev[devind].pd_bar[i].pb_flags & PBF_IO)
{
ior.ior_base= pcidev[devind].pd_bar[i].pb_base;
ior.ior_limit= ior.ior_base +
pcidev[devind].pd_bar[i].pb_size-1;
if(debug) {
printf(
2006-01-27 14:18:07 +01:00
"pci_reserve3: for proc %d, adding I/O range [0x%x..0x%x]\n",
proc, ior.ior_base, ior.ior_limit);
}
r= sys_privctl(proc, SYS_PRIV_ADD_IO, &ior);
2006-01-27 14:18:07 +01:00
if (r != OK)
{
printf("sys_privctl failed for proc %d: %d\n",
proc, r);
}
}
else
{
mr.mr_base= pcidev[devind].pd_bar[i].pb_base;
mr.mr_limit= mr.mr_base +
pcidev[devind].pd_bar[i].pb_size-1;
r= sys_privctl(proc, SYS_PRIV_ADD_MEM, &mr);
2006-01-27 14:18:07 +01:00
if (r != OK)
{
printf("sys_privctl failed for proc %d: %d\n",
proc, r);
}
}
}
ilr= pcidev[devind].pd_ilr;
if (ilr != PCI_ILR_UNKNOWN)
{
if(debug) printf("pci_reserve3: adding IRQ %d\n", ilr);
r= sys_privctl(proc, SYS_PRIV_ADD_IRQ, &ilr);
2006-01-27 14:18:07 +01:00
if (r != OK)
{
printf("sys_privctl failed for proc %d: %d\n",
proc, r);
}
}
return OK;
}
/*===========================================================================*
* pci_release *
*===========================================================================*/
PUBLIC void pci_release(proc)
endpoint_t proc;
{
int i;
for (i= 0; i<nr_pcidev; i++)
{
if (!pcidev[i].pd_inuse)
continue;
if (pcidev[i].pd_proc != proc)
continue;
pcidev[i].pd_inuse= 0;
}
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* pci_ids_s *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PUBLIC int pci_ids_s(devind, vidp, didp)
2005-04-21 16:53:53 +02:00
int devind;
u16_t *vidp;
u16_t *didp;
{
if (devind < 0 || devind >= nr_pcidev)
return EINVAL;
2005-04-21 16:53:53 +02:00
*vidp= pcidev[devind].pd_vid;
*didp= pcidev[devind].pd_did;
return OK;
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* pci_rescan_bus *
*===========================================================================*/
PUBLIC void pci_rescan_bus(busnr)
u8_t busnr;
{
int busind;
busind= get_busind(busnr);
probe_bus(busind);
/* Allocate bus numbers for uninitialized bridges */
complete_bridges();
/* Allocate I/O and memory resources for uninitialized devices */
complete_bars();
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* pci_slot_name_s *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PUBLIC int pci_slot_name_s(devind, cpp)
2005-04-21 16:53:53 +02:00
int devind;
char **cpp;
2005-04-21 16:53:53 +02:00
{
static char label[]= "ddd.ddd.ddd";
char *end;
char *p;
if (devind < 0 || devind >= nr_pcidev)
return EINVAL;
2005-04-21 16:53:53 +02:00
p= label;
end= label+sizeof(label);
ntostr(pcidev[devind].pd_busnr, &p, end);
2005-04-21 16:53:53 +02:00
*p++= '.';
ntostr(pcidev[devind].pd_dev, &p, end);
*p++= '.';
ntostr(pcidev[devind].pd_func, &p, end);
*cpp= label;
return OK;
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* pci_dev_name *
*===========================================================================*/
PUBLIC char *pci_dev_name(vid, did)
u16_t vid;
u16_t did;
{
int i;
for (i= 0; pci_device_table[i].name; i++)
{
if (pci_device_table[i].vid == vid &&
pci_device_table[i].did == did)
{
return pci_device_table[i].name;
}
}
return NULL;
}
/*===========================================================================*
* pci_attr_r8_s *
*===========================================================================*/
PUBLIC int pci_attr_r8_s(devind, port, vp)
int devind;
int port;
u8_t *vp;
{
if (devind < 0 || devind >= nr_pcidev)
return EINVAL;
if (port < 0 || port > 255)
return EINVAL;
*vp= pci_attr_r8_u(devind, port);
return OK;
}
/*===========================================================================*
* pci_attr_r8_u *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PRIVATE u8_t pci_attr_r8_u(devind, port)
2005-04-21 16:53:53 +02:00
int devind;
int port;
{
int busnr, busind;
2005-04-21 16:53:53 +02:00
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
2005-04-21 16:53:53 +02:00
return pcibus[busind].pb_rreg8(busind, devind, port);
}
/*===========================================================================*
* pci_attr_r16 *
*===========================================================================*/
PUBLIC u16_t pci_attr_r16(devind, port)
int devind;
int port;
{
int busnr, busind;
2005-04-21 16:53:53 +02:00
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
2005-04-21 16:53:53 +02:00
return pcibus[busind].pb_rreg16(busind, devind, port);
}
/*===========================================================================*
* pci_attr_r32_s *
*===========================================================================*/
PUBLIC int pci_attr_r32_s(devind, port, vp)
int devind;
int port;
u32_t *vp;
{
if (devind < 0 || devind >= nr_pcidev)
return EINVAL;
if (port < 0 || port > 256-4)
return EINVAL;
*vp= pci_attr_r32_u(devind, port);
return OK;
}
/*===========================================================================*
* pci_attr_r32_u *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PRIVATE u32_t pci_attr_r32_u(devind, port)
2005-04-21 16:53:53 +02:00
int devind;
int port;
{
int busnr, busind;
2005-04-21 16:53:53 +02:00
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
2005-04-21 16:53:53 +02:00
return pcibus[busind].pb_rreg32(busind, devind, port);
}
/*===========================================================================*
* pci_attr_w8 *
*===========================================================================*/
PUBLIC void pci_attr_w8(devind, port, value)
int devind;
int port;
u16_t value;
{
int busnr, busind;
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
pcibus[busind].pb_wreg8(busind, devind, port, value);
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* pci_attr_w16 *
*===========================================================================*/
PUBLIC void pci_attr_w16(devind, port, value)
int devind;
int port;
u16_t value;
{
int busnr, busind;
2005-04-21 16:53:53 +02:00
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
2005-04-21 16:53:53 +02:00
pcibus[busind].pb_wreg16(busind, devind, port, value);
}
/*===========================================================================*
* pci_attr_w32 *
*===========================================================================*/
PUBLIC void pci_attr_w32(devind, port, value)
int devind;
int port;
u32_t value;
{
int busnr, busind;
2005-04-21 16:53:53 +02:00
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
2005-04-21 16:53:53 +02:00
pcibus[busind].pb_wreg32(busind, devind, port, value);
}
/*===========================================================================*
* pci_intel_init *
*===========================================================================*/
PRIVATE void pci_intel_init()
{
/* Try to detect a know PCI controller. Read the Vendor ID and
* the Device ID for function 0 of device 0.
* Two times the value 0xffff suggests a system without a (compatible)
2006-03-07 15:14:53 +01:00
* PCI controller.
2005-04-21 16:53:53 +02:00
*/
u32_t bus, dev, func;
u16_t vid, did;
int s, i, r, busind, busnr;
2005-04-21 16:53:53 +02:00
char *dstr;
bus= 0;
dev= 0;
func= 0;
vid= PCII_RREG16_(bus, dev, func, PCI_VID);
did= PCII_RREG16_(bus, dev, func, PCI_DID);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
if (vid == 0xffff && did == 0xffff)
return; /* Nothing here */
2006-03-07 15:14:53 +01:00
#if 0
2005-04-21 16:53:53 +02:00
for (i= 0; pci_intel_ctrl[i].vid; i++)
{
if (pci_intel_ctrl[i].vid == vid &&
pci_intel_ctrl[i].did == did)
{
break;
}
}
if (!pci_intel_ctrl[i].vid)
{
printf("pci_intel_init (warning): unknown PCI-controller:\n"
2005-04-21 16:53:53 +02:00
"\tvendor %04X (%s), device %04X\n",
vid, pci_vid_name(vid), did);
}
2006-03-07 15:14:53 +01:00
#endif
2005-04-21 16:53:53 +02:00
if (nr_pcibus >= NR_PCIBUS)
panic("too many PCI busses: %d", nr_pcibus);
2005-04-21 16:53:53 +02:00
busind= nr_pcibus;
nr_pcibus++;
pcibus[busind].pb_type= PBT_INTEL_HOST;
pcibus[busind].pb_needinit= 0;
2005-04-21 16:53:53 +02:00
pcibus[busind].pb_isabridge_dev= -1;
pcibus[busind].pb_isabridge_type= 0;
pcibus[busind].pb_devind= -1;
pcibus[busind].pb_busnr= 0;
2005-04-21 16:53:53 +02:00
pcibus[busind].pb_rreg8= pcii_rreg8;
pcibus[busind].pb_rreg16= pcii_rreg16;
pcibus[busind].pb_rreg32= pcii_rreg32;
pcibus[busind].pb_wreg8= pcii_wreg8;
2005-04-21 16:53:53 +02:00
pcibus[busind].pb_wreg16= pcii_wreg16;
pcibus[busind].pb_wreg32= pcii_wreg32;
pcibus[busind].pb_rsts= pcii_rsts;
pcibus[busind].pb_wsts= pcii_wsts;
dstr= pci_dev_name(vid, did);
if (!dstr)
dstr= "unknown device";
if (debug)
{
printf("pci_intel_init: %s (%04X/%04X)\n",
dstr, vid, did);
}
probe_bus(busind);
r= do_isabridge(busind);
if (r != OK)
{
busnr= pcibus[busind].pb_busnr;
2005-04-21 16:53:53 +02:00
/* Disable all devices for this bus */
for (i= 0; i<nr_pcidev; i++)
{
if (pcidev[i].pd_busnr != busnr)
2005-04-21 16:53:53 +02:00
continue;
pcidev[i].pd_inuse= 1;
}
return;
}
/* Look for PCI bridges */
2005-04-21 16:53:53 +02:00
do_pcibridge(busind);
/* Allocate bus numbers for uninitialized bridges */
complete_bridges();
/* Allocate I/O and memory resources for uninitialized devices */
complete_bars();
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* probe_bus *
*===========================================================================*/
PRIVATE void probe_bus(int busind)
2005-04-21 16:53:53 +02:00
{
u32_t dev, func, t3;
2005-04-21 16:53:53 +02:00
u16_t vid, did, sts;
u8_t headt;
u8_t baseclass, subclass, infclass;
int devind, busnr;
2005-04-21 16:53:53 +02:00
char *s, *dstr;
#if DEBUG
printf("probe_bus(%d)\n", busind);
#endif
if (nr_pcidev >= NR_PCIDEV)
panic("too many PCI devices: %d", nr_pcidev);
2005-04-21 16:53:53 +02:00
devind= nr_pcidev;
busnr= pcibus[busind].pb_busnr;
2005-04-21 16:53:53 +02:00
for (dev= 0; dev<32; dev++)
{
for (func= 0; func < 8; func++)
{
pcidev[devind].pd_busnr= busnr;
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_dev= dev;
pcidev[devind].pd_func= func;
pci_attr_wsts(devind,
PSR_SSE|PSR_RMAS|PSR_RTAS);
vid= pci_attr_r16(devind, PCI_VID);
did= pci_attr_r16(devind, PCI_DID);
headt= pci_attr_r8_u(devind, PCI_HEADT);
2005-04-21 16:53:53 +02:00
sts= pci_attr_rsts(devind);
2005-08-09 13:23:41 +02:00
#if 0
printf("vid 0x%x, did 0x%x, headt 0x%x, sts 0x%x\n",
vid, did, headt, sts);
#endif
if (vid == NO_VID && did == NO_VID)
2006-01-27 14:18:07 +01:00
{
if (func == 0)
break; /* Nothing here */
/* Scan all functions of a multifunction
* device.
*/
continue;
}
2005-08-09 13:23:41 +02:00
2005-04-21 16:53:53 +02:00
if (sts & (PSR_SSE|PSR_RMAS|PSR_RTAS))
{
static int warned = 0;
if(!warned) {
printf(
"PCI: ignoring bad value 0x%x in sts for QEMU\n",
sts & (PSR_SSE|PSR_RMAS|PSR_RTAS));
warned = 1;
}
2005-04-21 16:53:53 +02:00
}
dstr= pci_dev_name(vid, did);
if (debug)
{
if (dstr)
{
printf("%d.%lu.%lu: %s (%04X/%04X)\n",
busind, (unsigned long)dev,
(unsigned long)func, dstr,
vid, did);
}
else
{
printf(
"%d.%lu.%lu: Unknown device, vendor %04X (%s), device %04X\n",
busind, (unsigned long)dev,
(unsigned long)func, vid,
pci_vid_name(vid), did);
}
printf("Device index: %d\n", devind);
printf("Subsystem: Vid 0x%x, did 0x%x\n",
pci_attr_r16(devind, PCI_SUBVID),
pci_attr_r16(devind, PCI_SUBDID));
2005-04-21 16:53:53 +02:00
}
baseclass= pci_attr_r8_u(devind, PCI_BCR);
subclass= pci_attr_r8_u(devind, PCI_SCR);
infclass= pci_attr_r8_u(devind, PCI_PIFR);
2005-04-21 16:53:53 +02:00
s= pci_subclass_name(baseclass, subclass, infclass);
if (!s)
s= pci_baseclass_name(baseclass);
{
if (!s)
s= "(unknown class)";
}
if (debug)
{
printf("\tclass %s (%X/%X/%X)\n", s,
baseclass, subclass, infclass);
}
if (is_duplicate(busnr, dev, func))
{
printf("\tduplicate!\n");
if (func == 0 && !(headt & PHT_MULTIFUNC))
break;
continue;
}
2005-04-21 16:53:53 +02:00
devind= nr_pcidev;
nr_pcidev++;
pcidev[devind].pd_baseclass= baseclass;
pcidev[devind].pd_subclass= subclass;
pcidev[devind].pd_infclass= infclass;
pcidev[devind].pd_vid= vid;
pcidev[devind].pd_did= did;
pcidev[devind].pd_inuse= 0;
pcidev[devind].pd_bar_nr= 0;
record_irq(devind);
switch(headt & PHT_MASK)
{
case PHT_NORMAL:
2010-01-13 11:52:47 +01:00
record_bars_normal(devind);
break;
case PHT_BRIDGE:
record_bars_bridge(devind);
break;
case PHT_CARDBUS:
record_bars_cardbus(devind);
break;
default:
printf("\t%d.%d.%d: unknown header type %d\n",
busind, dev, func,
headt & PHT_MASK);
break;
}
2006-03-06 16:19:51 +01:00
if (debug)
print_capabilities(devind);
t3= ((baseclass << 16) | (subclass << 8) | infclass);
#if 0
if (t3 == PCI_T3_VGA || t3 == PCI_T3_VGA_OLD)
report_vga(devind);
#endif
2005-04-21 16:53:53 +02:00
if (nr_pcidev >= NR_PCIDEV)
panic("too many PCI devices: %d", nr_pcidev);
2005-04-21 16:53:53 +02:00
devind= nr_pcidev;
if (func == 0 && !(headt & PHT_MULTIFUNC))
break;
}
}
}
/*===========================================================================*
* is_duplicate *
*===========================================================================*/
PRIVATE int is_duplicate(busnr, dev, func)
u8_t busnr;
u8_t dev;
u8_t func;
{
int i;
for (i= 0; i<nr_pcidev; i++)
{
if (pcidev[i].pd_busnr == busnr &&
pcidev[i].pd_dev == dev &&
pcidev[i].pd_func == func)
{
return 1;
}
}
return 0;
}
/*===========================================================================*
* record_irq *
*===========================================================================*/
PRIVATE void record_irq(devind)
int devind;
{
int ilr, ipr, busnr, busind, cb_devind;
ilr= pci_attr_r8_u(devind, PCI_ILR);
ipr= pci_attr_r8_u(devind, PCI_IPR);
if (ilr == 0)
{
static int first= 1;
if (ipr && first && debug)
{
first= 0;
printf("PCI: strange, BIOS assigned IRQ0\n");
}
ilr= PCI_ILR_UNKNOWN;
}
pcidev[devind].pd_ilr= ilr;
if (ilr == PCI_ILR_UNKNOWN && !ipr)
{
}
else if (ilr != PCI_ILR_UNKNOWN && ipr)
{
if (debug)
printf("\tIRQ %d for INT%c\n", ilr, 'A' + ipr-1);
}
else if (ilr != PCI_ILR_UNKNOWN)
{
printf(
"PCI: IRQ %d is assigned, but device %d.%d.%d does not need it\n",
ilr, pcidev[devind].pd_busnr, pcidev[devind].pd_dev,
pcidev[devind].pd_func);
}
else
{
/* Check for cardbus devices */
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
if (pcibus[busind].pb_type == PBT_CARDBUS)
{
cb_devind= pcibus[busind].pb_devind;
ilr= pcidev[cb_devind].pd_ilr;
if (ilr != PCI_ILR_UNKNOWN)
{
if (debug)
{
printf(
"assigning IRQ %d to Cardbus device\n",
ilr);
}
pci_attr_w8(devind, PCI_ILR, ilr);
pcidev[devind].pd_ilr= ilr;
return;
}
}
if(debug) {
printf(
"PCI: device %d.%d.%d uses INT%c but is not assigned any IRQ\n",
pcidev[devind].pd_busnr, pcidev[devind].pd_dev,
pcidev[devind].pd_func, 'A' + ipr-1);
}
}
}
/*===========================================================================*
2010-01-13 11:52:47 +01:00
* record_bars_normal *
*===========================================================================*/
2010-01-13 11:52:47 +01:00
PRIVATE void record_bars_normal(devind)
int devind;
{
2010-01-13 11:52:47 +01:00
int i, j, clear_01, clear_23, pb_nr;
2010-01-13 11:52:47 +01:00
/* The BAR area of normal devices is six DWORDs in size. */
record_bars(devind, PCI_BAR_6);
/* Special case code for IDE controllers in compatibility mode */
if (pcidev[devind].pd_baseclass == PCI_BCR_MASS_STORAGE &&
pcidev[devind].pd_subclass == PCI_MS_IDE)
{
/* IDE device */
clear_01= 0;
clear_23= 0;
if (!(pcidev[devind].pd_infclass & PCI_IDE_PRI_NATIVE))
{
if (debug)
{
printf(
"primary channel is not in native mode, clearing BARs 0 and 1\n");
}
clear_01= 1;
}
if (!(pcidev[devind].pd_infclass & PCI_IDE_SEC_NATIVE))
{
if (debug)
{
printf(
2007-02-21 18:01:43 +01:00
"secondary channel is not in native mode, clearing BARs 2 and 3\n");
}
clear_23= 1;
}
j= 0;
for (i= 0; i<pcidev[devind].pd_bar_nr; i++)
{
pb_nr= pcidev[devind].pd_bar[i].pb_nr;
if ((pb_nr == 0 || pb_nr == 1) && clear_01)
{
if (debug) printf("skipping bar %d\n", pb_nr);
continue; /* Skip */
}
if ((pb_nr == 2 || pb_nr == 3) && clear_23)
{
if (debug) printf("skipping bar %d\n", pb_nr);
continue; /* Skip */
}
if (i == j)
2006-11-01 15:55:00 +01:00
{
j++;
continue; /* No need to copy */
2006-11-01 15:55:00 +01:00
}
pcidev[devind].pd_bar[j]=
pcidev[devind].pd_bar[i];
j++;
}
pcidev[devind].pd_bar_nr= j;
}
}
/*===========================================================================*
* record_bars_bridge *
*===========================================================================*/
PRIVATE void record_bars_bridge(devind)
int devind;
{
u32_t base, limit, size;
2010-01-13 11:52:47 +01:00
/* The generic BAR area of PCI-to-PCI bridges is two DWORDs in size.
* It may contain up to two 32-bit BARs, or one 64-bit BAR.
*/
record_bars(devind, PCI_BAR_2);
base= ((pci_attr_r8_u(devind, PPB_IOBASE) & PPB_IOB_MASK) << 8) |
(pci_attr_r16(devind, PPB_IOBASEU16) << 16);
limit= 0xff |
((pci_attr_r8_u(devind, PPB_IOLIMIT) & PPB_IOL_MASK) << 8) |
((~PPB_IOL_MASK & 0xff) << 8) |
(pci_attr_r16(devind, PPB_IOLIMITU16) << 16);
size= limit-base + 1;
if (debug)
{
printf("\tI/O window: base 0x%x, limit 0x%x, size %d\n",
base, limit, size);
}
base= ((pci_attr_r16(devind, PPB_MEMBASE) & PPB_MEMB_MASK) << 16);
limit= 0xffff |
((pci_attr_r16(devind, PPB_MEMLIMIT) & PPB_MEML_MASK) << 16) |
((~PPB_MEML_MASK & 0xffff) << 16);
size= limit-base + 1;
if (debug)
{
printf("\tMemory window: base 0x%x, limit 0x%x, size 0x%x\n",
base, limit, size);
}
/* Ignore the upper 32 bits */
base= ((pci_attr_r16(devind, PPB_PFMEMBASE) & PPB_PFMEMB_MASK) << 16);
limit= 0xffff |
((pci_attr_r16(devind, PPB_PFMEMLIMIT) &
PPB_PFMEML_MASK) << 16) |
((~PPB_PFMEML_MASK & 0xffff) << 16);
size= limit-base + 1;
if (debug)
{
printf(
"\tPrefetchable memory window: base 0x%x, limit 0x%x, size 0x%x\n",
base, limit, size);
}
}
/*===========================================================================*
* record_bars_cardbus *
*===========================================================================*/
PRIVATE void record_bars_cardbus(devind)
int devind;
{
u32_t base, limit, size;
2010-01-13 11:52:47 +01:00
/* The generic BAR area of CardBus devices is one DWORD in size. */
record_bars(devind, PCI_BAR);
base= pci_attr_r32_u(devind, CBB_MEMBASE_0);
limit= pci_attr_r32_u(devind, CBB_MEMLIMIT_0) |
(~CBB_MEML_MASK & 0xffffffff);
size= limit-base + 1;
if (debug)
{
printf("\tMemory window 0: base 0x%x, limit 0x%x, size %d\n",
base, limit, size);
}
base= pci_attr_r32_u(devind, CBB_MEMBASE_1);
limit= pci_attr_r32_u(devind, CBB_MEMLIMIT_1) |
(~CBB_MEML_MASK & 0xffffffff);
size= limit-base + 1;
if (debug)
{
printf("\tMemory window 1: base 0x%x, limit 0x%x, size %d\n",
base, limit, size);
}
base= pci_attr_r32_u(devind, CBB_IOBASE_0);
limit= pci_attr_r32_u(devind, CBB_IOLIMIT_0) |
(~CBB_IOL_MASK & 0xffffffff);
size= limit-base + 1;
if (debug)
{
printf("\tI/O window 0: base 0x%x, limit 0x%x, size %d\n",
base, limit, size);
}
base= pci_attr_r32_u(devind, CBB_IOBASE_1);
limit= pci_attr_r32_u(devind, CBB_IOLIMIT_1) |
(~CBB_IOL_MASK & 0xffffffff);
size= limit-base + 1;
if (debug)
{
printf("\tI/O window 1: base 0x%x, limit 0x%x, size %d\n",
base, limit, size);
}
}
2010-01-13 11:52:47 +01:00
/*===========================================================================*
* record_bars *
*===========================================================================*/
PRIVATE void record_bars(int devind, int last_reg)
2010-01-13 11:52:47 +01:00
{
int i, reg, width;
for (i= 0, reg= PCI_BAR; reg <= last_reg; i += width, reg += 4 * width)
{
width = record_bar(devind, i, reg == last_reg);
}
}
/*===========================================================================*
* record_bar *
*===========================================================================*/
2010-01-13 11:52:47 +01:00
PRIVATE int record_bar(devind, bar_nr, last)
int devind;
int bar_nr;
2010-01-13 11:52:47 +01:00
int last;
{
2010-01-13 11:52:47 +01:00
int reg, prefetch, type, dev_bar_nr, width;
u32_t bar, bar2;
u16_t cmd;
2010-01-13 11:52:47 +01:00
/* Start by assuming that this is a 32-bit bar, taking up one DWORD. */
width = 1;
reg= PCI_BAR+4*bar_nr;
bar= pci_attr_r32_u(devind, reg);
if (bar & PCI_BAR_IO)
{
/* Disable I/O access before probing for BAR's size */
cmd = pci_attr_r16(devind, PCI_CR);
pci_attr_w16(devind, PCI_CR, cmd & ~PCI_CR_IO_EN);
/* Probe BAR's size */
pci_attr_w32(devind, reg, 0xffffffff);
bar2= pci_attr_r32_u(devind, reg);
/* Restore original state */
pci_attr_w32(devind, reg, bar);
pci_attr_w16(devind, PCI_CR, cmd);
bar &= ~(u32_t)3; /* Clear non-address bits */
bar2 &= ~(u32_t)3;
bar2= (~bar2 & 0xffff)+1;
if (debug)
{
printf("\tbar_%d: %d bytes at 0x%x I/O\n",
bar_nr, bar2, bar);
}
dev_bar_nr= pcidev[devind].pd_bar_nr++;
pcidev[devind].pd_bar[dev_bar_nr].pb_flags= PBF_IO;
pcidev[devind].pd_bar[dev_bar_nr].pb_base= bar;
pcidev[devind].pd_bar[dev_bar_nr].pb_size= bar2;
pcidev[devind].pd_bar[dev_bar_nr].pb_nr= bar_nr;
if (bar == 0)
{
pcidev[devind].pd_bar[dev_bar_nr].pb_flags |=
PBF_INCOMPLETE;
}
}
else
{
2010-01-13 11:52:47 +01:00
type= (bar & PCI_BAR_TYPE);
switch(type) {
case PCI_TYPE_32:
case PCI_TYPE_32_1M:
break;
case PCI_TYPE_64:
/* A 64-bit BAR takes up two consecutive DWORDs. */
if (last)
{
printf("PCI: device %d.%d.%d BAR %d extends"
" beyond designated area\n",
pcidev[devind].pd_busnr,
pcidev[devind].pd_dev,
pcidev[devind].pd_func, bar_nr);
return width;
}
width++;
bar2= pci_attr_r32_u(devind, reg+4);
/* If the upper 32 bits of the BAR are not zero, the
* memory is inaccessible to us; ignore the BAR.
*/
if (bar2 != 0)
{
if (debug)
{
printf("\tbar_%d: (64-bit BAR with"
" high bits set)\n", bar_nr);
}
return width;
}
break;
default:
/* Ignore the BAR. */
if (debug)
{
printf("\tbar_%d: (unknown type %x)\n",
bar_nr, type);
}
return width;
}
/* Disable mem access before probing for BAR's size */
cmd = pci_attr_r16(devind, PCI_CR);
pci_attr_w16(devind, PCI_CR, cmd & ~PCI_CR_MEM_EN);
/* Probe BAR's size */
pci_attr_w32(devind, reg, 0xffffffff);
bar2= pci_attr_r32_u(devind, reg);
/* Restore original values */
pci_attr_w32(devind, reg, bar);
pci_attr_w16(devind, PCI_CR, cmd);
if (bar2 == 0)
2010-01-13 11:52:47 +01:00
return width; /* Reg. is not implemented */
prefetch= !!(bar & PCI_BAR_PREFETCH);
bar &= ~(u32_t)0xf; /* Clear non-address bits */
bar2 &= ~(u32_t)0xf;
bar2= (~bar2)+1;
if (debug)
{
2010-01-13 11:52:47 +01:00
printf("\tbar_%d: 0x%x bytes at 0x%x%s memory%s\n",
bar_nr, bar2, bar,
2010-01-13 11:52:47 +01:00
prefetch ? " prefetchable" : "",
type == PCI_TYPE_64 ? ", 64-bit" : "");
}
dev_bar_nr= pcidev[devind].pd_bar_nr++;
pcidev[devind].pd_bar[dev_bar_nr].pb_flags= 0;
pcidev[devind].pd_bar[dev_bar_nr].pb_base= bar;
pcidev[devind].pd_bar[dev_bar_nr].pb_size= bar2;
pcidev[devind].pd_bar[dev_bar_nr].pb_nr= bar_nr;
if (bar == 0)
{
pcidev[devind].pd_bar[dev_bar_nr].pb_flags |=
PBF_INCOMPLETE;
}
}
2010-01-13 11:52:47 +01:00
return width;
}
/*===========================================================================*
* complete_bridges *
*===========================================================================*/
PRIVATE void complete_bridges()
{
int i, freebus, devind, prim_busnr;
for (i= 0; i<nr_pcibus; i++)
{
if (!pcibus[i].pb_needinit)
continue;
printf("should allocate bus number for bus %d\n", i);
freebus= get_freebus();
printf("got bus number %d\n", freebus);
devind= pcibus[i].pb_devind;
prim_busnr= pcidev[devind].pd_busnr;
if (prim_busnr != 0)
{
printf(
"complete_bridge: updating subordinate bus number not implemented\n");
}
pcibus[i].pb_needinit= 0;
pcibus[i].pb_busnr= freebus;
printf("devind = %d\n", devind);
printf("prim_busnr= %d\n", prim_busnr);
pci_attr_w8(devind, PPB_PRIMBN, prim_busnr);
pci_attr_w8(devind, PPB_SECBN, freebus);
pci_attr_w8(devind, PPB_SUBORDBN, freebus);
printf("CR = 0x%x\n", pci_attr_r16(devind, PCI_CR));
printf("SECBLT = 0x%x\n", pci_attr_r8_u(devind, PPB_SECBLT));
printf("BRIDGECTRL = 0x%x\n",
pci_attr_r16(devind, PPB_BRIDGECTRL));
}
}
/*===========================================================================*
* complete_bars *
*===========================================================================*/
PRIVATE void complete_bars(void)
{
int i, j, r, bar_nr, reg;
u32_t memgap_low, memgap_high, iogap_low, iogap_high, io_high,
base, size, v32, diff1, diff2;
char *cp, *next;
char memstr[256];
r= env_get_param("memory", memstr, sizeof(memstr));
if (r != OK)
panic("env_get_param failed: %d", r);
/* Set memgap_low to just above physical memory */
memgap_low= 0;
cp= memstr;
while (*cp != '\0')
{
base= strtoul(cp, &next, 16);
if (!(*next) || next == cp || *next != ':')
goto bad_mem_string;
cp= next+1;
size= strtoul(cp, &next, 16);
if (next == cp || (*next != ',' && *next != '\0'))
if (!*next)
goto bad_mem_string;
if (base+size > memgap_low)
memgap_low= base+size;
if (*next)
cp= next+1;
else
break;
}
memgap_high= 0xfe000000; /* Leave space for the CPU (APIC) */
if (debug)
{
printf("complete_bars: initial gap: [0x%x .. 0x%x>\n",
memgap_low, memgap_high);
}
/* Find the lowest memory base */
for (i= 0; i<nr_pcidev; i++)
{
for (j= 0; j<pcidev[i].pd_bar_nr; j++)
{
if (pcidev[i].pd_bar[j].pb_flags & PBF_IO)
continue;
if (pcidev[i].pd_bar[j].pb_flags & PBF_INCOMPLETE)
continue;
base= pcidev[i].pd_bar[j].pb_base;
size= pcidev[i].pd_bar[j].pb_size;
if (base >= memgap_high)
continue; /* Not in the gap */
if (base+size <= memgap_low)
continue; /* Not in the gap */
/* Reduce the gap by the smallest amount */
diff1= base+size-memgap_low;
diff2= memgap_high-base;
if (diff1 < diff2)
memgap_low= base+size;
else
memgap_high= base;
}
}
if (debug)
{
printf("complete_bars: intermediate gap: [0x%x .. 0x%x>\n",
memgap_low, memgap_high);
}
/* Should check main memory size */
if (memgap_high < memgap_low)
{
printf("PCI: bad memory gap: [0x%x .. 0x%x>\n",
memgap_low, memgap_high);
panic(NULL);
}
iogap_high= 0x10000;
iogap_low= 0x400;
/* Find the free I/O space */
for (i= 0; i<nr_pcidev; i++)
{
for (j= 0; j<pcidev[i].pd_bar_nr; j++)
{
if (!(pcidev[i].pd_bar[j].pb_flags & PBF_IO))
continue;
if (pcidev[i].pd_bar[j].pb_flags & PBF_INCOMPLETE)
continue;
base= pcidev[i].pd_bar[j].pb_base;
size= pcidev[i].pd_bar[j].pb_size;
if (base >= iogap_high)
continue;
if (base+size <= iogap_low)
continue;
#if 0
2006-04-11 13:50:29 +02:00
if (debug)
{
printf(
"pci device %d (%04x/%04x), bar %d: base 0x%x, size 0x%x\n",
i, pcidev[i].pd_vid, pcidev[i].pd_did,
j, base, size);
}
#endif
if (base+size-iogap_low < iogap_high-base)
iogap_low= base+size;
else
iogap_high= base;
}
}
if (iogap_high < iogap_low)
2006-04-11 13:50:29 +02:00
{
if (debug)
{
printf("iogap_high too low, should panic\n");
}
else
panic("iogap_high too low: %d", iogap_high);
2006-04-11 13:50:29 +02:00
}
if (debug)
printf("I/O range = [0x%x..0x%x>\n", iogap_low, iogap_high);
for (i= 0; i<nr_pcidev; i++)
{
for (j= 0; j<pcidev[i].pd_bar_nr; j++)
{
if (pcidev[i].pd_bar[j].pb_flags & PBF_IO)
continue;
if (!(pcidev[i].pd_bar[j].pb_flags & PBF_INCOMPLETE))
continue;
size= pcidev[i].pd_bar[j].pb_size;
if (size < I386_PAGE_SIZE)
size= I386_PAGE_SIZE;
base= memgap_high-size;
base &= ~(u32_t)(size-1);
if (base < memgap_low)
panic("memory base too low: %d", base);
memgap_high= base;
bar_nr= pcidev[i].pd_bar[j].pb_nr;
reg= PCI_BAR + 4*bar_nr;
v32= pci_attr_r32_u(i, reg);
pci_attr_w32(i, reg, v32 | base);
if (debug)
{
printf(
"complete_bars: allocated 0x%x size %d to %d.%d.%d, bar_%d\n",
base, size, pcidev[i].pd_busnr,
pcidev[i].pd_dev, pcidev[i].pd_func,
bar_nr);
}
pcidev[i].pd_bar[j].pb_base= base;
pcidev[i].pd_bar[j].pb_flags &= ~PBF_INCOMPLETE;
}
io_high= iogap_high;
for (j= 0; j<pcidev[i].pd_bar_nr; j++)
{
if (!(pcidev[i].pd_bar[j].pb_flags & PBF_IO))
continue;
if (!(pcidev[i].pd_bar[j].pb_flags & PBF_INCOMPLETE))
continue;
size= pcidev[i].pd_bar[j].pb_size;
base= iogap_high-size;
base &= ~(u32_t)(size-1);
/* Assume that ISA compatibility is required. Only
* use the lowest 256 bytes out of every 1024 bytes.
*/
base &= 0xfcff;
if (base < iogap_low)
panic("I/O base too low: %d", base);
iogap_high= base;
bar_nr= pcidev[i].pd_bar[j].pb_nr;
reg= PCI_BAR + 4*bar_nr;
v32= pci_attr_r32_u(i, reg);
pci_attr_w32(i, reg, v32 | base);
if (debug)
{
printf(
"complete_bars: allocated 0x%x size %d to %d.%d.%d, bar_%d\n",
base, size, pcidev[i].pd_busnr,
pcidev[i].pd_dev, pcidev[i].pd_func,
bar_nr);
}
pcidev[i].pd_bar[j].pb_base= base;
pcidev[i].pd_bar[j].pb_flags &= ~PBF_INCOMPLETE;
}
if (iogap_high != io_high)
{
update_bridge4dev_io(i, iogap_high,
io_high-iogap_high);
}
}
for (i= 0; i<nr_pcidev; i++)
{
for (j= 0; j<pcidev[i].pd_bar_nr; j++)
{
if (!(pcidev[i].pd_bar[j].pb_flags & PBF_INCOMPLETE))
continue;
printf("should allocate resources for device %d\n", i);
}
}
return;
bad_mem_string:
printf("PCI: bad memory environment string '%s'\n", memstr);
panic(NULL);
}
/*===========================================================================*
* update_bridge4dev_io *
*===========================================================================*/
PRIVATE void update_bridge4dev_io(devind, io_base, io_size)
int devind;
u32_t io_base;
u32_t io_size;
{
int busnr, busind, type, br_devind;
u16_t v16;
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
type= pcibus[busind].pb_type;
if (type == PBT_INTEL_HOST)
return; /* Nothing to do for host controller */
if (type == PBT_PCIBRIDGE)
{
printf(
"update_bridge4dev_io: not implemented for PCI bridges\n");
return;
}
if (type != PBT_CARDBUS)
panic("update_bridge4dev_io: strange bus type: %d", type);
if (debug)
{
printf("update_bridge4dev_io: adding 0x%x at 0x%x\n",
io_size, io_base);
}
br_devind= pcibus[busind].pb_devind;
pci_attr_w32(br_devind, CBB_IOLIMIT_0, io_base+io_size-1);
pci_attr_w32(br_devind, CBB_IOBASE_0, io_base);
/* Enable I/O access. Enable busmaster access as well. */
v16= pci_attr_r16(devind, PCI_CR);
pci_attr_w16(devind, PCI_CR, v16 | PCI_CR_IO_EN | PCI_CR_MAST_EN);
}
/*===========================================================================*
* get_freebus *
*===========================================================================*/
PRIVATE int get_freebus()
{
int i, freebus;
freebus= 1;
for (i= 0; i<nr_pcibus; i++)
{
if (pcibus[i].pb_needinit)
continue;
if (pcibus[i].pb_type == PBT_INTEL_HOST)
continue;
if (pcibus[i].pb_busnr <= freebus)
freebus= pcibus[i].pb_busnr+1;
printf("get_freebus: should check suboridinate bus number\n");
}
return freebus;
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* do_isabridge *
*===========================================================================*/
PRIVATE int do_isabridge(busind)
int busind;
{
int i, j, r, type, busnr, unknown_bridge, bridge_dev;
2005-04-21 16:53:53 +02:00
u16_t vid, did;
u32_t t3;
2005-04-21 16:53:53 +02:00
char *dstr;
unknown_bridge= -1;
bridge_dev= -1;
2005-04-21 16:53:53 +02:00
j= 0; /* lint */
vid= did= 0; /* lint */
busnr= pcibus[busind].pb_busnr;
2005-04-21 16:53:53 +02:00
for (i= 0; i< nr_pcidev; i++)
{
if (pcidev[i].pd_busnr != busnr)
2005-04-21 16:53:53 +02:00
continue;
t3= ((pcidev[i].pd_baseclass << 16) |
(pcidev[i].pd_subclass << 8) | pcidev[i].pd_infclass);
if (t3 == PCI_T3_ISA)
2005-04-21 16:53:53 +02:00
{
/* ISA bridge. Report if no supported bridge is
* found.
*/
unknown_bridge= i;
}
vid= pcidev[i].pd_vid;
did= pcidev[i].pd_did;
for (j= 0; pci_isabridge[j].vid != 0; j++)
{
if (pci_isabridge[j].vid != vid)
continue;
if (pci_isabridge[j].did != did)
continue;
if (pci_isabridge[j].checkclass &&
unknown_bridge != i)
{
/* This part of multifunction device is
* not the bridge.
*/
continue;
}
break;
}
if (pci_isabridge[j].vid)
{
bridge_dev= i;
break;
}
}
if (bridge_dev != -1)
{
dstr= pci_dev_name(vid, did);
if (!dstr)
dstr= "unknown device";
if (debug)
{
printf("found ISA bridge (%04X/%04X) %s\n",
vid, did, dstr);
}
pcibus[busind].pb_isabridge_dev= bridge_dev;
type= pci_isabridge[j].type;
pcibus[busind].pb_isabridge_type= type;
switch(type)
{
case PCI_IB_PIIX:
r= do_piix(bridge_dev);
break;
case PCI_IB_VIA:
r= do_via_isabr(bridge_dev);
break;
case PCI_IB_AMD:
r= do_amd_isabr(bridge_dev);
break;
case PCI_IB_SIS:
r= do_sis_isabr(bridge_dev);
break;
default:
panic("unknown ISA bridge type: %d", type);
2005-04-21 16:53:53 +02:00
}
return r;
}
if (unknown_bridge == -1)
{
if (debug)
{
printf("(warning) no ISA bridge found on bus %d\n",
busind);
}
return 0;
2005-04-21 16:53:53 +02:00
}
2006-04-11 13:50:29 +02:00
if (debug)
{
printf(
"(warning) unsupported ISA bridge %04X/%04X for bus %d\n",
pcidev[unknown_bridge].pd_vid,
pcidev[unknown_bridge].pd_did, busind);
}
return 0;
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* do_pcibridge *
*===========================================================================*/
PRIVATE void do_pcibridge(busind)
int busind;
{
int i, devind, busnr;
2005-04-21 16:53:53 +02:00
int ind, type;
u16_t vid, did;
u8_t sbusn, baseclass, subclass, infclass, headt;
u32_t t3;
2005-04-21 16:53:53 +02:00
vid= did= 0; /* lint */
busnr= pcibus[busind].pb_busnr;
2005-04-21 16:53:53 +02:00
for (devind= 0; devind< nr_pcidev; devind++)
{
#if 0
printf("do_pcibridge: trying %u.%u.%u\n",
pcidev[devind].pd_busind, pcidev[devind].pd_dev,
pcidev[devind].pd_func);
#endif
if (pcidev[devind].pd_busnr != busnr)
{
#if 0
printf("wrong bus\n");
#endif
2005-04-21 16:53:53 +02:00
continue;
}
2005-04-21 16:53:53 +02:00
vid= pcidev[devind].pd_vid;
did= pcidev[devind].pd_did;
for (i= 0; pci_pcibridge[i].vid != 0; i++)
{
if (pci_pcibridge[i].vid != vid)
continue;
if (pci_pcibridge[i].did != did)
continue;
break;
}
type= pci_pcibridge[i].type;
2005-04-21 16:53:53 +02:00
if (pci_pcibridge[i].vid == 0)
{
headt= pci_attr_r8_u(devind, PCI_HEADT);
type= 0;
if ((headt & PHT_MASK) == PHT_BRIDGE)
type= PCI_PPB_STD;
else if ((headt & PHT_MASK) == PHT_CARDBUS)
type= PCI_PPB_CB;
else
{
#if 0
printf("not a bridge\n");
#endif
continue; /* Not a bridge */
}
baseclass= pci_attr_r8_u(devind, PCI_BCR);
subclass= pci_attr_r8_u(devind, PCI_SCR);
infclass= pci_attr_r8_u(devind, PCI_PIFR);
t3= ((baseclass << 16) | (subclass << 8) | infclass);
if (type == PCI_PPB_STD &&
t3 != PCI_T3_PCI2PCI &&
t3 != PCI_T3_PCI2PCI_SUBTR)
{
printf(
"Unknown PCI class %02x:%02x:%02x for PCI-to-PCI bridge, device %04X/%04X\n",
baseclass, subclass, infclass,
vid, did);
continue;
}
if (type == PCI_PPB_CB &&
t3 != PCI_T3_CARDBUS)
{
printf(
"Unknown PCI class %02x:%02x:%02x for Cardbus bridge, device %04X/%04X\n",
baseclass, subclass, infclass,
vid, did);
continue;
}
}
2005-04-21 16:53:53 +02:00
if (debug)
{
printf("%u.%u.%u: PCI-to-PCI bridge: %04X/%04X\n",
pcidev[devind].pd_busnr,
pcidev[devind].pd_dev,
pcidev[devind].pd_func, vid, did);
}
2005-04-21 16:53:53 +02:00
/* Assume that the BIOS initialized the secondary bus
* number.
*/
sbusn= pci_attr_r8_u(devind, PPB_SECBN);
2005-04-21 16:53:53 +02:00
if (nr_pcibus >= NR_PCIBUS)
panic("too many PCI busses: %d", nr_pcibus);
2005-04-21 16:53:53 +02:00
ind= nr_pcibus;
nr_pcibus++;
pcibus[ind].pb_type= PBT_PCIBRIDGE;
pcibus[ind].pb_needinit= 1;
2005-04-21 16:53:53 +02:00
pcibus[ind].pb_isabridge_dev= -1;
pcibus[ind].pb_isabridge_type= 0;
pcibus[ind].pb_devind= devind;
pcibus[ind].pb_busnr= sbusn;
2005-04-21 16:53:53 +02:00
pcibus[ind].pb_rreg8= pcibus[busind].pb_rreg8;
pcibus[ind].pb_rreg16= pcibus[busind].pb_rreg16;
pcibus[ind].pb_rreg32= pcibus[busind].pb_rreg32;
pcibus[ind].pb_wreg8= pcibus[busind].pb_wreg8;
2005-04-21 16:53:53 +02:00
pcibus[ind].pb_wreg16= pcibus[busind].pb_wreg16;
pcibus[ind].pb_wreg32= pcibus[busind].pb_wreg32;
switch(type)
{
case PCI_PPB_STD:
pcibus[ind].pb_rsts= pcibr_std_rsts;
pcibus[ind].pb_wsts= pcibr_std_wsts;
break;
case PCI_PPB_CB:
pcibus[ind].pb_type= PBT_CARDBUS;
pcibus[ind].pb_rsts= pcibr_cb_rsts;
pcibus[ind].pb_wsts= pcibr_cb_wsts;
2005-04-21 16:53:53 +02:00
break;
case PCI_AGPB_VIA:
pcibus[ind].pb_rsts= pcibr_via_rsts;
pcibus[ind].pb_wsts= pcibr_via_wsts;
break;
default:
panic("unknown PCI-PCI bridge type: %d", type);
2005-04-21 16:53:53 +02:00
}
if (debug)
{
printf(
"bus(table) = %d, bus(sec) = %d, bus(subord) = %d\n",
ind, sbusn, pci_attr_r8_u(devind, PPB_SUBORDBN));
}
if (sbusn == 0)
{
printf("Secondary bus number not initialized\n");
continue;
}
pcibus[ind].pb_needinit= 0;
2005-04-21 16:53:53 +02:00
probe_bus(ind);
/* Look for PCI bridges */
do_pcibridge(ind);
2005-04-21 16:53:53 +02:00
}
}
/*===========================================================================*
* get_busind *
*===========================================================================*/
PRIVATE int get_busind(busnr)
int busnr;
{
int i;
for (i= 0; i<nr_pcibus; i++)
{
if (pcibus[i].pb_busnr == busnr)
return i;
}
panic("get_busind: can't find bus: %d", busnr);
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* do_piix *
*===========================================================================*/
PRIVATE int do_piix(int devind)
2005-04-21 16:53:53 +02:00
{
int i, s, irqrc, irq;
2005-10-24 16:02:45 +02:00
u32_t elcr1, elcr2, elcr;
2005-04-21 16:53:53 +02:00
#if DEBUG
printf("in piix\n");
#endif
#if USER_SPACE
if (OK != (s=sys_inb(PIIX_ELCR1, &elcr1)))
printf("Warning, sys_inb failed: %d\n", s);
if (OK != (s=sys_inb(PIIX_ELCR2, &elcr2)))
printf("Warning, sys_inb failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
elcr1= inb(PIIX_ELCR1);
elcr2= inb(PIIX_ELCR2);
#endif
2005-04-21 16:53:53 +02:00
elcr= elcr1 | (elcr2 << 8);
for (i= 0; i<4; i++)
{
irqrc= pci_attr_r8_u(devind, PIIX_PIRQRCA+i);
2005-04-21 16:53:53 +02:00
if (irqrc & PIIX_IRQ_DI)
{
if (debug)
printf("INT%c: disabled\n", 'A'+i);
}
else
{
irq= irqrc & PIIX_IRQ_MASK;
if (debug)
printf("INT%c: %d\n", 'A'+i, irq);
if (!(elcr & (1 << irq)))
{
if (debug)
2005-08-09 13:23:41 +02:00
{
printf(
"(warning) IRQ %d is not level triggered\n",
irq);
2005-08-09 13:23:41 +02:00
}
2005-04-21 16:53:53 +02:00
}
irq_mode_pci(irq);
}
}
return 0;
}
/*===========================================================================*
* do_amd_isabr *
*===========================================================================*/
PRIVATE int do_amd_isabr(int devind)
2005-04-21 16:53:53 +02:00
{
int i, busnr, dev, func, xdevind, irq, edge;
2005-04-21 16:53:53 +02:00
u8_t levmask;
u16_t pciirq;
/* Find required function */
func= AMD_ISABR_FUNC;
busnr= pcidev[devind].pd_busnr;
2005-04-21 16:53:53 +02:00
dev= pcidev[devind].pd_dev;
/* Fake a device with the required function */
if (nr_pcidev >= NR_PCIDEV)
panic("too many PCI devices: %d", nr_pcidev);
2005-04-21 16:53:53 +02:00
xdevind= nr_pcidev;
pcidev[xdevind].pd_busnr= busnr;
2005-04-21 16:53:53 +02:00
pcidev[xdevind].pd_dev= dev;
pcidev[xdevind].pd_func= func;
pcidev[xdevind].pd_inuse= 1;
nr_pcidev++;
levmask= pci_attr_r8_u(xdevind, AMD_ISABR_PCIIRQ_LEV);
2005-04-21 16:53:53 +02:00
pciirq= pci_attr_r16(xdevind, AMD_ISABR_PCIIRQ_ROUTE);
for (i= 0; i<4; i++)
{
edge= (levmask >> i) & 1;
irq= (pciirq >> (4*i)) & 0xf;
if (!irq)
{
if (debug)
printf("INT%c: disabled\n", 'A'+i);
}
else
{
if (debug)
printf("INT%c: %d\n", 'A'+i, irq);
if (edge && debug)
2005-04-21 16:53:53 +02:00
{
printf(
"(warning) IRQ %d is not level triggered\n",
2005-04-21 16:53:53 +02:00
irq);
}
irq_mode_pci(irq);
}
}
nr_pcidev--;
return 0;
}
/*===========================================================================*
* do_sis_isabr *
*===========================================================================*/
PRIVATE int do_sis_isabr(int devind)
2005-04-21 16:53:53 +02:00
{
int i, irq;
2005-04-21 16:53:53 +02:00
irq= 0; /* lint */
for (i= 0; i<4; i++)
{
irq= pci_attr_r8_u(devind, SIS_ISABR_IRQ_A+i);
2005-04-21 16:53:53 +02:00
if (irq & SIS_IRQ_DISABLED)
{
if (debug)
printf("INT%c: disabled\n", 'A'+i);
}
else
{
irq &= SIS_IRQ_MASK;
if (debug)
printf("INT%c: %d\n", 'A'+i, irq);
irq_mode_pci(irq);
}
}
return 0;
}
/*===========================================================================*
* do_via_isabr *
*===========================================================================*/
PRIVATE int do_via_isabr(int devind)
2005-04-21 16:53:53 +02:00
{
int i, irq, edge;
2005-04-21 16:53:53 +02:00
u8_t levmask;
levmask= pci_attr_r8_u(devind, VIA_ISABR_EL);
2005-04-21 16:53:53 +02:00
irq= 0; /* lint */
edge= 0; /* lint */
for (i= 0; i<4; i++)
{
switch(i)
{
case 0:
edge= (levmask & VIA_ISABR_EL_INTA);
irq= pci_attr_r8_u(devind, VIA_ISABR_IRQ_R2) >> 4;
2005-04-21 16:53:53 +02:00
break;
case 1:
edge= (levmask & VIA_ISABR_EL_INTB);
irq= pci_attr_r8_u(devind, VIA_ISABR_IRQ_R2);
2005-04-21 16:53:53 +02:00
break;
case 2:
edge= (levmask & VIA_ISABR_EL_INTC);
irq= pci_attr_r8_u(devind, VIA_ISABR_IRQ_R3) >> 4;
2005-04-21 16:53:53 +02:00
break;
case 3:
edge= (levmask & VIA_ISABR_EL_INTD);
irq= pci_attr_r8_u(devind, VIA_ISABR_IRQ_R1) >> 4;
2005-04-21 16:53:53 +02:00
break;
default:
assert(0);
2005-04-21 16:53:53 +02:00
}
irq &= 0xf;
if (!irq)
{
if (debug)
printf("INT%c: disabled\n", 'A'+i);
}
else
{
if (debug)
printf("INT%c: %d\n", 'A'+i, irq);
if (edge && debug)
2005-04-21 16:53:53 +02:00
{
printf(
"(warning) IRQ %d is not level triggered\n",
2005-04-21 16:53:53 +02:00
irq);
}
irq_mode_pci(irq);
}
}
return 0;
}
#if 0
/*===========================================================================*
* report_vga *
*===========================================================================*/
PRIVATE void report_vga(devind)
int devind;
{
/* Report the amount of video memory. This is needed by the X11R6
* postinstall script to chmem the X server. Hopefully this can be
* removed when we get virtual memory.
*/
size_t amount, size;
int i;
amount= 0;
for (i= 0; i<pcidev[devind].pd_bar_nr; i++)
{
if (pcidev[devind].pd_bar[i].pb_flags & PBF_IO)
continue;
size= pcidev[devind].pd_bar[i].pb_size;
if (size < amount)
continue;
amount= size;
}
if (size != 0)
{
printf("PCI: video memory for device at %d.%d.%d: %d bytes\n",
pcidev[devind].pd_busnr,
pcidev[devind].pd_dev,
pcidev[devind].pd_func,
amount);
}
}
#endif
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* pci_vid_name *
*===========================================================================*/
PRIVATE char *pci_vid_name(vid)
u16_t vid;
{
int i;
for (i= 0; pci_vendor_table[i].name; i++)
{
if (pci_vendor_table[i].vid == vid)
return pci_vendor_table[i].name;
}
return "unknown";
}
/*===========================================================================*
* pci_baseclass_name *
*===========================================================================*/
PRIVATE char *pci_baseclass_name(baseclass)
u8_t baseclass;
{
int i;
for (i= 0; pci_baseclass_table[i].name; i++)
{
if (pci_baseclass_table[i].baseclass == baseclass)
return pci_baseclass_table[i].name;
}
return NULL;
}
/*===========================================================================*
* pci_subclass_name *
*===========================================================================*/
PRIVATE char *pci_subclass_name(baseclass, subclass, infclass)
u8_t baseclass;
u8_t subclass;
u8_t infclass;
{
int i;
for (i= 0; pci_subclass_table[i].name; i++)
{
if (pci_subclass_table[i].baseclass != baseclass)
continue;
if (pci_subclass_table[i].subclass != subclass)
continue;
if (pci_subclass_table[i].infclass != infclass &&
pci_subclass_table[i].infclass != (u16_t)-1)
{
continue;
}
return pci_subclass_table[i].name;
}
return NULL;
}
/*===========================================================================*
* ntostr *
*===========================================================================*/
PRIVATE void ntostr(n, str, end)
unsigned n;
char **str;
char *end;
{
char tmpstr[20];
int i;
if (n == 0)
{
tmpstr[0]= '0';
i= 1;
}
else
{
for (i= 0; n; i++)
{
tmpstr[i]= '0' + (n%10);
n /= 10;
}
}
for (; i>0; i--)
{
if (*str == end)
{
break;
}
**str= tmpstr[i-1];
(*str)++;
}
if (*str == end)
end[-1]= '\0';
else
**str= '\0';
}
/*===========================================================================*
* pci_attr_rsts *
*===========================================================================*/
PRIVATE u16_t pci_attr_rsts(devind)
int devind;
{
int busnr, busind;
2005-04-21 16:53:53 +02:00
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
2005-04-21 16:53:53 +02:00
return pcibus[busind].pb_rsts(busind);
}
/*===========================================================================*
* pcibr_std_rsts *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PRIVATE u16_t pcibr_std_rsts(busind)
2005-04-21 16:53:53 +02:00
int busind;
{
int devind;
devind= pcibus[busind].pb_devind;
2005-04-21 16:53:53 +02:00
return pci_attr_r16(devind, PPB_SSTS);
}
/*===========================================================================*
* pcibr_std_wsts *
2005-04-21 16:53:53 +02:00
*===========================================================================*/
PRIVATE void pcibr_std_wsts(busind, value)
2005-04-21 16:53:53 +02:00
int busind;
u16_t value;
{
int devind;
devind= pcibus[busind].pb_devind;
#if 0
printf("pcibr_std_wsts(%d, 0x%X), devind= %d\n",
2005-04-21 16:53:53 +02:00
busind, value, devind);
#endif
pci_attr_w16(devind, PPB_SSTS, value);
}
/*===========================================================================*
* pcibr_cb_rsts *
*===========================================================================*/
PRIVATE u16_t pcibr_cb_rsts(busind)
int busind;
{
int devind;
devind= pcibus[busind].pb_devind;
return pci_attr_r16(devind, CBB_SSTS);
}
/*===========================================================================*
* pcibr_cb_wsts *
*===========================================================================*/
PRIVATE void pcibr_cb_wsts(busind, value)
int busind;
u16_t value;
{
int devind;
devind= pcibus[busind].pb_devind;
#if 0
printf("pcibr_cb_wsts(%d, 0x%X), devind= %d\n",
busind, value, devind);
#endif
pci_attr_w16(devind, CBB_SSTS, value);
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* pcibr_via_rsts *
*===========================================================================*/
PRIVATE u16_t pcibr_via_rsts(busind)
int busind;
{
int devind;
devind= pcibus[busind].pb_devind;
return 0;
}
/*===========================================================================*
* pcibr_via_wsts *
*===========================================================================*/
PRIVATE void pcibr_via_wsts(busind, value)
int busind;
u16_t value;
{
int devind;
devind= pcibus[busind].pb_devind;
#if 0
printf("pcibr_via_wsts(%d, 0x%X), devind= %d (not implemented)\n",
busind, value, devind);
#endif
}
/*===========================================================================*
* pci_attr_wsts *
*===========================================================================*/
PRIVATE void pci_attr_wsts(int devind, u16_t value)
2005-04-21 16:53:53 +02:00
{
int busnr, busind;
2005-04-21 16:53:53 +02:00
busnr= pcidev[devind].pd_busnr;
busind= get_busind(busnr);
2005-04-21 16:53:53 +02:00
pcibus[busind].pb_wsts(busind, value);
}
/*===========================================================================*
* pcii_rreg8 *
*===========================================================================*/
PRIVATE u8_t pcii_rreg8(busind, devind, port)
int busind;
int devind;
int port;
{
u8_t v;
int s;
2005-04-21 16:53:53 +02:00
v= PCII_RREG8_(pcibus[busind].pb_busnr,
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_dev, pcidev[devind].pd_func,
port);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
#if 0
printf("pcii_rreg8(%d, %d, 0x%X): %d.%d.%d= 0x%X\n",
busind, devind, port,
pcibus[busind].pb_bus, pcidev[devind].pd_dev,
pcidev[devind].pd_func, v);
#endif
return v;
}
/*===========================================================================*
* pcii_rreg16 *
*===========================================================================*/
PRIVATE u16_t pcii_rreg16(busind, devind, port)
int busind;
int devind;
int port;
{
u16_t v;
int s;
2005-04-21 16:53:53 +02:00
v= PCII_RREG16_(pcibus[busind].pb_busnr,
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_dev, pcidev[devind].pd_func,
port);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
#if 0
printf("pcii_rreg16(%d, %d, 0x%X): %d.%d.%d= 0x%X\n",
busind, devind, port,
pcibus[busind].pb_bus, pcidev[devind].pd_dev,
pcidev[devind].pd_func, v);
#endif
return v;
}
/*===========================================================================*
* pcii_rreg32 *
*===========================================================================*/
PRIVATE u32_t pcii_rreg32(busind, devind, port)
int busind;
int devind;
int port;
{
u32_t v;
int s;
2005-04-21 16:53:53 +02:00
v= PCII_RREG32_(pcibus[busind].pb_busnr,
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_dev, pcidev[devind].pd_func,
port);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
#if 0
printf("pcii_rreg32(%d, %d, 0x%X): %d.%d.%d= 0x%X\n",
busind, devind, port,
pcibus[busind].pb_bus, pcidev[devind].pd_dev,
pcidev[devind].pd_func, v);
#endif
return v;
}
/*===========================================================================*
* pcii_wreg8 *
*===========================================================================*/
PRIVATE void pcii_wreg8(busind, devind, port, value)
int busind;
int devind;
int port;
u8_t value;
{
int s;
#if 0
printf("pcii_wreg8(%d, %d, 0x%X, 0x%X): %d.%d.%d\n",
busind, devind, port, value,
pcibus[busind].pb_bus, pcidev[devind].pd_dev,
pcidev[devind].pd_func);
#endif
PCII_WREG8_(pcibus[busind].pb_busnr,
pcidev[devind].pd_dev, pcidev[devind].pd_func,
port, value);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
outl(PCII_CONFADD, PCII_UNSEL);
#endif
}
2005-04-21 16:53:53 +02:00
/*===========================================================================*
* pcii_wreg16 *
*===========================================================================*/
PRIVATE void pcii_wreg16(busind, devind, port, value)
int busind;
int devind;
int port;
u16_t value;
{
int s;
2005-04-21 16:53:53 +02:00
#if 0
printf("pcii_wreg16(%d, %d, 0x%X, 0x%X): %d.%d.%d\n",
busind, devind, port, value,
pcibus[busind].pb_bus, pcidev[devind].pd_dev,
pcidev[devind].pd_func);
#endif
PCII_WREG16_(pcibus[busind].pb_busnr,
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_dev, pcidev[devind].pd_func,
port, value);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* pcii_wreg32 *
*===========================================================================*/
PRIVATE void pcii_wreg32(busind, devind, port, value)
int busind;
int devind;
int port;
u32_t value;
{
int s;
2005-04-21 16:53:53 +02:00
#if 0
printf("pcii_wreg32(%d, %d, 0x%X, 0x%X): %d.%d.%d\n",
busind, devind, port, value,
pcibus[busind].pb_busnr, pcidev[devind].pd_dev,
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_func);
#endif
PCII_WREG32_(pcibus[busind].pb_busnr,
2005-04-21 16:53:53 +02:00
pcidev[devind].pd_dev, pcidev[devind].pd_func,
port, value);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n",s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
}
/*===========================================================================*
* pcii_rsts *
*===========================================================================*/
PRIVATE u16_t pcii_rsts(int busind)
2005-04-21 16:53:53 +02:00
{
u16_t v;
int s;
v= PCII_RREG16_(pcibus[busind].pb_busnr, 0, 0, PCI_SR);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
return v;
}
/*===========================================================================*
* pcii_wsts *
*===========================================================================*/
PRIVATE void pcii_wsts(busind, value)
int busind;
u16_t value;
{
int s;
PCII_WREG16_(pcibus[busind].pb_busnr, 0, 0, PCI_SR, value);
#if USER_SPACE
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
printf("PCI: warning, sys_outl failed: %d\n", s);
#else
2005-04-21 16:53:53 +02:00
outl(PCII_CONFADD, PCII_UNSEL);
#endif
2005-04-21 16:53:53 +02:00
}
2006-03-06 16:19:51 +01:00
/*===========================================================================*
* print_capabilities *
*===========================================================================*/
PRIVATE void print_capabilities(devind)
int devind;
{
u8_t status, capptr, type, next, subtype;
2006-03-06 16:19:51 +01:00
char *str;
/* Check capabilities bit in the device status register */
status= pci_attr_r16(devind, PCI_SR);
if (!(status & PSR_CAPPTR))
return;
capptr= (pci_attr_r8_u(devind, PCI_CAPPTR) & PCI_CP_MASK);
2006-03-06 16:19:51 +01:00
while (capptr != 0)
{
type = pci_attr_r8_u(devind, capptr+CAP_TYPE);
next= (pci_attr_r8_u(devind, capptr+CAP_NEXT) & PCI_CP_MASK);
2006-03-06 16:19:51 +01:00
switch(type)
{
case 1: str= "PCI Power Management"; break;
case 2: str= "AGP"; break;
case 3: str= "Vital Product Data"; break;
case 4: str= "Slot Identification"; break;
case 5: str= "Message Signaled Interrupts"; break;
case 6: str= "CompactPCI Hot Swap"; break;
case 8: str= "AMD HyperTransport"; break;
case 0xf: str= "Secure Device"; break;
default: str= "(unknown type)"; break;
2006-03-06 16:19:51 +01:00
}
printf(" @0x%x (0x%08x): capability type 0x%x: %s",
capptr, pci_attr_r32_u(devind, capptr), type, str);
if (type == 0x08)
print_hyper_cap(devind, capptr);
else if (type == 0x0f)
{
subtype= (pci_attr_r8_u(devind, capptr+2) & 0x07);
switch(subtype)
{
case 0: str= "Device Exclusion Vector"; break;
case 3: str= "IOMMU"; break;
default: str= "(unknown type)"; break;
}
printf(", sub type 0%o: %s", subtype, str);
}
printf("\n");
2006-03-06 16:19:51 +01:00
capptr= next;
}
}
/*===========================================================================*
* visible *
*===========================================================================*/
PRIVATE int visible(aclp, devind)
struct rs_pci *aclp;
int devind;
{
int i;
u32_t class_id;
if (!aclp)
return TRUE; /* Should be changed when ACLs become
* mandatory.
*/
/* Check whether the caller is allowed to get this device. */
for (i= 0; i<aclp->rsp_nr_device; i++)
{
if (aclp->rsp_device[i].vid == pcidev[devind].pd_vid &&
aclp->rsp_device[i].did == pcidev[devind].pd_did)
{
return TRUE;
}
}
if (!aclp->rsp_nr_class)
return FALSE;
class_id= (pcidev[devind].pd_baseclass << 16) |
(pcidev[devind].pd_subclass << 8) |
pcidev[devind].pd_infclass;
for (i= 0; i<aclp->rsp_nr_class; i++)
{
if (aclp->rsp_class[i].class ==
(class_id & aclp->rsp_class[i].mask))
{
return TRUE;
}
}
return FALSE;
}
/*===========================================================================*
* print_hyper_cap *
*===========================================================================*/
PRIVATE void print_hyper_cap(devind, capptr)
int devind;
u8_t capptr;
{
u32_t v;
u16_t cmd;
int type0, type1;
printf("\n");
v= pci_attr_r32_u(devind, capptr);
printf("print_hyper_cap: @0x%x, off 0 (cap):", capptr);
cmd= (v >> 16) & 0xffff;
#if 0
if (v & 0x10000)
{
printf(" WarmReset");
v &= ~0x10000;
}
if (v & 0x20000)
{
printf(" DblEnded");
v &= ~0x20000;
}
printf(" DevNum %d", (v & 0x7C0000) >> 18);
v &= ~0x7C0000;
#endif
type0= (cmd & 0xE000) >> 13;
type1= (cmd & 0xF800) >> 11;
if (type0 == 0 || type0 == 1)
{
printf("Capability Type: %s\n",
type0 == 0 ? "Slave or Primary Interface" :
"Host or Secondary Interface");
cmd &= ~0xE000;
}
else
{
printf(" Capability Type 0x%x", type1);
cmd &= ~0xF800;
}
if (cmd)
printf(" undecoded 0x%x\n", cmd);
#if 0
printf("print_hyper_cap: off 4 (ctl): 0x%x\n",
pci_attr_r32_u(devind, capptr+4));
printf("print_hyper_cap: off 8 (freq/rev): 0x%x\n",
pci_attr_r32_u(devind, capptr+8));
printf("print_hyper_cap: off 12 (cap): 0x%x\n",
pci_attr_r32_u(devind, capptr+12));
printf("print_hyper_cap: off 16 (buf count): 0x%x\n",
pci_attr_r32_u(devind, capptr+16));
v= pci_attr_r32_u(devind, capptr+20);
printf("print_hyper_cap: @0x%x, off 20 (bus nr): ",
capptr+20);
printf("prim %d", v & 0xff);
printf(", sec %d", (v >> 8) & 0xff);
printf(", sub %d", (v >> 16) & 0xff);
if (v >> 24)
printf(", reserved %d", (v >> 24) & 0xff);
printf("\n");
printf("print_hyper_cap: off 24 (type): 0x%x\n",
pci_attr_r32_u(devind, capptr+24));
#endif
}
2005-04-21 16:53:53 +02:00
/*
* $PchId: pci.c,v 1.7 2003/08/07 09:06:51 philip Exp $
*/