pci api question

Jason Lunz (lunz@falooley.org)
Fri, 13 Dec 2002 12:07:44 -0500


I have a userspace program that I'd like to port to the kernel. The only
thing it really does is flip a bit in a pci config register of my Via
northbridge.

I've got it mostly done, except that I'm not sure how to express what
the userspace program does using the linux pci api. The (very ugly)
userspace program, in a nutshell, does this:

#define DWORD unsigned long

DWORD PCIRead(int reg, int fn=0, int dev=0,int bus=0, int port=0xcf8)
{
DWORD r=0x80000000;
DWORD ret,org;

r|=(( bus & 0xff) <<16);
r|=(( dev & 0x1f) <<11);
r|=(( fn & 0x7 ) << 8);
r|=(( reg & 0xfc) );

org=inl(port);
outl(r,port);
ret=inl(port+4);
outl(org,port);

return ret;
}

void PCIWrite(DWORD val, int reg, int fn=0, int dev=0,int bus=0, int port=0xcf8)
{
DWORD r=0x80000000;
DWORD org;

r|=(( bus & 0xff) <<16);
r|=(( dev & 0x1f) <<11);
r|=(( fn & 0x7 ) << 8);
r|=(( reg & 0xfc) );

org=inl(port);
outl(r,port);
outl(val,port+4);
outl(org,port);
}

void main(void) {
DWORD res;

res =PCIRead(0x52,0,0,0);
printf("read %#.8x\n", res);
res |= 0x00800000;
printf("writing %#.8x\n", res);
PCIWrite(res,0x52,0,0,0);
}

this prints out

read 0xb46ba417
writing 0xb4eba417

I want to do this within the kernel. I have this (abridged):

#include <linux/module.h>
#include <linux/pci.h>

static int __devinit via_nb_init(struct pci_dev *pdev, const struct pci_device_id *ent);
static void __devexit via_nb_remove(struct pci_dev *pdev);

static struct pci_device_id via_nb_tbl[] __devinitdata = {
{PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, PCI_ANY_ID, PCI_ANY_ID,},
{PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8371_0, PCI_ANY_ID, PCI_ANY_ID,},
{0,}
};

static struct pci_driver via_nb_driver = {
name: "via83xx_pm-nb",
id_table: via_nb_tbl,
probe: via_nb_init,
remove: via_nb_remove,
};

static int __devinit
via_nb_init(struct pci_dev *pdev, const struct pci_device_id *ent)
{
u32 enable;
printk(KERN_INFO "via83xx_pm: Initializing northbridge %s\n", pdev->name);

pci_read_config_dword(pdev, 0x52, &enable);
printk(KERN_INFO "read %#.8x\n", enable);
enable |= 0x00800000;
printk(KERN_INFO "writing %#.8x to 0x52\n", enable);
pci_write_config_dword(pdev, 0x52, enable);
return 0;
}

static void __devexit
via_nb_remove(struct pci_dev *pdev)
{
u32 enable;
pci_read_config_dword(pdev, 0x52, &enable);
printk(KERN_INFO "0x52 is %#.8x\n", enable);
enable &= ~0x00800000;
printk(KERN_INFO "would write %#.8x to 0x52\n", enable);
pci_write_config_dword(pdev, 0x52, enable);
}

static int __init
via83xx_pm_init(void)
{
found = pci_module_init(&via_nb_driver);
if (found < 0) {
printk(KERN_ERR "via83xx_pm: Could not find northbridge\n");
return 1;
}

return 0;
}

static void __exit
via83xx_pm_cleanup(void)
{
pci_unregister_driver(&via_nb_driver);
}

MODULE_LICENSE("GPL");
module_init(via83xx_pm_init);
module_exit(via83xx_pm_cleanup);

however, the pci_(read|write)_config_dword() calls seem to be changing
a different register than the userspace program. The kernel module
prints out these (very different) values:

read 0xc12cc400
would write 0xc1acc400

The userspace version works. The kernel one doesn't. Any idea what I'm
doing wrong?

thanks, Jason
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/