2005-04-21 16:53:53 +02:00
|
|
|
/*
|
|
|
|
pci.c
|
|
|
|
|
|
|
|
Configure devices on the PCI bus
|
|
|
|
|
|
|
|
Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl>
|
|
|
|
*/
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
#include <minix/driver.h>
|
|
|
|
|
|
|
|
#include <minix/acpi.h>
|
2012-07-16 13:17:11 +02:00
|
|
|
#include <minix/param.h>
|
2014-10-14 21:38:39 +02:00
|
|
|
#include <minix/ds.h>
|
|
|
|
#include <minix/rs.h>
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
#include <machine/pci.h>
|
2010-09-02 17:43:59 +02:00
|
|
|
#include <machine/pci_amd.h>
|
|
|
|
#include <machine/pci_intel.h>
|
|
|
|
#include <machine/pci_sis.h>
|
|
|
|
#include <machine/pci_via.h>
|
2014-10-14 21:38:39 +02:00
|
|
|
#include <machine/vmparam.h>
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-05-11 11:02:00 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
#include "pci.h"
|
|
|
|
|
|
|
|
#define irq_mode_pci(irq) ((void)0)
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
#define PBT_INTEL_HOST 1
|
2005-04-21 16:53:53 +02:00
|
|
|
#define PBT_PCIBRIDGE 2
|
2006-01-12 15:46:12 +01:00
|
|
|
#define PBT_CARDBUS 3
|
|
|
|
|
|
|
|
#define BAM_NR 6 /* Number of base-address registers */
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
struct pci_acl pci_acl[NR_DRIVERS];
|
2005-08-09 13:23:41 +02:00
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static struct pcibus
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
int pb_type;
|
2006-01-12 15:46:12 +01:00
|
|
|
int pb_needinit;
|
2005-04-21 16:53:53 +02:00
|
|
|
int pb_isabridge_dev;
|
|
|
|
int pb_isabridge_type;
|
|
|
|
|
|
|
|
int pb_devind;
|
2006-01-12 15:46:12 +01:00
|
|
|
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);
|
2010-04-07 15:35:56 +02:00
|
|
|
void (*pb_wreg8)(int busind, int devind, int port, u8_t value);
|
|
|
|
void (*pb_wreg16)(int busind, int devind, int port, u16_t value);
|
2005-04-21 16:53:53 +02:00
|
|
|
void (*pb_wreg32)(int busind, int devind, int port, u32_t value);
|
|
|
|
u16_t (*pb_rsts)(int busind);
|
2010-04-07 15:35:56 +02:00
|
|
|
void (*pb_wsts)(int busind, u16_t value);
|
2005-04-21 16:53:53 +02:00
|
|
|
} pcibus[NR_PCIBUS];
|
2012-03-25 20:25:53 +02:00
|
|
|
static int nr_pcibus= 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static struct pcidev
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
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;
|
2013-10-01 00:42:41 +02:00
|
|
|
u16_t pd_sub_vid;
|
|
|
|
u16_t pd_sub_did;
|
2006-01-12 15:46:12 +01:00
|
|
|
u8_t pd_ilr;
|
2007-04-24 14:55:37 +02:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
u8_t pd_inuse;
|
2007-04-24 14:55:37 +02:00
|
|
|
endpoint_t pd_proc;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
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];
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
/* pb_flags */
|
|
|
|
#define PBF_IO 1 /* I/O else memory */
|
|
|
|
#define PBF_INCOMPLETE 2 /* not allocated */
|
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static int nr_pcidev= 0;
|
|
|
|
|
|
|
|
static struct machine machine;
|
|
|
|
static endpoint_t acpi_ep;
|
2010-09-02 17:44:38 +02:00
|
|
|
|
2005-05-11 11:02:00 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* helper functions for I/O *
|
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static unsigned
|
|
|
|
pci_inb(u16_t port) {
|
2005-10-24 16:02:45 +02:00
|
|
|
u32_t value;
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_inb(port, &value)) !=OK)
|
|
|
|
printf("PCI: warning, sys_inb failed: %d\n", s);
|
|
|
|
return value;
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
static unsigned
|
|
|
|
pci_inw(u16_t port) {
|
2005-12-02 15:45:10 +01:00
|
|
|
u32_t value;
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_inw(port, &value)) !=OK)
|
|
|
|
printf("PCI: warning, sys_inw failed: %d\n", s);
|
|
|
|
return value;
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
static unsigned
|
|
|
|
pci_inl(u16_t port) {
|
2010-04-07 15:35:56 +02:00
|
|
|
u32_t value;
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_inl(port, &value)) !=OK)
|
|
|
|
printf("PCI: warning, sys_inl failed: %d\n", s);
|
|
|
|
return value;
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
static void
|
|
|
|
pci_outb(u16_t port, u8_t value) {
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_outb(port, value)) !=OK)
|
|
|
|
printf("PCI: warning, sys_outb failed: %d\n", s);
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
static void
|
|
|
|
pci_outw(u16_t port, u16_t value) {
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_outw(port, value)) !=OK)
|
|
|
|
printf("PCI: warning, sys_outw failed: %d\n", s);
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
static void
|
|
|
|
pci_outl(u16_t port, u32_t value) {
|
2005-05-11 11:02:00 +02:00
|
|
|
int s;
|
|
|
|
if ((s=sys_outl(port, value)) !=OK)
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static u8_t
|
|
|
|
pcii_rreg8(int busind, int devind, int port)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
u8_t v;
|
|
|
|
int s;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
v= PCII_RREG8_(pcibus[busind].pb_busnr,
|
|
|
|
pcidev[devind].pd_dev, pcidev[devind].pd_func,
|
|
|
|
port);
|
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
2007-02-20 18:09:19 +01:00
|
|
|
#if 0
|
2014-10-14 21:38:39 +02:00
|
|
|
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);
|
2007-02-20 18:09:19 +01:00
|
|
|
#endif
|
2014-10-14 21:38:39 +02:00
|
|
|
return v;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static u16_t
|
|
|
|
pcii_rreg16(int busind, int devind, int port)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
u16_t v;
|
|
|
|
int s;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
v= PCII_RREG16_(pcibus[busind].pb_busnr,
|
|
|
|
pcidev[devind].pd_dev, pcidev[devind].pd_func,
|
|
|
|
port);
|
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
2007-02-20 18:09:19 +01:00
|
|
|
#if 0
|
2014-10-14 21:38:39 +02:00
|
|
|
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);
|
2007-02-20 18:09:19 +01:00
|
|
|
#endif
|
2014-10-14 21:38:39 +02:00
|
|
|
return v;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static u32_t
|
|
|
|
pcii_rreg32(int busind, int devind, int port)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
u32_t v;
|
|
|
|
int s;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
v= PCII_RREG32_(pcibus[busind].pb_busnr,
|
|
|
|
pcidev[devind].pd_dev, pcidev[devind].pd_func,
|
|
|
|
port);
|
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
2007-02-20 18:09:19 +01:00
|
|
|
#if 0
|
2014-10-14 21:38:39 +02:00
|
|
|
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);
|
2007-02-20 18:09:19 +01:00
|
|
|
#endif
|
2014-10-14 21:38:39 +02:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pcii_wreg8(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 (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pcii_wreg16(int busind, int devind, int port, u16_t value)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
#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,
|
|
|
|
pcidev[devind].pd_dev, pcidev[devind].pd_func,
|
|
|
|
port, value);
|
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pcii_wreg32(int busind, int devind, int port, u32_t value)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
#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,
|
|
|
|
pcidev[devind].pd_func);
|
|
|
|
#endif
|
|
|
|
PCII_WREG32_(pcibus[busind].pb_busnr,
|
|
|
|
pcidev[devind].pd_dev, pcidev[devind].pd_func,
|
|
|
|
port, value);
|
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n",s);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* ntostr *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
ntostr(unsigned int n, char **str, const char *end)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
char tmpstr[20];
|
|
|
|
int i;
|
2006-01-27 14:18:07 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if (n == 0)
|
2008-02-22 16:59:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
tmpstr[0]= '0';
|
|
|
|
i= 1;
|
2008-02-22 16:59:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
else
|
2012-03-07 23:45:55 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; n; i++)
|
|
|
|
{
|
|
|
|
tmpstr[i]= '0' + (n%10);
|
|
|
|
n /= 10;
|
|
|
|
}
|
2012-03-07 23:45:55 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
for (; i>0; i--)
|
2006-01-27 14:18:07 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (*str == end)
|
2006-01-27 14:18:07 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
break;
|
2006-01-27 14:18:07 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
**str= tmpstr[i-1];
|
|
|
|
(*str)++;
|
2006-01-27 14:18:07 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
if (*str == end)
|
|
|
|
(*str)[-1]= '\0';
|
|
|
|
else
|
|
|
|
**str= '\0';
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* get_busind *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
get_busind(int busnr)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; i<nr_pcibus; i++)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcibus[i].pb_busnr == busnr)
|
|
|
|
return i;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
panic("get_busind: can't find bus: %d", busnr);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* Unprotected helper functions *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static u8_t
|
|
|
|
__pci_attr_r8(int devind, int port)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int busnr, busind;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
busnr= pcidev[devind].pd_busnr;
|
2006-01-12 15:46:12 +01:00
|
|
|
busind= get_busind(busnr);
|
2014-10-14 21:38:39 +02:00
|
|
|
return pcibus[busind].pb_rreg8(busind, devind, port);
|
2008-02-22 16:59:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static u16_t
|
|
|
|
__pci_attr_r16(int devind, int port)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
int busnr, busind;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
busind= get_busind(busnr);
|
2014-10-14 21:38:39 +02:00
|
|
|
return pcibus[busind].pb_rreg16(busind, devind, port);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static u32_t
|
|
|
|
__pci_attr_r32(int devind, int port)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
int busnr, busind;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
busind= get_busind(busnr);
|
2014-10-14 21:38:39 +02:00
|
|
|
return pcibus[busind].pb_rreg32(busind, devind, port);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
__pci_attr_w8(int devind, int port, u8_t value)
|
2008-02-22 16:59:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int busnr, busind;
|
2008-02-22 16:59:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
busind= get_busind(busnr);
|
|
|
|
pcibus[busind].pb_wreg8(busind, devind, port, value);
|
2008-02-22 16:59:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
__pci_attr_w16(int devind, int port, u16_t value)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
int busnr, busind;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
busind= get_busind(busnr);
|
2014-10-14 21:38:39 +02:00
|
|
|
pcibus[busind].pb_wreg16(busind, devind, port, value);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
__pci_attr_w32(int devind, int port, u32_t value)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
|
|
|
int busnr, busind;
|
|
|
|
|
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
busind= get_busind(busnr);
|
2014-10-14 21:38:39 +02:00
|
|
|
pcibus[busind].pb_wreg32(busind, devind, port, value);
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* helpers *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static u16_t
|
|
|
|
pci_attr_rsts(int devind)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
int busnr, busind;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
busind= get_busind(busnr);
|
2014-10-14 21:38:39 +02:00
|
|
|
return pcibus[busind].pb_rsts(busind);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
pci_attr_wsts(int devind, u16_t value)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
int busnr, busind;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
busind= get_busind(busnr);
|
2014-10-14 21:38:39 +02:00
|
|
|
pcibus[busind].pb_wsts(busind, value);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static u16_t
|
|
|
|
pcii_rsts(int busind)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
u16_t v;
|
|
|
|
int s;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
v= PCII_RREG16_(pcibus[busind].pb_busnr, 0, 0, PCI_SR);
|
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
|
|
|
return v;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
pcii_wsts(int busind, u16_t value)
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
PCII_WREG16_(pcibus[busind].pb_busnr, 0, 0, PCI_SR, value);
|
2005-05-11 11:02:00 +02:00
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
2014-10-14 21:38:39 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
is_duplicate(u8_t busnr, u8_t dev, u8_t func)
|
|
|
|
{
|
|
|
|
int i;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; i<nr_pcidev; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcidev[i].pd_busnr == busnr &&
|
|
|
|
pcidev[i].pd_dev == dev &&
|
|
|
|
pcidev[i].pd_func == func)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
return 1;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
get_freebus(void)
|
|
|
|
{
|
|
|
|
int i, freebus;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
freebus= 1;
|
|
|
|
for (i= 0; i<nr_pcibus; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
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");
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return freebus;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static const char *
|
|
|
|
pci_vid_name(u16_t vid)
|
|
|
|
{
|
|
|
|
int i;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; pci_vendor_table[i].name; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pci_vendor_table[i].vid == vid)
|
|
|
|
return pci_vendor_table[i].name;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return "unknown";
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static const char *
|
|
|
|
pci_baseclass_name(u8_t baseclass)
|
|
|
|
{
|
|
|
|
int i;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; pci_baseclass_table[i].name; i++)
|
|
|
|
{
|
|
|
|
if (pci_baseclass_table[i].baseclass == baseclass)
|
|
|
|
return pci_baseclass_table[i].name;
|
|
|
|
}
|
|
|
|
return NULL;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static const char *
|
|
|
|
pci_subclass_name(u8_t baseclass, u8_t subclass, u8_t infclass)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int i;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; pci_subclass_table[i].name; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
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)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return pci_subclass_table[i].name;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
print_hyper_cap(int devind, u8_t capptr)
|
|
|
|
{
|
|
|
|
u32_t v;
|
|
|
|
u16_t cmd;
|
|
|
|
int type0, type1;
|
2005-08-09 13:23:41 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
printf("\n");
|
|
|
|
v= __pci_attr_r32(devind, capptr);
|
|
|
|
printf("print_hyper_cap: @0x%x, off 0 (cap):", capptr);
|
|
|
|
cmd= (v >> 16) & 0xffff;
|
2006-01-12 15:46:12 +01:00
|
|
|
#if 0
|
2014-10-14 21:38:39 +02:00
|
|
|
if (v & 0x10000)
|
|
|
|
{
|
|
|
|
printf(" WarmReset");
|
|
|
|
v &= ~0x10000;
|
|
|
|
}
|
|
|
|
if (v & 0x20000)
|
|
|
|
{
|
|
|
|
printf(" DblEnded");
|
|
|
|
v &= ~0x20000;
|
|
|
|
}
|
|
|
|
printf(" DevNum %d", (v & 0x7C0000) >> 18);
|
|
|
|
v &= ~0x7C0000;
|
2006-01-12 15:46:12 +01:00
|
|
|
#endif
|
2014-10-14 21:38:39 +02:00
|
|
|
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);
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
#if 0
|
|
|
|
printf("print_hyper_cap: off 4 (ctl): 0x%x\n",
|
|
|
|
__pci_attr_r32(devind, capptr+4));
|
|
|
|
printf("print_hyper_cap: off 8 (freq/rev): 0x%x\n",
|
|
|
|
__pci_attr_r32(devind, capptr+8));
|
|
|
|
printf("print_hyper_cap: off 12 (cap): 0x%x\n",
|
|
|
|
__pci_attr_r32(devind, capptr+12));
|
|
|
|
printf("print_hyper_cap: off 16 (buf count): 0x%x\n",
|
|
|
|
__pci_attr_r32(devind, capptr+16));
|
|
|
|
v= __pci_attr_r32(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(devind, capptr+24));
|
|
|
|
#endif
|
|
|
|
}
|
2013-10-01 00:42:41 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
print_capabilities(int devind)
|
|
|
|
{
|
|
|
|
u8_t status, capptr, type, next, subtype;
|
|
|
|
const char *str;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* Check capabilities bit in the device status register */
|
|
|
|
status= __pci_attr_r16(devind, PCI_SR);
|
|
|
|
if (!(status & PSR_CAPPTR))
|
|
|
|
return;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
capptr= (__pci_attr_r8(devind, PCI_CAPPTR) & PCI_CP_MASK);
|
|
|
|
while (capptr != 0)
|
|
|
|
{
|
|
|
|
type = __pci_attr_r8(devind, capptr+CAP_TYPE);
|
|
|
|
next= (__pci_attr_r8(devind, capptr+CAP_NEXT) & PCI_CP_MASK);
|
|
|
|
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-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
printf(" @0x%x (0x%08x): capability type 0x%x: %s",
|
|
|
|
capptr, __pci_attr_r32(devind, capptr), type, str);
|
|
|
|
if (type == 0x08)
|
|
|
|
print_hyper_cap(devind, capptr);
|
|
|
|
else if (type == 0x0f)
|
|
|
|
{
|
|
|
|
subtype= (__pci_attr_r8(devind, capptr+2) & 0x07);
|
|
|
|
switch(subtype)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
case 0: str= "Device Exclusion Vector"; break;
|
|
|
|
case 3: str= "IOMMU"; break;
|
|
|
|
default: str= "(unknown type)"; break;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
printf(", sub type 0%o: %s", subtype, str);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
printf("\n");
|
|
|
|
capptr= next;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* ISA Bridge Helpers *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
update_bridge4dev_io(int devind, u32_t io_base, u32_t io_size)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int busnr, busind, type, br_devind;
|
|
|
|
u16_t v16;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
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)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
printf(
|
|
|
|
"update_bridge4dev_io: not implemented for PCI bridges\n");
|
|
|
|
return;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
if (type != PBT_CARDBUS)
|
|
|
|
panic("update_bridge4dev_io: strange bus type: %d", type);
|
2010-09-02 17:44:38 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
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);
|
2010-09-02 17:44:38 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* 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);
|
2010-09-02 17:44:38 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
do_piix(int devind)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int i, s, irqrc, irq;
|
|
|
|
u32_t elcr1, elcr2, elcr;
|
2010-09-02 17:44:38 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
#if DEBUG
|
|
|
|
printf("in piix\n");
|
|
|
|
#endif
|
|
|
|
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);
|
|
|
|
elcr= elcr1 | (elcr2 << 8);
|
|
|
|
for (i= 0; i<4; i++)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
irqrc= __pci_attr_r8(devind, PIIX_PIRQRCA+i);
|
|
|
|
if (irqrc & PIIX_IRQ_DI)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (debug)
|
|
|
|
printf("INT%c: disabled\n", 'A'+i);
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
else
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
irq= irqrc & PIIX_IRQ_MASK;
|
|
|
|
if (debug)
|
|
|
|
printf("INT%c: %d\n", 'A'+i, irq);
|
|
|
|
if (!(elcr & (1 << irq)))
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf(
|
2014-10-14 21:38:39 +02:00
|
|
|
"(warning) IRQ %d is not level triggered\n",
|
|
|
|
irq);
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
irq_mode_pci(irq);
|
2006-03-03 11:21:45 +01:00
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return 0;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
do_amd_isabr(int devind)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int i, busnr, dev, func, xdevind, irq, edge;
|
|
|
|
u8_t levmask;
|
|
|
|
u16_t pciirq;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* Find required function */
|
|
|
|
func= AMD_ISABR_FUNC;
|
|
|
|
busnr= pcidev[devind].pd_busnr;
|
|
|
|
dev= pcidev[devind].pd_dev;
|
2006-04-12 13:18:13 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* Fake a device with the required function */
|
|
|
|
if (nr_pcidev >= NR_PCIDEV)
|
|
|
|
panic("too many PCI devices: %d", nr_pcidev);
|
|
|
|
xdevind= nr_pcidev;
|
|
|
|
pcidev[xdevind].pd_busnr= busnr;
|
|
|
|
pcidev[xdevind].pd_dev= dev;
|
|
|
|
pcidev[xdevind].pd_func= func;
|
|
|
|
pcidev[xdevind].pd_inuse= 1;
|
|
|
|
nr_pcidev++;
|
|
|
|
|
|
|
|
levmask= __pci_attr_r8(xdevind, AMD_ISABR_PCIIRQ_LEV);
|
|
|
|
pciirq= __pci_attr_r16(xdevind, AMD_ISABR_PCIIRQ_ROUTE);
|
|
|
|
for (i= 0; i<4; i++)
|
2006-04-12 13:18:13 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
edge= (levmask >> i) & 1;
|
|
|
|
irq= (pciirq >> (4*i)) & 0xf;
|
|
|
|
if (!irq)
|
2006-04-12 13:18:13 +02:00
|
|
|
{
|
|
|
|
if (debug)
|
2014-10-14 21:38:39 +02:00
|
|
|
printf("INT%c: disabled\n", 'A'+i);
|
2006-04-12 13:18:13 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
else
|
2006-04-12 13:18:13 +02:00
|
|
|
{
|
|
|
|
if (debug)
|
2014-10-14 21:38:39 +02:00
|
|
|
printf("INT%c: %d\n", 'A'+i, irq);
|
|
|
|
if (edge && debug)
|
2006-04-12 13:18:13 +02:00
|
|
|
{
|
|
|
|
printf(
|
2014-10-14 21:38:39 +02:00
|
|
|
"(warning) IRQ %d is not level triggered\n",
|
|
|
|
irq);
|
2006-04-12 13:18:13 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
irq_mode_pci(irq);
|
2006-04-12 13:18:13 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
}
|
|
|
|
nr_pcidev--;
|
|
|
|
return 0;
|
|
|
|
}
|
2006-04-12 13:18:13 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
do_sis_isabr(int devind)
|
|
|
|
{
|
|
|
|
int i, irq;
|
|
|
|
|
|
|
|
irq= 0; /* lint */
|
|
|
|
for (i= 0; i<4; i++)
|
|
|
|
{
|
|
|
|
irq= __pci_attr_r8(devind, SIS_ISABR_IRQ_A+i);
|
|
|
|
if (irq & SIS_IRQ_DISABLED)
|
2006-04-12 13:18:13 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
do_via_isabr(int devind)
|
|
|
|
{
|
|
|
|
int i, irq, edge;
|
|
|
|
u8_t levmask;
|
|
|
|
|
|
|
|
levmask= __pci_attr_r8(devind, VIA_ISABR_EL);
|
|
|
|
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(devind, VIA_ISABR_IRQ_R2) >> 4;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
edge= (levmask & VIA_ISABR_EL_INTB);
|
|
|
|
irq= __pci_attr_r8(devind, VIA_ISABR_IRQ_R2);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
edge= (levmask & VIA_ISABR_EL_INTC);
|
|
|
|
irq= __pci_attr_r8(devind, VIA_ISABR_IRQ_R3) >> 4;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
edge= (levmask & VIA_ISABR_EL_INTD);
|
|
|
|
irq= __pci_attr_r8(devind, VIA_ISABR_IRQ_R1) >> 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("PCI: VIA ISA Bridge IRQ Detection Failed");
|
|
|
|
}
|
|
|
|
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)
|
2006-11-01 15:55:00 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
printf(
|
|
|
|
"(warning) IRQ %d is not level triggered\n",
|
|
|
|
irq);
|
2006-11-01 15:55:00 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
irq_mode_pci(irq);
|
2006-04-12 13:18:13 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return 0;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
do_isabridge(int busind)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int i, j, r, type, busnr, unknown_bridge, bridge_dev;
|
|
|
|
u16_t vid, did;
|
|
|
|
u32_t t3;
|
|
|
|
const char *dstr;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
unknown_bridge= -1;
|
|
|
|
bridge_dev= -1;
|
|
|
|
j= 0; /* lint */
|
|
|
|
vid= did= 0; /* lint */
|
|
|
|
busnr= pcibus[busind].pb_busnr;
|
|
|
|
for (i= 0; i< nr_pcidev; i++)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcidev[i].pd_busnr != busnr)
|
|
|
|
continue;
|
|
|
|
t3= ((pcidev[i].pd_baseclass << 16) |
|
|
|
|
(pcidev[i].pd_subclass << 8) | pcidev[i].pd_infclass);
|
|
|
|
if (t3 == PCI_T3_ISA)
|
|
|
|
{
|
|
|
|
/* 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;
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if (bridge_dev != -1)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
return r;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if (unknown_bridge == -1)
|
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf("(warning) no ISA bridge found on bus %d\n",
|
|
|
|
busind);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf(
|
2014-10-14 21:38:39 +02:00
|
|
|
"(warning) unsupported ISA bridge %04X:%04X for bus %d\n",
|
|
|
|
pcidev[unknown_bridge].pd_vid,
|
|
|
|
pcidev[unknown_bridge].pd_did, busind);
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return 0;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* IRQ handling *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
acpi_get_irq(unsigned bus, unsigned dev, unsigned pin)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int err;
|
|
|
|
message m;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
((struct acpi_get_irq_req *)&m)->hdr.request = ACPI_REQ_GET_IRQ;
|
|
|
|
((struct acpi_get_irq_req *)&m)->bus = bus;
|
|
|
|
((struct acpi_get_irq_req *)&m)->dev = dev;
|
|
|
|
((struct acpi_get_irq_req *)&m)->pin = pin;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if ((err = ipc_sendrec(acpi_ep, &m)) != OK)
|
|
|
|
panic("PCI: error %d while receiveing from ACPI\n", err);
|
|
|
|
|
|
|
|
return ((struct acpi_get_irq_resp *)&m)->irq;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
derive_irq(struct pcidev * dev, int pin)
|
|
|
|
{
|
|
|
|
struct pcidev * parent_bridge;
|
|
|
|
int slot;
|
|
|
|
|
|
|
|
parent_bridge = &pcidev[pcibus[get_busind(dev->pd_busnr)].pb_devind];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't support PCI-Express, no ARI, decode the slot of the device
|
|
|
|
* and mangle the pin as the device is behind a bridge
|
|
|
|
*/
|
|
|
|
slot = ((dev->pd_func) >> 3) & 0x1f;
|
|
|
|
|
|
|
|
return acpi_get_irq(parent_bridge->pd_busnr,
|
|
|
|
parent_bridge->pd_dev, (pin + slot) % 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
record_irq(int devind)
|
|
|
|
{
|
|
|
|
int ilr, ipr, busnr, busind, cb_devind;
|
|
|
|
|
|
|
|
ilr= __pci_attr_r8(devind, PCI_ILR);
|
|
|
|
ipr= __pci_attr_r8(devind, PCI_IPR);
|
|
|
|
|
|
|
|
if (ipr && machine.apic_enabled) {
|
|
|
|
int irq;
|
|
|
|
|
|
|
|
irq = acpi_get_irq(pcidev[devind].pd_busnr,
|
|
|
|
pcidev[devind].pd_dev, ipr - 1);
|
|
|
|
|
|
|
|
if (irq < 0)
|
|
|
|
irq = derive_irq(&pcidev[devind], ipr - 1);
|
|
|
|
|
|
|
|
if (irq >= 0) {
|
|
|
|
ilr = irq;
|
|
|
|
__pci_attr_w8(devind, PCI_ILR, ilr);
|
|
|
|
if (debug)
|
|
|
|
printf("PCI: ACPI IRQ %d for "
|
|
|
|
"device %d.%d.%d INT%c\n",
|
|
|
|
irq,
|
|
|
|
pcidev[devind].pd_busnr,
|
|
|
|
pcidev[devind].pd_dev,
|
|
|
|
pcidev[devind].pd_func,
|
|
|
|
'A' + ipr-1);
|
|
|
|
}
|
|
|
|
else if (debug) {
|
|
|
|
printf("PCI: no ACPI IRQ routing for "
|
|
|
|
"device %d.%d.%d INT%c\n",
|
|
|
|
pcidev[devind].pd_busnr,
|
|
|
|
pcidev[devind].pd_dev,
|
|
|
|
pcidev[devind].pd_func,
|
|
|
|
'A' + ipr-1);
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if (ilr == 0)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
static int first= 1;
|
|
|
|
if (ipr && first && debug)
|
|
|
|
{
|
|
|
|
first= 0;
|
|
|
|
printf("PCI: strange, BIOS assigned IRQ0\n");
|
|
|
|
}
|
|
|
|
ilr= PCI_ILR_UNKNOWN;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
pcidev[devind].pd_ilr= ilr;
|
|
|
|
if (ilr == PCI_ILR_UNKNOWN && !ipr)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
else if (ilr != PCI_ILR_UNKNOWN && ipr)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (debug)
|
|
|
|
printf("\tIRQ %d for INT%c\n", ilr, 'A' + ipr-1);
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
else if (ilr != PCI_ILR_UNKNOWN)
|
2010-01-13 11:52:47 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* BAR helpers *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
record_bar(int devind, int bar_nr, int last)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2010-01-13 11:52:47 +01:00
|
|
|
int reg, prefetch, type, dev_bar_nr, width;
|
2006-01-12 15:46:12 +01:00
|
|
|
u32_t bar, bar2;
|
2009-05-28 01:35:34 +02:00
|
|
|
u16_t cmd;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2010-01-13 11:52:47 +01:00
|
|
|
/* Start by assuming that this is a 32-bit bar, taking up one DWORD. */
|
|
|
|
width = 1;
|
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
reg= PCI_BAR+4*bar_nr;
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
bar= __pci_attr_r32(devind, reg);
|
2006-01-12 15:46:12 +01:00
|
|
|
if (bar & PCI_BAR_IO)
|
|
|
|
{
|
2009-05-28 01:35:34 +02:00
|
|
|
/* Disable I/O access before probing for BAR's size */
|
2014-10-14 21:38:39 +02:00
|
|
|
cmd = __pci_attr_r16(devind, PCI_CR);
|
|
|
|
__pci_attr_w16(devind, PCI_CR, cmd & ~PCI_CR_IO_EN);
|
2009-05-28 01:35:34 +02:00
|
|
|
|
|
|
|
/* Probe BAR's size */
|
2014-10-14 21:38:39 +02:00
|
|
|
__pci_attr_w32(devind, reg, 0xffffffff);
|
|
|
|
bar2= __pci_attr_r32(devind, reg);
|
2009-05-28 01:35:34 +02:00
|
|
|
|
|
|
|
/* Restore original state */
|
2014-10-14 21:38:39 +02:00
|
|
|
__pci_attr_w32(devind, reg, bar);
|
|
|
|
__pci_attr_w16(devind, PCI_CR, cmd);
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2012-03-08 00:01:25 +01:00
|
|
|
bar &= PCI_BAR_IO_MASK; /* Clear non-address bits */
|
|
|
|
bar2 &= PCI_BAR_IO_MASK;
|
2006-01-12 15:46:12 +01:00
|
|
|
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)
|
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
pcidev[devind].pd_bar[dev_bar_nr].pb_flags |=
|
2006-01-12 15:46:12 +01:00
|
|
|
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++;
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
bar2= __pci_attr_r32(devind, reg+4);
|
2010-01-13 11:52:47 +01:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2009-05-28 01:35:34 +02:00
|
|
|
/* Disable mem access before probing for BAR's size */
|
2014-10-14 21:38:39 +02:00
|
|
|
cmd = __pci_attr_r16(devind, PCI_CR);
|
|
|
|
__pci_attr_w16(devind, PCI_CR, cmd & ~PCI_CR_MEM_EN);
|
2009-05-28 01:35:34 +02:00
|
|
|
|
|
|
|
/* Probe BAR's size */
|
2014-10-14 21:38:39 +02:00
|
|
|
__pci_attr_w32(devind, reg, 0xffffffff);
|
|
|
|
bar2= __pci_attr_r32(devind, reg);
|
2009-05-28 01:35:34 +02:00
|
|
|
|
|
|
|
/* Restore original values */
|
2014-10-14 21:38:39 +02:00
|
|
|
__pci_attr_w32(devind, reg, bar);
|
|
|
|
__pci_attr_w16(devind, PCI_CR, cmd);
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
if (bar2 == 0)
|
2010-01-13 11:52:47 +01:00
|
|
|
return width; /* Reg. is not implemented */
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
prefetch= !!(bar & PCI_BAR_PREFETCH);
|
2012-03-08 00:01:25 +01:00
|
|
|
bar &= PCI_BAR_MEM_MASK; /* Clear non-address bits */
|
|
|
|
bar2 &= PCI_BAR_MEM_MASK;
|
2006-01-12 15:46:12 +01:00
|
|
|
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",
|
2006-01-12 15:46:12 +01:00
|
|
|
bar_nr, bar2, bar,
|
2010-01-13 11:52:47 +01:00
|
|
|
prefetch ? " prefetchable" : "",
|
|
|
|
type == PCI_TYPE_64 ? ", 64-bit" : "");
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
pcidev[devind].pd_bar[dev_bar_nr].pb_flags |=
|
2006-01-12 15:46:12 +01:00
|
|
|
PBF_INCOMPLETE;
|
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
return width;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
record_bars(int devind, int last_reg)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
record_bars_normal(int devind)
|
|
|
|
{
|
|
|
|
int i, j, clear_01, clear_23, pb_nr;
|
|
|
|
|
|
|
|
/* 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(
|
|
|
|
"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)
|
|
|
|
{
|
|
|
|
j++;
|
|
|
|
continue; /* No need to copy */
|
|
|
|
}
|
|
|
|
pcidev[devind].pd_bar[j]=
|
|
|
|
pcidev[devind].pd_bar[i];
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
pcidev[devind].pd_bar_nr= j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
record_bars_bridge(int devind)
|
|
|
|
{
|
|
|
|
u32_t base, limit, size;
|
|
|
|
|
|
|
|
/* 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(devind, PPB_IOBASE) & PPB_IOB_MASK) << 8) |
|
|
|
|
(__pci_attr_r16(devind, PPB_IOBASEU16) << 16);
|
|
|
|
limit= 0xff |
|
|
|
|
((__pci_attr_r8(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);
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
record_bars_cardbus(int devind)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
u32_t base, limit, size;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* The generic BAR area of CardBus devices is one DWORD in size. */
|
|
|
|
record_bars(devind, PCI_BAR);
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
base= __pci_attr_r32(devind, CBB_MEMBASE_0);
|
|
|
|
limit= __pci_attr_r32(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);
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
base= __pci_attr_r32(devind, CBB_MEMBASE_1);
|
|
|
|
limit= __pci_attr_r32(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);
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
base= __pci_attr_r32(devind, CBB_IOBASE_0);
|
|
|
|
limit= __pci_attr_r32(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);
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
base= __pci_attr_r32(devind, CBB_IOBASE_1);
|
|
|
|
limit= __pci_attr_r32(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);
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
complete_bars(void)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
No more intel/minix segments.
This commit removes all traces of Minix segments (the text/data/stack
memory map abstraction in the kernel) and significance of Intel segments
(hardware segments like CS, DS that add offsets to all addressing before
page table translation). This ultimately simplifies the memory layout
and addressing and makes the same layout possible on non-Intel
architectures.
There are only two types of addresses in the world now: virtual
and physical; even the kernel and processes have the same virtual
address space. Kernel and user processes can be distinguished at a
glance as processes won't use 0xF0000000 and above.
No static pre-allocated memory sizes exist any more.
Changes to booting:
. The pre_init.c leaves the kernel and modules exactly as
they were left by the bootloader in physical memory
. The kernel starts running using physical addressing,
loaded at a fixed location given in its linker script by the
bootloader. All code and data in this phase are linked to
this fixed low location.
. It makes a bootstrap pagetable to map itself to a
fixed high location (also in linker script) and jumps to
the high address. All code and data then use this high addressing.
. All code/data symbols linked at the low addresses is prefixed by
an objcopy step with __k_unpaged_*, so that that code cannot
reference highly-linked symbols (which aren't valid yet) or vice
versa (symbols that aren't valid any more).
. The two addressing modes are separated in the linker script by
collecting the unpaged_*.o objects and linking them with low
addresses, and linking the rest high. Some objects are linked
twice, once low and once high.
. The bootstrap phase passes a lot of information (e.g. free memory
list, physical location of the modules, etc.) using the kinfo
struct.
. After this bootstrap the low-linked part is freed.
. The kernel maps in VM into the bootstrap page table so that VM can
begin executing. Its first job is to make page tables for all other
boot processes. So VM runs before RS, and RS gets a fully dynamic,
VM-managed address space. VM gets its privilege info from RS as usual
but that happens after RS starts running.
. Both the kernel loading VM and VM organizing boot processes happen
using the libexec logic. This removes the last reason for VM to
still know much about exec() and vm/exec.c is gone.
Further Implementation:
. All segments are based at 0 and have a 4 GB limit.
. The kernel is mapped in at the top of the virtual address
space so as not to constrain the user processes.
. Processes do not use segments from the LDT at all; there are
no segments in the LDT any more, so no LLDT is needed.
. The Minix segments T/D/S are gone and so none of the
user-space or in-kernel copy functions use them. The copy
functions use a process endpoint of NONE to realize it's
a physical address, virtual otherwise.
. The umap call only makes sense to translate a virtual address
to a physical address now.
. Segments-related calls like newmap and alloc_segments are gone.
. All segments-related translation in VM is gone (vir2map etc).
. Initialization in VM is simpler as no moving around is necessary.
. VM and all other boot processes can be linked wherever they wish
and will be mapped in at the right location by the kernel and VM
respectively.
Other changes:
. The multiboot code is less special: it does not use mb_print
for its diagnostics any more but uses printf() as normal, saving
the output into the diagnostics buffer, only printing to the
screen using the direct print functions if a panic() occurs.
. The multiboot code uses the flexible 'free memory map list'
style to receive the list of free memory if available.
. The kernel determines the memory layout of the processes to
a degree: it tells VM where the kernel starts and ends and
where the kernel wants the top of the process to be. VM then
uses this entire range, i.e. the stack is right at the top,
and mmap()ped bits of memory are placed below that downwards,
and the break grows upwards.
Other Consequences:
. Every process gets its own page table as address spaces
can't be separated any more by segments.
. As all segments are 0-based, there is no distinction between
virtual and linear addresses, nor between userspace and
kernel addresses.
. Less work is done when context switching, leading to a net
performance increase. (8% faster on my machine for 'make servers'.)
. The layout and configuration of the GDT makes sysenter and syscall
possible.
2012-05-07 16:03:35 +02:00
|
|
|
int i, j, bar_nr, reg;
|
2006-04-03 14:12:04 +02:00
|
|
|
u32_t memgap_low, memgap_high, iogap_low, iogap_high, io_high,
|
|
|
|
base, size, v32, diff1, diff2;
|
No more intel/minix segments.
This commit removes all traces of Minix segments (the text/data/stack
memory map abstraction in the kernel) and significance of Intel segments
(hardware segments like CS, DS that add offsets to all addressing before
page table translation). This ultimately simplifies the memory layout
and addressing and makes the same layout possible on non-Intel
architectures.
There are only two types of addresses in the world now: virtual
and physical; even the kernel and processes have the same virtual
address space. Kernel and user processes can be distinguished at a
glance as processes won't use 0xF0000000 and above.
No static pre-allocated memory sizes exist any more.
Changes to booting:
. The pre_init.c leaves the kernel and modules exactly as
they were left by the bootloader in physical memory
. The kernel starts running using physical addressing,
loaded at a fixed location given in its linker script by the
bootloader. All code and data in this phase are linked to
this fixed low location.
. It makes a bootstrap pagetable to map itself to a
fixed high location (also in linker script) and jumps to
the high address. All code and data then use this high addressing.
. All code/data symbols linked at the low addresses is prefixed by
an objcopy step with __k_unpaged_*, so that that code cannot
reference highly-linked symbols (which aren't valid yet) or vice
versa (symbols that aren't valid any more).
. The two addressing modes are separated in the linker script by
collecting the unpaged_*.o objects and linking them with low
addresses, and linking the rest high. Some objects are linked
twice, once low and once high.
. The bootstrap phase passes a lot of information (e.g. free memory
list, physical location of the modules, etc.) using the kinfo
struct.
. After this bootstrap the low-linked part is freed.
. The kernel maps in VM into the bootstrap page table so that VM can
begin executing. Its first job is to make page tables for all other
boot processes. So VM runs before RS, and RS gets a fully dynamic,
VM-managed address space. VM gets its privilege info from RS as usual
but that happens after RS starts running.
. Both the kernel loading VM and VM organizing boot processes happen
using the libexec logic. This removes the last reason for VM to
still know much about exec() and vm/exec.c is gone.
Further Implementation:
. All segments are based at 0 and have a 4 GB limit.
. The kernel is mapped in at the top of the virtual address
space so as not to constrain the user processes.
. Processes do not use segments from the LDT at all; there are
no segments in the LDT any more, so no LLDT is needed.
. The Minix segments T/D/S are gone and so none of the
user-space or in-kernel copy functions use them. The copy
functions use a process endpoint of NONE to realize it's
a physical address, virtual otherwise.
. The umap call only makes sense to translate a virtual address
to a physical address now.
. Segments-related calls like newmap and alloc_segments are gone.
. All segments-related translation in VM is gone (vir2map etc).
. Initialization in VM is simpler as no moving around is necessary.
. VM and all other boot processes can be linked wherever they wish
and will be mapped in at the right location by the kernel and VM
respectively.
Other changes:
. The multiboot code is less special: it does not use mb_print
for its diagnostics any more but uses printf() as normal, saving
the output into the diagnostics buffer, only printing to the
screen using the direct print functions if a panic() occurs.
. The multiboot code uses the flexible 'free memory map list'
style to receive the list of free memory if available.
. The kernel determines the memory layout of the processes to
a degree: it tells VM where the kernel starts and ends and
where the kernel wants the top of the process to be. VM then
uses this entire range, i.e. the stack is right at the top,
and mmap()ped bits of memory are placed below that downwards,
and the break grows upwards.
Other Consequences:
. Every process gets its own page table as address spaces
can't be separated any more by segments.
. As all segments are 0-based, there is no distinction between
virtual and linear addresses, nor between userspace and
kernel addresses.
. Less work is done when context switching, leading to a net
performance increase. (8% faster on my machine for 'make servers'.)
. The layout and configuration of the GDT makes sysenter and syscall
possible.
2012-05-07 16:03:35 +02:00
|
|
|
kinfo_t kinfo;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
No more intel/minix segments.
This commit removes all traces of Minix segments (the text/data/stack
memory map abstraction in the kernel) and significance of Intel segments
(hardware segments like CS, DS that add offsets to all addressing before
page table translation). This ultimately simplifies the memory layout
and addressing and makes the same layout possible on non-Intel
architectures.
There are only two types of addresses in the world now: virtual
and physical; even the kernel and processes have the same virtual
address space. Kernel and user processes can be distinguished at a
glance as processes won't use 0xF0000000 and above.
No static pre-allocated memory sizes exist any more.
Changes to booting:
. The pre_init.c leaves the kernel and modules exactly as
they were left by the bootloader in physical memory
. The kernel starts running using physical addressing,
loaded at a fixed location given in its linker script by the
bootloader. All code and data in this phase are linked to
this fixed low location.
. It makes a bootstrap pagetable to map itself to a
fixed high location (also in linker script) and jumps to
the high address. All code and data then use this high addressing.
. All code/data symbols linked at the low addresses is prefixed by
an objcopy step with __k_unpaged_*, so that that code cannot
reference highly-linked symbols (which aren't valid yet) or vice
versa (symbols that aren't valid any more).
. The two addressing modes are separated in the linker script by
collecting the unpaged_*.o objects and linking them with low
addresses, and linking the rest high. Some objects are linked
twice, once low and once high.
. The bootstrap phase passes a lot of information (e.g. free memory
list, physical location of the modules, etc.) using the kinfo
struct.
. After this bootstrap the low-linked part is freed.
. The kernel maps in VM into the bootstrap page table so that VM can
begin executing. Its first job is to make page tables for all other
boot processes. So VM runs before RS, and RS gets a fully dynamic,
VM-managed address space. VM gets its privilege info from RS as usual
but that happens after RS starts running.
. Both the kernel loading VM and VM organizing boot processes happen
using the libexec logic. This removes the last reason for VM to
still know much about exec() and vm/exec.c is gone.
Further Implementation:
. All segments are based at 0 and have a 4 GB limit.
. The kernel is mapped in at the top of the virtual address
space so as not to constrain the user processes.
. Processes do not use segments from the LDT at all; there are
no segments in the LDT any more, so no LLDT is needed.
. The Minix segments T/D/S are gone and so none of the
user-space or in-kernel copy functions use them. The copy
functions use a process endpoint of NONE to realize it's
a physical address, virtual otherwise.
. The umap call only makes sense to translate a virtual address
to a physical address now.
. Segments-related calls like newmap and alloc_segments are gone.
. All segments-related translation in VM is gone (vir2map etc).
. Initialization in VM is simpler as no moving around is necessary.
. VM and all other boot processes can be linked wherever they wish
and will be mapped in at the right location by the kernel and VM
respectively.
Other changes:
. The multiboot code is less special: it does not use mb_print
for its diagnostics any more but uses printf() as normal, saving
the output into the diagnostics buffer, only printing to the
screen using the direct print functions if a panic() occurs.
. The multiboot code uses the flexible 'free memory map list'
style to receive the list of free memory if available.
. The kernel determines the memory layout of the processes to
a degree: it tells VM where the kernel starts and ends and
where the kernel wants the top of the process to be. VM then
uses this entire range, i.e. the stack is right at the top,
and mmap()ped bits of memory are placed below that downwards,
and the break grows upwards.
Other Consequences:
. Every process gets its own page table as address spaces
can't be separated any more by segments.
. As all segments are 0-based, there is no distinction between
virtual and linear addresses, nor between userspace and
kernel addresses.
. Less work is done when context switching, leading to a net
performance increase. (8% faster on my machine for 'make servers'.)
. The layout and configuration of the GDT makes sysenter and syscall
possible.
2012-05-07 16:03:35 +02:00
|
|
|
if(OK != sys_getkinfo(&kinfo))
|
|
|
|
panic("can't get kinfo");
|
2006-04-03 14:12:04 +02:00
|
|
|
|
No more intel/minix segments.
This commit removes all traces of Minix segments (the text/data/stack
memory map abstraction in the kernel) and significance of Intel segments
(hardware segments like CS, DS that add offsets to all addressing before
page table translation). This ultimately simplifies the memory layout
and addressing and makes the same layout possible on non-Intel
architectures.
There are only two types of addresses in the world now: virtual
and physical; even the kernel and processes have the same virtual
address space. Kernel and user processes can be distinguished at a
glance as processes won't use 0xF0000000 and above.
No static pre-allocated memory sizes exist any more.
Changes to booting:
. The pre_init.c leaves the kernel and modules exactly as
they were left by the bootloader in physical memory
. The kernel starts running using physical addressing,
loaded at a fixed location given in its linker script by the
bootloader. All code and data in this phase are linked to
this fixed low location.
. It makes a bootstrap pagetable to map itself to a
fixed high location (also in linker script) and jumps to
the high address. All code and data then use this high addressing.
. All code/data symbols linked at the low addresses is prefixed by
an objcopy step with __k_unpaged_*, so that that code cannot
reference highly-linked symbols (which aren't valid yet) or vice
versa (symbols that aren't valid any more).
. The two addressing modes are separated in the linker script by
collecting the unpaged_*.o objects and linking them with low
addresses, and linking the rest high. Some objects are linked
twice, once low and once high.
. The bootstrap phase passes a lot of information (e.g. free memory
list, physical location of the modules, etc.) using the kinfo
struct.
. After this bootstrap the low-linked part is freed.
. The kernel maps in VM into the bootstrap page table so that VM can
begin executing. Its first job is to make page tables for all other
boot processes. So VM runs before RS, and RS gets a fully dynamic,
VM-managed address space. VM gets its privilege info from RS as usual
but that happens after RS starts running.
. Both the kernel loading VM and VM organizing boot processes happen
using the libexec logic. This removes the last reason for VM to
still know much about exec() and vm/exec.c is gone.
Further Implementation:
. All segments are based at 0 and have a 4 GB limit.
. The kernel is mapped in at the top of the virtual address
space so as not to constrain the user processes.
. Processes do not use segments from the LDT at all; there are
no segments in the LDT any more, so no LLDT is needed.
. The Minix segments T/D/S are gone and so none of the
user-space or in-kernel copy functions use them. The copy
functions use a process endpoint of NONE to realize it's
a physical address, virtual otherwise.
. The umap call only makes sense to translate a virtual address
to a physical address now.
. Segments-related calls like newmap and alloc_segments are gone.
. All segments-related translation in VM is gone (vir2map etc).
. Initialization in VM is simpler as no moving around is necessary.
. VM and all other boot processes can be linked wherever they wish
and will be mapped in at the right location by the kernel and VM
respectively.
Other changes:
. The multiboot code is less special: it does not use mb_print
for its diagnostics any more but uses printf() as normal, saving
the output into the diagnostics buffer, only printing to the
screen using the direct print functions if a panic() occurs.
. The multiboot code uses the flexible 'free memory map list'
style to receive the list of free memory if available.
. The kernel determines the memory layout of the processes to
a degree: it tells VM where the kernel starts and ends and
where the kernel wants the top of the process to be. VM then
uses this entire range, i.e. the stack is right at the top,
and mmap()ped bits of memory are placed below that downwards,
and the break grows upwards.
Other Consequences:
. Every process gets its own page table as address spaces
can't be separated any more by segments.
. As all segments are 0-based, there is no distinction between
virtual and linear addresses, nor between userspace and
kernel addresses.
. Less work is done when context switching, leading to a net
performance increase. (8% faster on my machine for 'make servers'.)
. The layout and configuration of the GDT makes sysenter and syscall
possible.
2012-05-07 16:03:35 +02:00
|
|
|
/* Set memgap_low to just above physical memory */
|
|
|
|
memgap_low= kinfo.mem_high_phys;
|
2006-04-03 14:12:04 +02:00
|
|
|
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);
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
/* 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;
|
2006-04-03 14:12:04 +02:00
|
|
|
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;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-03 14:12:04 +02:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf("complete_bars: intermediate gap: [0x%x .. 0x%x>\n",
|
|
|
|
memgap_low, memgap_high);
|
|
|
|
}
|
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
/* Should check main memory size */
|
2006-04-03 14:12:04 +02:00
|
|
|
if (memgap_high < memgap_low)
|
|
|
|
{
|
2007-02-16 16:50:49 +01:00
|
|
|
printf("PCI: bad memory gap: [0x%x .. 0x%x>\n",
|
2006-04-03 14:12:04 +02:00
|
|
|
memgap_low, memgap_high);
|
2010-03-05 16:05:11 +01:00
|
|
|
panic(NULL);
|
2006-04-03 14:12:04 +02:00
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2006-04-03 14:12:04 +02:00
|
|
|
iogap_high= 0x10000;
|
|
|
|
iogap_low= 0x400;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
/* 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;
|
2006-04-03 14:12:04 +02:00
|
|
|
if (base >= iogap_high)
|
2006-01-12 15:46:12 +01:00
|
|
|
continue;
|
2006-04-03 14:12:04 +02:00
|
|
|
if (base+size <= iogap_low)
|
2006-01-12 15:46:12 +01:00
|
|
|
continue;
|
2006-04-12 13:18:13 +02:00
|
|
|
#if 0
|
2006-04-11 13:50:29 +02:00
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf(
|
2013-10-01 00:42:41 +02:00
|
|
|
"pci device %d (%04x:%04x), bar %d: base 0x%x, size 0x%x\n",
|
2006-04-11 13:50:29 +02:00
|
|
|
i, pcidev[i].pd_vid, pcidev[i].pd_did,
|
|
|
|
j, base, size);
|
|
|
|
}
|
2006-04-12 13:18:13 +02:00
|
|
|
#endif
|
2006-04-03 14:12:04 +02:00
|
|
|
if (base+size-iogap_low < iogap_high-base)
|
|
|
|
iogap_low= base+size;
|
2006-01-12 15:46:12 +01:00
|
|
|
else
|
2006-04-03 14:12:04 +02:00
|
|
|
iogap_high= base;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-03 14:12:04 +02:00
|
|
|
if (iogap_high < iogap_low)
|
2006-04-11 13:50:29 +02:00
|
|
|
{
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
printf("iogap_high too low, should panic\n");
|
|
|
|
}
|
|
|
|
else
|
2010-03-05 16:05:11 +01:00
|
|
|
panic("iogap_high too low: %d", iogap_high);
|
2006-04-11 13:50:29 +02:00
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
if (debug)
|
2006-04-03 14:12:04 +02:00
|
|
|
printf("I/O range = [0x%x..0x%x>\n", iogap_low, iogap_high);
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
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;
|
2013-01-13 17:20:11 +01:00
|
|
|
if (size < PAGE_SIZE)
|
|
|
|
size= PAGE_SIZE;
|
2006-04-03 14:12:04 +02:00
|
|
|
base= memgap_high-size;
|
2006-01-12 15:46:12 +01:00
|
|
|
base &= ~(u32_t)(size-1);
|
2006-04-03 14:12:04 +02:00
|
|
|
if (base < memgap_low)
|
2010-03-05 16:05:11 +01:00
|
|
|
panic("memory base too low: %d", base);
|
2006-04-03 14:12:04 +02:00
|
|
|
memgap_high= base;
|
2006-01-12 15:46:12 +01:00
|
|
|
bar_nr= pcidev[i].pd_bar[j].pb_nr;
|
|
|
|
reg= PCI_BAR + 4*bar_nr;
|
2014-10-14 21:38:39 +02:00
|
|
|
v32= __pci_attr_r32(i, reg);
|
|
|
|
__pci_attr_w32(i, reg, v32 | base);
|
2006-01-12 15:46:12 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-04-03 14:12:04 +02:00
|
|
|
io_high= iogap_high;
|
2006-01-12 15:46:12 +01:00
|
|
|
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;
|
2006-04-03 14:12:04 +02:00
|
|
|
base= iogap_high-size;
|
2006-01-12 15:46:12 +01:00
|
|
|
base &= ~(u32_t)(size-1);
|
|
|
|
|
|
|
|
/* Assume that ISA compatibility is required. Only
|
|
|
|
* use the lowest 256 bytes out of every 1024 bytes.
|
|
|
|
*/
|
|
|
|
base &= 0xfcff;
|
|
|
|
|
2006-04-03 14:12:04 +02:00
|
|
|
if (base < iogap_low)
|
2010-03-05 16:05:11 +01:00
|
|
|
panic("I/O base too low: %d", base);
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2006-04-03 14:12:04 +02:00
|
|
|
iogap_high= base;
|
2006-01-12 15:46:12 +01:00
|
|
|
bar_nr= pcidev[i].pd_bar[j].pb_nr;
|
|
|
|
reg= PCI_BAR + 4*bar_nr;
|
2014-10-14 21:38:39 +02:00
|
|
|
v32= __pci_attr_r32(i, reg);
|
|
|
|
__pci_attr_w32(i, reg, v32 | base);
|
2006-01-12 15:46:12 +01:00
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
2006-04-03 14:12:04 +02:00
|
|
|
if (iogap_high != io_high)
|
|
|
|
{
|
|
|
|
update_bridge4dev_io(i, iogap_high,
|
|
|
|
io_high-iogap_high);
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2009-11-06 09:58:05 +01:00
|
|
|
return;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* PCI Bridge Helpers *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
probe_bus(int busind)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
u32_t dev, func, t3;
|
|
|
|
u16_t vid, did, sts, sub_vid, sub_did;
|
|
|
|
u8_t headt;
|
|
|
|
u8_t baseclass, subclass, infclass;
|
|
|
|
int devind, busnr;
|
|
|
|
const char *s, *dstr;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
|
|
|
if (debug)
|
2014-10-14 21:38:39 +02:00
|
|
|
printf("probe_bus(%d)\n", busind);
|
|
|
|
if (nr_pcidev >= NR_PCIDEV)
|
|
|
|
panic("too many PCI devices: %d", nr_pcidev);
|
|
|
|
devind= nr_pcidev;
|
|
|
|
|
|
|
|
busnr= pcibus[busind].pb_busnr;
|
|
|
|
for (dev= 0; dev<32; dev++)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
for (func= 0; func < 8; func++)
|
|
|
|
{
|
|
|
|
pcidev[devind].pd_busnr= busnr;
|
|
|
|
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(devind, PCI_HEADT);
|
|
|
|
sts= pci_attr_rsts(devind);
|
|
|
|
|
|
|
|
#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)
|
|
|
|
{
|
|
|
|
if (func == 0)
|
|
|
|
break; /* Nothing here */
|
|
|
|
|
|
|
|
/* Scan all functions of a multifunction
|
|
|
|
* device.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub_vid= __pci_attr_r16(devind, PCI_SUBVID);
|
|
|
|
sub_did= __pci_attr_r16(devind, PCI_SUBDID);
|
|
|
|
|
|
|
|
dstr= _pci_dev_name(vid, did);
|
|
|
|
if (debug)
|
|
|
|
{
|
|
|
|
if (dstr)
|
|
|
|
{
|
|
|
|
printf("%d.%lu.%lu: %s (%04X:%04X)\n",
|
|
|
|
busnr, (unsigned long)dev,
|
|
|
|
(unsigned long)func, dstr,
|
|
|
|
vid, did);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf(
|
|
|
|
"%d.%lu.%lu: Unknown device, vendor %04X (%s), device %04X\n",
|
|
|
|
busnr, (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",
|
|
|
|
sub_vid, sub_did);
|
|
|
|
}
|
|
|
|
|
|
|
|
baseclass= __pci_attr_r8(devind, PCI_BCR);
|
|
|
|
subclass= __pci_attr_r8(devind, PCI_SCR);
|
|
|
|
infclass= __pci_attr_r8(devind, PCI_PIFR);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_sub_vid= sub_vid;
|
|
|
|
pcidev[devind].pd_sub_did= sub_did;
|
|
|
|
pcidev[devind].pd_inuse= 0;
|
|
|
|
pcidev[devind].pd_bar_nr= 0;
|
|
|
|
record_irq(devind);
|
|
|
|
switch(headt & PHT_MASK)
|
|
|
|
{
|
|
|
|
case PHT_NORMAL:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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
|
|
|
|
|
|
|
|
if (nr_pcidev >= NR_PCIDEV)
|
|
|
|
panic("too many PCI devices: %d", nr_pcidev);
|
|
|
|
devind= nr_pcidev;
|
|
|
|
|
|
|
|
if (func == 0 && !(headt & PHT_MULTIFUNC))
|
|
|
|
break;
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
static u16_t
|
|
|
|
pcibr_std_rsts(int busind)
|
|
|
|
{
|
|
|
|
int devind;
|
|
|
|
|
|
|
|
devind= pcibus[busind].pb_devind;
|
|
|
|
return __pci_attr_r16(devind, PPB_SSTS);
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
pcibr_std_wsts(int busind, u16_t value)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int devind;
|
|
|
|
devind= pcibus[busind].pb_devind;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
#if 0
|
|
|
|
printf("pcibr_std_wsts(%d, 0x%X), devind= %d\n",
|
|
|
|
busind, value, devind);
|
|
|
|
#endif
|
|
|
|
__pci_attr_w16(devind, PPB_SSTS, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16_t
|
|
|
|
pcibr_cb_rsts(int busind)
|
|
|
|
{
|
|
|
|
int devind;
|
|
|
|
devind= pcibus[busind].pb_devind;
|
|
|
|
|
|
|
|
return __pci_attr_r16(devind, CBB_SSTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pcibr_cb_wsts(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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16_t
|
|
|
|
pcibr_via_rsts(int busind)
|
|
|
|
{
|
|
|
|
return 0;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
pcibr_via_wsts(int busind, u16_t value)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int devind;
|
|
|
|
devind= pcibus[busind].pb_devind;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
#if 0
|
|
|
|
printf("pcibr_via_wsts(%d, 0x%X), devind= %d (not implemented)\n",
|
|
|
|
busind, value, devind);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
complete_bridges(void)
|
|
|
|
{
|
|
|
|
int i, freebus, devind, prim_busnr;
|
|
|
|
|
|
|
|
for (i= 0; i<nr_pcibus; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (!pcibus[i].pb_needinit)
|
2005-04-21 16:53:53 +02:00
|
|
|
continue;
|
2014-10-14 21:38:39 +02:00
|
|
|
printf("should allocate bus number for bus %d\n", i);
|
|
|
|
freebus= get_freebus();
|
|
|
|
printf("got bus number %d\n", freebus);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
devind= pcibus[i].pb_devind;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
prim_busnr= pcidev[devind].pd_busnr;
|
|
|
|
if (prim_busnr != 0)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
printf(
|
|
|
|
"complete_bridge: updating subordinate bus number not implemented\n");
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
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(devind, PPB_SECBLT));
|
|
|
|
printf("BRIDGECTRL = 0x%x\n",
|
|
|
|
__pci_attr_r16(devind, PPB_BRIDGECTRL));
|
2006-04-11 13:50:29 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2010-10-21 19:07:09 +02:00
|
|
|
/*
|
|
|
|
* tells acpi which two busses are connected by this bridge. The primary bus
|
|
|
|
* (pbnr) must be already known to acpi and it must map dev as the connection to
|
|
|
|
* the secondary (sbnr) bus
|
|
|
|
*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
acpi_map_bridge(unsigned int pbnr, unsigned int dev, unsigned int sbnr)
|
2010-10-21 19:07:09 +02:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
message m;
|
|
|
|
|
|
|
|
((struct acpi_map_bridge_req *)&m)->hdr.request = ACPI_REQ_MAP_BRIDGE;
|
|
|
|
((struct acpi_map_bridge_req *)&m)->primary_bus = pbnr;
|
|
|
|
((struct acpi_map_bridge_req *)&m)->secondary_bus = sbnr;
|
|
|
|
((struct acpi_map_bridge_req *)&m)->device = dev;
|
|
|
|
|
2013-11-01 13:34:14 +01:00
|
|
|
if ((err = ipc_sendrec(acpi_ep, &m)) != OK)
|
2010-10-21 19:07:09 +02:00
|
|
|
panic("PCI: error %d while receiveing from ACPI\n", err);
|
|
|
|
|
|
|
|
if (((struct acpi_map_bridge_resp *)&m)->err != OK)
|
2011-05-06 17:41:14 +02:00
|
|
|
printf("PCI: acpi failed to map pci (%d) to pci (%d) bridge\n",
|
2010-10-21 19:07:09 +02:00
|
|
|
pbnr, sbnr);
|
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
do_pcibridge(int busind)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
int i, devind, busnr;
|
2005-04-21 16:53:53 +02:00
|
|
|
int ind, type;
|
|
|
|
u16_t vid, did;
|
2006-01-12 15:46:12 +01:00
|
|
|
u8_t sbusn, baseclass, subclass, infclass, headt;
|
2005-08-26 13:36:57 +02:00
|
|
|
u32_t t3;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
vid= did= 0; /* lint */
|
2006-01-12 15:46:12 +01:00
|
|
|
busnr= pcibus[busind].pb_busnr;
|
2005-04-21 16:53:53 +02:00
|
|
|
for (devind= 0; devind< nr_pcidev; devind++)
|
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
#if 0
|
|
|
|
printf("do_pcibridge: trying %u.%u.%u\n",
|
2014-10-14 21:38:39 +02:00
|
|
|
pcidev[devind].pd_busnr, pcidev[devind].pd_dev,
|
2006-01-12 15:46:12 +01:00
|
|
|
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;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
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;
|
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
type= pci_pcibridge[i].type;
|
2005-04-21 16:53:53 +02:00
|
|
|
if (pci_pcibridge[i].vid == 0)
|
2005-08-26 13:36:57 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
headt= __pci_attr_r8(devind, PCI_HEADT);
|
2006-01-12 15:46:12 +01:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
baseclass= __pci_attr_r8(devind, PCI_BCR);
|
|
|
|
subclass= __pci_attr_r8(devind, PCI_SCR);
|
|
|
|
infclass= __pci_attr_r8(devind, PCI_PIFR);
|
2006-01-12 15:46:12 +01:00
|
|
|
t3= ((baseclass << 16) | (subclass << 8) | infclass);
|
|
|
|
if (type == PCI_PPB_STD &&
|
|
|
|
t3 != PCI_T3_PCI2PCI &&
|
|
|
|
t3 != PCI_T3_PCI2PCI_SUBTR)
|
2005-08-26 13:36:57 +02:00
|
|
|
{
|
|
|
|
printf(
|
2013-10-01 00:42:41 +02:00
|
|
|
"Unknown PCI class %02x/%02x/%02x for PCI-to-PCI bridge, device %04X:%04X\n",
|
2006-01-12 15:46:12 +01:00
|
|
|
baseclass, subclass, infclass,
|
2005-08-26 13:36:57 +02:00
|
|
|
vid, did);
|
2006-01-12 15:46:12 +01:00
|
|
|
continue;
|
2014-10-14 21:38:39 +02:00
|
|
|
}
|
2006-01-12 15:46:12 +01:00
|
|
|
if (type == PCI_PPB_CB &&
|
|
|
|
t3 != PCI_T3_CARDBUS)
|
|
|
|
{
|
|
|
|
printf(
|
2013-10-01 00:42:41 +02:00
|
|
|
"Unknown PCI class %02x/%02x/%02x for Cardbus bridge, device %04X:%04X\n",
|
2006-01-12 15:46:12 +01:00
|
|
|
baseclass, subclass, infclass,
|
|
|
|
vid, did);
|
|
|
|
continue;
|
2014-10-14 21:38:39 +02:00
|
|
|
}
|
2005-08-26 13:36:57 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
if (debug)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2013-10-01 00:42:41 +02:00
|
|
|
printf("%u.%u.%u: PCI-to-PCI bridge: %04X:%04X\n",
|
2006-01-12 15:46:12 +01:00
|
|
|
pcidev[devind].pd_busnr,
|
2014-10-14 21:38:39 +02:00
|
|
|
pcidev[devind].pd_dev,
|
2006-01-12 15:46:12 +01:00
|
|
|
pcidev[devind].pd_func, vid, did);
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Assume that the BIOS initialized the secondary bus
|
|
|
|
* number.
|
|
|
|
*/
|
2014-10-14 21:38:39 +02:00
|
|
|
sbusn= __pci_attr_r8(devind, PPB_SECBN);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
if (nr_pcibus >= NR_PCIBUS)
|
2010-03-05 16:05:11 +01:00
|
|
|
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;
|
2006-01-12 15:46:12 +01:00
|
|
|
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;
|
2006-01-12 15:46:12 +01:00
|
|
|
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;
|
2006-01-12 15:46:12 +01:00
|
|
|
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)
|
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
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:
|
2014-10-14 21:38:39 +02:00
|
|
|
panic("unknown PCI-PCI bridge type: %d", type);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2010-10-21 19:07:09 +02:00
|
|
|
|
|
|
|
if (machine.apic_enabled)
|
2014-10-14 21:38:39 +02:00
|
|
|
acpi_map_bridge(pcidev[devind].pd_busnr,
|
|
|
|
pcidev[devind].pd_dev, sbusn);
|
|
|
|
|
|
|
|
if (debug)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
printf(
|
|
|
|
"bus(table) = %d, bus(sec) = %d, bus(subord) = %d\n",
|
|
|
|
ind, sbusn, __pci_attr_r8(devind, PPB_SUBORDBN));
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
if (sbusn == 0)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
printf("Secondary bus number not initialized\n");
|
|
|
|
continue;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
pcibus[ind].pb_needinit= 0;
|
|
|
|
|
|
|
|
probe_bus(ind);
|
|
|
|
|
|
|
|
/* Look for PCI bridges */
|
|
|
|
do_pcibridge(ind);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* pci_intel_init *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
pci_intel_init()
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
/* 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)
|
|
|
|
* PCI controller.
|
|
|
|
*/
|
|
|
|
u32_t bus, dev, func;
|
|
|
|
u16_t vid, did;
|
|
|
|
int s, i, r, busind, busnr;
|
|
|
|
const char *dstr;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
bus= 0;
|
|
|
|
dev= 0;
|
|
|
|
func= 0;
|
|
|
|
|
|
|
|
vid= PCII_RREG16_(bus, dev, func, PCI_VID);
|
|
|
|
did= PCII_RREG16_(bus, dev, func, PCI_DID);
|
|
|
|
if (OK != (s=sys_outl(PCII_CONFADD, PCII_UNSEL)))
|
|
|
|
printf("PCI: warning, sys_outl failed: %d\n", s);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (vid == 0xffff && did == 0xffff)
|
|
|
|
return; /* Nothing here */
|
|
|
|
|
|
|
|
for (i= 0; pci_intel_ctrl[i].vid; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pci_intel_ctrl[i].vid == vid &&
|
|
|
|
pci_intel_ctrl[i].did == did)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!pci_intel_ctrl[i].vid)
|
|
|
|
{
|
|
|
|
printf("pci_intel_init (warning): unknown PCI-controller:\n"
|
|
|
|
"\tvendor %04X (%s), device %04X\n",
|
|
|
|
vid, pci_vid_name(vid), did);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (nr_pcibus >= NR_PCIBUS)
|
|
|
|
panic("too many PCI busses: %d", nr_pcibus);
|
|
|
|
busind= nr_pcibus;
|
|
|
|
nr_pcibus++;
|
|
|
|
pcibus[busind].pb_type= PBT_INTEL_HOST;
|
|
|
|
pcibus[busind].pb_needinit= 0;
|
|
|
|
pcibus[busind].pb_isabridge_dev= -1;
|
|
|
|
pcibus[busind].pb_isabridge_type= 0;
|
|
|
|
pcibus[busind].pb_devind= -1;
|
|
|
|
pcibus[busind].pb_busnr= 0;
|
|
|
|
pcibus[busind].pb_rreg8= pcii_rreg8;
|
|
|
|
pcibus[busind].pb_rreg16= pcii_rreg16;
|
|
|
|
pcibus[busind].pb_rreg32= pcii_rreg32;
|
|
|
|
pcibus[busind].pb_wreg8= pcii_wreg8;
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Disable all devices for this bus */
|
|
|
|
for (i= 0; i<nr_pcidev; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcidev[i].pd_busnr != busnr)
|
|
|
|
continue;
|
|
|
|
pcidev[i].pd_inuse= 1;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* Look for PCI bridges */
|
|
|
|
do_pcibridge(busind);
|
|
|
|
|
|
|
|
/* Allocate bus numbers for uninitialized bridges */
|
|
|
|
complete_bridges();
|
|
|
|
|
|
|
|
/* Allocate I/O and memory resources for uninitialized devices */
|
|
|
|
complete_bars();
|
|
|
|
}
|
2006-03-17 16:23:59 +01:00
|
|
|
|
2008-11-19 13:26:10 +01:00
|
|
|
#if 0
|
2006-03-17 16:23:59 +01:00
|
|
|
/*===========================================================================*
|
|
|
|
* report_vga *
|
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static void
|
|
|
|
report_vga(int devind)
|
2006-03-17 16:23:59 +01:00
|
|
|
{
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
2008-11-19 13:26:10 +01:00
|
|
|
#endif
|
2006-03-17 16:23:59 +01:00
|
|
|
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* visible *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
static int
|
|
|
|
visible(struct rs_pci *aclp, int devind)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
u16_t acl_sub_vid, acl_sub_did;
|
2005-04-21 16:53:53 +02:00
|
|
|
int i;
|
2014-10-14 21:38:39 +02:00
|
|
|
u32_t class_id;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if (!aclp)
|
|
|
|
return TRUE; /* Should be changed when ACLs become
|
|
|
|
* mandatory. Do note that procfs relies
|
|
|
|
* on being able to see all devices.
|
|
|
|
*/
|
|
|
|
/* Check whether the caller is allowed to get this device. */
|
|
|
|
for (i= 0; i<aclp->rsp_nr_device; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
acl_sub_vid = aclp->rsp_device[i].sub_vid;
|
|
|
|
acl_sub_did = aclp->rsp_device[i].sub_did;
|
|
|
|
if (aclp->rsp_device[i].vid == pcidev[devind].pd_vid &&
|
|
|
|
aclp->rsp_device[i].did == pcidev[devind].pd_did &&
|
|
|
|
(acl_sub_vid == NO_SUB_VID ||
|
|
|
|
acl_sub_vid == pcidev[devind].pd_sub_vid) &&
|
|
|
|
(acl_sub_did == NO_SUB_DID ||
|
|
|
|
acl_sub_did == pcidev[devind].pd_sub_did))
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
if (!aclp->rsp_nr_class)
|
|
|
|
return FALSE;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
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++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (aclp->rsp_class[i].pciclass ==
|
|
|
|
(class_id & aclp->rsp_class[i].mask))
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
return TRUE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
return FALSE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* sef_cb_init_fresh *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
sef_cb_init_fresh(int type, sef_init_info_t *info)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
/* Initialize the pci driver. */
|
|
|
|
long v;
|
|
|
|
int i, r;
|
|
|
|
struct rprocpub rprocpub[NR_BOOT_PROCS];
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
v= 0;
|
|
|
|
env_parse("pci_debug", "d", 0, &v, 0, 1);
|
|
|
|
debug= v;
|
|
|
|
|
|
|
|
if (sys_getmachine(&machine)) {
|
|
|
|
printf("PCI: no machine\n");
|
|
|
|
return ENODEV;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
if (machine.apic_enabled &&
|
|
|
|
ds_retrieve_label_endpt("acpi", &acpi_ep) != OK) {
|
|
|
|
panic("PCI: Cannot use APIC mode without ACPI!\n");
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
/* 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))) != OK) {
|
|
|
|
panic("sys_safecopyfrom failed: %d", r);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
|
|
|
|
return(OK);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* map_service *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
map_service(struct rprocpub *rpub)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
/* Map a new service by registering a new acl entry if required. */
|
|
|
|
int i;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* 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);
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* Initialize acl slot. */
|
|
|
|
pci_acl[i].inuse = 1;
|
|
|
|
pci_acl[i].acl = rpub->pci_acl;
|
|
|
|
|
|
|
|
return(OK);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_find_dev *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
_pci_find_dev(u8_t bus, u8_t dev, u8_t func, int *devindp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
int devind;
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (devind= 0; devind < nr_pcidev; devind++)
|
|
|
|
{
|
|
|
|
if (pcidev[devind].pd_busnr == bus &&
|
|
|
|
pcidev[devind].pd_dev == dev &&
|
|
|
|
pcidev[devind].pd_func == func)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (devind >= nr_pcidev)
|
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
#if 0
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcidev[devind].pd_inuse)
|
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
#endif
|
2014-10-14 21:38:39 +02:00
|
|
|
*devindp= devind;
|
|
|
|
return 1;
|
2006-01-12 15:46:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_first_dev *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
_pci_first_dev(struct rs_pci *aclp, int *devindp, u16_t *vidp,
|
|
|
|
u16_t *didp)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
|
|
|
int devind;
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (devind= 0; devind < nr_pcidev; devind++)
|
|
|
|
{
|
2006-01-12 15:46:12 +01:00
|
|
|
#if 0
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcidev[devind].pd_inuse)
|
|
|
|
continue;
|
2006-01-12 15:46:12 +01:00
|
|
|
#endif
|
2014-10-14 21:38:39 +02:00
|
|
|
if (!visible(aclp, devind))
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (devind >= nr_pcidev)
|
|
|
|
return 0;
|
|
|
|
*devindp= devind;
|
|
|
|
*vidp= pcidev[devind].pd_vid;
|
|
|
|
*didp= pcidev[devind].pd_did;
|
|
|
|
return 1;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_next_dev *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
_pci_next_dev(struct rs_pci *aclp, int *devindp, u16_t *vidp, u16_t *didp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
int devind;
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (devind= *devindp+1; devind < nr_pcidev; devind++)
|
|
|
|
{
|
2005-04-21 16:53:53 +02:00
|
|
|
#if 0
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcidev[devind].pd_inuse)
|
|
|
|
continue;
|
2005-04-21 16:53:53 +02:00
|
|
|
#endif
|
2014-10-14 21:38:39 +02:00
|
|
|
if (!visible(aclp, devind))
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (devind >= nr_pcidev)
|
|
|
|
return 0;
|
|
|
|
*devindp= devind;
|
|
|
|
*vidp= pcidev[devind].pd_vid;
|
|
|
|
*didp= pcidev[devind].pd_did;
|
|
|
|
return 1;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_reserve *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
_pci_reserve(int devind, endpoint_t proc, struct rs_pci *aclp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int i, r;
|
|
|
|
int ilr;
|
|
|
|
struct io_range ior;
|
|
|
|
struct minix_mem_range mr;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
{
|
|
|
|
printf("pci_reserve_a: bad devind: %d\n", devind);
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
if (!visible(aclp, devind))
|
|
|
|
{
|
|
|
|
printf("pci_reserve_a: %u is not allowed to reserve %d\n",
|
|
|
|
proc, devind);
|
|
|
|
return EPERM;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if(pcidev[devind].pd_inuse && pcidev[devind].pd_proc != proc)
|
|
|
|
return EBUSY;
|
|
|
|
pcidev[devind].pd_inuse= 1;
|
|
|
|
pcidev[devind].pd_proc= proc;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; i<pcidev[devind].pd_bar_nr; i++)
|
|
|
|
{
|
|
|
|
if (pcidev[devind].pd_bar[i].pb_flags & PBF_INCOMPLETE)
|
|
|
|
{
|
|
|
|
printf("pci_reserve_a: 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;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if(debug) {
|
|
|
|
printf(
|
|
|
|
"pci_reserve_a: 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);
|
|
|
|
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;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
r= sys_privctl(proc, SYS_PRIV_ADD_MEM, &mr);
|
|
|
|
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_reserve_a: adding IRQ %d\n", ilr);
|
|
|
|
r= sys_privctl(proc, SYS_PRIV_ADD_IRQ, &ilr);
|
|
|
|
if (r != OK)
|
|
|
|
{
|
|
|
|
printf("sys_privctl failed for proc %d: %d\n",
|
|
|
|
proc, r);
|
|
|
|
}
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
return OK;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2006-01-12 15:46:12 +01:00
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_release *
|
2006-01-12 15:46:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
void
|
|
|
|
_pci_release(endpoint_t proc)
|
2006-01-12 15:46:12 +01:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int i;
|
2006-01-12 15:46:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_ids *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
_pci_ids(int devind, u16_t *vidp, u16_t *didp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
*vidp= pcidev[devind].pd_vid;
|
|
|
|
*didp= pcidev[devind].pd_did;
|
|
|
|
return OK;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_rescan_bus *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
void
|
|
|
|
_pci_rescan_bus(u8_t busnr)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
int busind;
|
2005-05-11 11:02:00 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_slot_name *
|
2005-04-21 16:53:53 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
_pci_slot_name(int devind, char **cpp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
static char label[]= "ddd.ddd.ddd.ddd";
|
|
|
|
char *end;
|
|
|
|
char *p;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
2006-03-06 16:19:51 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
p= label;
|
|
|
|
end= label+sizeof(label);
|
2006-03-06 16:19:51 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/* FIXME: domain nb is always 0 on 32bit system, but we should
|
|
|
|
* retrieve it properly, somehow. */
|
|
|
|
ntostr(0, &p, end);
|
|
|
|
*p++= '.';
|
2006-03-06 16:19:51 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
ntostr(pcidev[devind].pd_busnr, &p, end);
|
|
|
|
*p++= '.';
|
2006-03-06 16:19:51 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
ntostr(pcidev[devind].pd_dev, &p, end);
|
|
|
|
*p++= '.';
|
|
|
|
|
|
|
|
ntostr(pcidev[devind].pd_func, &p, end);
|
2006-03-06 16:19:51 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
*cpp= label;
|
|
|
|
return OK;
|
|
|
|
}
|
2006-10-20 17:01:32 +02:00
|
|
|
|
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_dev_name *
|
2006-10-20 17:01:32 +02:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
const char *
|
|
|
|
_pci_dev_name(u16_t vid, u16_t did)
|
2006-10-20 17:01:32 +02:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
for (i= 0; pci_device_table[i].name; i++)
|
2006-10-20 17:01:32 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pci_device_table[i].vid == vid &&
|
|
|
|
pci_device_table[i].did == did)
|
2006-10-20 17:01:32 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
return pci_device_table[i].name;
|
2006-10-20 17:01:32 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2006-10-20 17:01:32 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* _pci_get_bar *
|
|
|
|
*===========================================================================*/
|
|
|
|
int
|
|
|
|
_pci_get_bar(int devind, int port, u32_t *base, u32_t *size,
|
|
|
|
int *ioflag)
|
|
|
|
{
|
|
|
|
int i, reg;
|
|
|
|
|
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
for (i= 0; i < pcidev[devind].pd_bar_nr; i++)
|
2006-10-20 17:01:32 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
reg= PCI_BAR+4*pcidev[devind].pd_bar[i].pb_nr;
|
|
|
|
|
|
|
|
if (reg == port)
|
2006-10-20 17:01:32 +02:00
|
|
|
{
|
2014-10-14 21:38:39 +02:00
|
|
|
if (pcidev[devind].pd_bar[i].pb_flags & PBF_INCOMPLETE)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
*base= pcidev[devind].pd_bar[i].pb_base;
|
|
|
|
*size= pcidev[devind].pd_bar[i].pb_size;
|
|
|
|
*ioflag=
|
|
|
|
!!(pcidev[devind].pd_bar[i].pb_flags & PBF_IO);
|
|
|
|
return OK;
|
2006-10-20 17:01:32 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-14 21:38:39 +02:00
|
|
|
return EINVAL;
|
|
|
|
}
|
2006-10-20 17:01:32 +02:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* _pci_attr_r8 *
|
|
|
|
*===========================================================================*/
|
|
|
|
int
|
|
|
|
_pci_attr_r8(int devind, int port, u8_t *vp)
|
|
|
|
{
|
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
|
|
|
if (port < 0 || port > 256-1)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
*vp= __pci_attr_r8(devind, port);
|
|
|
|
return OK;
|
2006-10-20 17:01:32 +02:00
|
|
|
}
|
|
|
|
|
2008-02-22 16:59:12 +01:00
|
|
|
/*===========================================================================*
|
2014-10-14 21:38:39 +02:00
|
|
|
* _pci_attr_r16 *
|
2008-02-22 16:59:12 +01:00
|
|
|
*===========================================================================*/
|
2014-10-14 21:38:39 +02:00
|
|
|
int
|
|
|
|
_pci_attr_r16(int devind, int port, u16_t *vp)
|
|
|
|
{
|
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
|
|
|
if (port < 0 || port > 256-2)
|
|
|
|
return EINVAL;
|
2008-02-22 16:59:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
*vp= __pci_attr_r16(devind, port);
|
|
|
|
return OK;
|
|
|
|
}
|
2008-02-22 16:59:12 +01:00
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* _pci_attr_r32 *
|
|
|
|
*===========================================================================*/
|
|
|
|
int
|
|
|
|
_pci_attr_r32(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(devind, port);
|
|
|
|
return OK;
|
2008-02-22 16:59:12 +01:00
|
|
|
}
|
|
|
|
|
2014-10-14 21:38:39 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* _pci_attr_w8 *
|
|
|
|
*===========================================================================*/
|
|
|
|
int
|
|
|
|
_pci_attr_w8(int devind, int port, u8_t value)
|
|
|
|
{
|
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
|
|
|
if (port < 0 || port > 256-1)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
__pci_attr_w8(devind, port, value);
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* _pci_attr_w16 *
|
|
|
|
*===========================================================================*/
|
|
|
|
int
|
|
|
|
_pci_attr_w16(int devind, int port, u16_t value)
|
|
|
|
{
|
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
|
|
|
if (port < 0 || port > 256-2)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
__pci_attr_w16(devind, port, value);
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* _pci_attr_w32 *
|
|
|
|
*===========================================================================*/
|
|
|
|
int
|
|
|
|
_pci_attr_w32(int devind, int port, u32_t value)
|
|
|
|
{
|
|
|
|
if (devind < 0 || devind >= nr_pcidev)
|
|
|
|
return EINVAL;
|
|
|
|
if (port < 0 || port > 256-4)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
__pci_attr_w32(devind, port, value);
|
|
|
|
return OK;
|
|
|
|
}
|