📄 pciirq.c
字号:
#include <stdio.h>
#include "typedef.h"
#include "low.h"
#include "list.h"
#include "pci.h"
extern struct irq_routing_table far * pirq_table;
/*
* If we have a IRQ routing table, use it to search for peer host
* bridges. It's a gross hack, but since there are no other known
* ways how to get a list of buses, we have to go this way.
*/
void pirq_peer_trick(void)
{
struct irq_routing_table far * rt = pirq_table;
u8 busmap[256];
long i;
struct irq_info far * e;
memset(busmap, 0, sizeof(busmap));
for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++)
{
e = &rt->slots[i];
{
int j;
#ifdef DEBUG_VERSION
safe_printf("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
for(j=0; j<4; j++)
safe_printf(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
safe_printf("\n");
#endif
}
busmap[e->bus] = 1;
}
for(i=1; i<256; i++)
{
/*
* It might be a secondary bus, but in this case its parent is already
* known (ascending bus order) and therefore pci_scan_bus returns immediately.
*/
if (busmap[i] && pci_scan_bus(i, pci_root_bus->ops, NULL))
{
#ifdef DEBUG_VERSION
safe_printf("PCI: Discovered primary peer bus %02x [IRQ]\n", i);
#endif
}
}
pcibios_last_bus = -1;
}
long pci_get_interrupt_pin(struct pci_dev far * dev, struct pci_dev far **bridge)
{
u8 pin;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (!pin)
return -1;
pin--;
while (dev->bus->self)
{
pin = (pin + PCI_SLOT(dev->devfn)) % 4;
dev = dev->bus->self;
}
*bridge = dev;
return pin;
}
int pcibios_set_irq_routing(struct pci_dev far * dev, long pin, long irq)
{
int res;
unsigned temp_cx,temp_bx;
temp_cx = (irq << 8) | (pin + 10);
temp_bx = (dev->bus->number << 8) | dev->devfn;
_asm mov ah, PCI_FUNCTION_ID
_asm mov al, SET_PCI_IRQ
_asm mov bx, temp_bx
_asm mov cx, temp_cx
_asm push ds
_asm mov dx,0f000h
_asm mov ds,dx
_asm int 1ah
_asm pop ds
_asm mov res,ax
_asm jc set_fail
_asm jmp set_ret
set_fail:
return 0;
set_ret:
return !(res & 0xff00);
}
long pirq_bios_set(struct pci_dev far * router, struct pci_dev far * dev, long pirq,
long irq)
{
struct pci_dev *bridge;
int pin = pci_get_interrupt_pin(dev, &bridge);
return pcibios_set_irq_routing(bridge, pin, irq);
}
/*
* The Intel PIIX4 pirq rules are fairly simple: "pirq" is
* just a pointer to the config space.
*/
long pirq_piix_get(struct pci_dev far * router, struct pci_dev far * dev, long pirq)
{
u8 x;
pci_read_config_byte(router, pirq, &x);
return (x < 16) ? x : 0;
}
long pirq_piix_set(struct pci_dev far * router, struct pci_dev far * dev, long pirq,
long irq)
{
pci_write_config_byte(router,pirq,(u8)irq);
return 1;
}
/*
* Common IRQ routing practice: nybbles in config space,
* offset by some magic constant.
*/
unsigned long read_config_nybble(struct pci_dev far * router, unsigned long offset,
unsigned long nr)
{
u8 x;
unsigned long reg = offset + (nr >> 1);
pci_read_config_byte(router, reg, &x);
return (nr & 1) ? (x >> 4) : (x & 0xf);
}
void write_config_nybble(struct pci_dev far * router, unsigned long offset,
unsigned long nr, unsigned long val)
{
u8 x;
unsigned long reg = offset + (nr >> 1);
pci_read_config_byte(router, reg, &x);
x = (nr & 1) ? ((x & 0x0f) | (val << 4)) : ((x & 0xf0) | val);
pci_write_config_byte(router, reg, x);
}
/*
* ALI pirq entries are damn ugly, and completely undocumented.
* This has been figured out from pirq tables, and it's not a pretty
* picture.
*/
long pirq_ali_get(struct pci_dev far * router, struct pci_dev far * dev, long pirq)
{
unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
return irqmap[read_config_nybble(router, 0x48, pirq-1)];
}
long pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, long pirq, long irq)
{
unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
unsigned int val = irqmap[irq];
if (val)
{
write_config_nybble(router, 0x48, pirq-1, val);
return 1;
}
return 0;
}
/*
* The VIA pirq rules are nibble-based, like ALI,
* but without the ugly irq number munging.
*/
long pirq_via_get(struct pci_dev far * router, struct pci_dev far * dev, long pirq)
{
return read_config_nybble(router, 0x55, pirq);
}
long pirq_via_set(struct pci_dev far * router, struct pci_dev far * dev, long pirq,
long irq)
{
write_config_nybble(router, 0x55, pirq, irq);
return 1;
}
/*
* OPTI: high four bits are nibble pointer..
* I wonder what the low bits do?
*/
long pirq_opti_get(struct pci_dev far * router, struct pci_dev far * dev, long pirq)
{
return read_config_nybble(router, 0xb8, pirq >> 4);
}
long pirq_opti_set(struct pci_dev far * router, struct pci_dev far * dev, long pirq,
long irq)
{
write_config_nybble(router, 0xb8, pirq >> 4, irq);
return 1;
}
/*
* Cyrix: nibble offset 0x5C
*/
long pirq_cyrix_get(struct pci_dev far * router, struct pci_dev far * dev, long pirq)
{
return read_config_nybble(router, 0x5C, pirq-1);
}
long pirq_cyrix_set(struct pci_dev far * router, struct pci_dev far * dev, long pirq,
long irq)
{
write_config_nybble(router, 0x5C, pirq-1, irq);
return 1;
}
/*
* PIRQ routing for SiS 85C503 router used in several SiS chipsets
* According to the SiS 5595 datasheet (preliminary V1.0, 12/24/1997)
* the related registers work as follows:
*
* general: one byte per re-routable IRQ,
* bit 7 IRQ mapping enabled (0) or disabled (1)
* bits [6:4] reserved
* bits [3:0] IRQ to map to
* allowed: 3-7, 9-12, 14-15
* reserved: 0, 1, 2, 8, 13
*
* individual registers in device config space:
*
* 0x41/0x42/0x43/0x44: PCI INT A/B/C/D - bits as in general case
*
* 0x61: IDEIRQ: bits as in general case - but:
* bits [6:5] must be written 01
* bit 4 channel-select primary (0), secondary (1)
*
* 0x62: USBIRQ: bits as in general case - but:
* bit 4 OHCI function disabled (0), enabled (1)
*
* 0x6a: ACPI/SCI IRQ - bits as in general case
*
* 0x7e: Data Acq. Module IRQ - bits as in general case
*
* Apparently there are systems implementing PCI routing table using both
* link values 0x01-0x04 and 0x41-0x44 for PCI INTA..D, but register offsets
* like 0x62 as link values for USBIRQ e.g. So there is no simple
* "register = offset + pirq" relation.
* Currently we support PCI INTA..D and USBIRQ and try our best to handle
* both link mappings.
* IDE/ACPI/DAQ mapping is currently unsupported (left untouched as set by BIOS).
*/
long pirq_sis_get(struct pci_dev far * router, struct pci_dev far * dev, long pirq)
{
u8 x;
int reg = pirq;
switch(pirq) {
case 0x01:
case 0x02:
case 0x03:
case 0x04:
reg += 0x40;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x62:
pci_read_config_byte(router, reg, &x);
if (reg != 0x62)
break;
if (!(x & 0x40))
return 0;
break;
case 0x61:
case 0x6a:
case 0x7e:
#ifdef DEBUG_VERSION
safe_printf("SiS pirq: advanced IDE/ACPI/DAQ mapping not yet implemented\n");
#endif
return 0;
default:
#ifdef DEBUG_VERSION
safe_printf("SiS router pirq escape (%d)\n", pirq);
#endif
return 0;
}
return (x & 0x80) ? 0 : (x & 0x0f);
}
long pirq_sis_set(struct pci_dev far * router, struct pci_dev far * dev, long pirq,
long irq)
{
u8 x;
int reg = pirq;
switch(pirq) {
case 0x01:
case 0x02:
case 0x03:
case 0x04:
reg += 0x40;
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x62:
x = (irq&0x0f) ? (irq&0x0f) : 0x80;
if (reg != 0x62)
break;
/* always mark OHCI enabled, as nothing else knows about this */
x |= 0x40;
break;
case 0x61:
case 0x6a:
case 0x7e:
#ifdef DEBUG_VERSION
safe_printf("advanced SiS pirq mapping not yet implemented\n");
#endif
return 0;
default:
#ifdef DEBUG_VERSION
safe_printf("SiS router pirq escape (%d)\n", pirq);
#endif
return 0;
}
pci_write_config_byte(router, reg, x);
return 1;
}
/*
* VLSI: nibble offset 0x74 - educated guess due to routing table and
* config space of VLSI 82C534 PCI-bridge/router (1004:0102)
* Tested on HP OmniBook 800 covering PIRQ 1, 2, 4, 8 for onboard
* devices, PIRQ 3 for non-pci(!) soundchip and (untested) PIRQ 6
* for the busbridge to the docking station.
*/
long pirq_vlsi_get(struct pci_dev far * router, struct pci_dev far * dev, long pirq)
{
if (pirq > 8)
{
#ifdef DEBUG_VERSION
safe_printf("VLSI router pirq escape (%d)\n", pirq);
#endif
return 0;
}
return read_config_nybble(router, 0x74, pirq-1);
}
long pirq_vlsi_set(struct pci_dev far * router, struct pci_dev far * dev, long pirq,
long irq)
{
if (pirq > 8)
{
#ifdef DEBUG_VERSION
safe_printf("VLSI router pirq escape (%d)\n", pirq);
#endif
return 0;
}
write_config_nybble(router, 0x74, pirq-1, irq);
return 1;
}
/*
* ServerWorks: PCI interrupts mapped to system IRQ lines through Index
* and Redirect I/O registers (0x0c00 and 0x0c01). The Index register
* format is (PCIIRQ## | 0x10), e.g.: PCIIRQ10=0x1a. The Redirect
* register is a straight binary coding of desired PIC IRQ (low nibble).
*
* The 'link' value in the PIRQ table is already in the correct format
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -