Hi,
I have an Alix 1.C for which I'm trying to write an I2C device driver. The system is AMD Geode LX based and as such has an AMD CS5536 South Bridge that contains the I2C/SMBus hardware. Judging by the datasheet available at http://support.amd.com/us/Embedded_TechDocs/33238G_cs5536_db.pdf, the registers of the I2C controller are mapped into I/O space via a BAR.
The BAR can be read via a fixed-address MSR and I determined that the eight registers are mapped at I/O addresses 0x6000-0x6007. I have successfully used the io(4) device to access the registers from userland.
Apparently, the south bridge also announces itself as a PCI-ISA bridge and the kernel correctly recognizes it as such. The five I/O port ranges the kernel prints seem to correspond to the five "legacy devices" the south bridge contains. Here's the relevant part of the dmesg output:
This is all fine, but I'd like to add a kernel device driver that uses the I/O port range 0x6000-0x6007. So my approach is to add a child to the ISA bus, and have the driver use bus_alloc_resource_any(9) to get a hold off the port range. Unfortunately, bus_alloc_resource_any(9) fails :-(
I read the developer's handbook and it seems the ISA bus driver needs to be told what I/O port range resource ID 0 corresponds to. So I added an identify routine to my driver that uses bus_set_resource(9) to set up the resource ID (I couldn't get the device.hints mechanism to work):
Here's the code in the attach routine that fails:
What am I doing wrong? Has the isab code already allocated the port range? If so, how do I get it to give me a hold off the resource?
I have an Alix 1.C for which I'm trying to write an I2C device driver. The system is AMD Geode LX based and as such has an AMD CS5536 South Bridge that contains the I2C/SMBus hardware. Judging by the datasheet available at http://support.amd.com/us/Embedded_TechDocs/33238G_cs5536_db.pdf, the registers of the I2C controller are mapped into I/O space via a BAR.
The BAR can be read via a fixed-address MSR and I determined that the eight registers are mapped at I/O addresses 0x6000-0x6007. I have successfully used the io(4) device to access the registers from userland.
Apparently, the south bridge also announces itself as a PCI-ISA bridge and the kernel correctly recognizes it as such. The five I/O port ranges the kernel prints seem to correspond to the five "legacy devices" the south bridge contains. Here's the relevant part of the dmesg output:
Code:
alix# dmesg | grep isa
isab0: <PCI-ISA bridge> port 0x6000-0x6007,0x6100-0x61ff,0x6200-0x623f,0x9d00-0x9d7f,0x9c00-0x9c3f at device 15.0 on pci0
isa0: <ISA bus> on isab0
pmtimer0 on isa0
atrtc0: <AT realtime clock> at port 0x70-0x71 irq 8 pnpid PNP0b00 on isa0
atkbdc0: <Keyboard controller (i8042)> at port 0x60,0x64 irq 1 pnpid PNP0303 on isa0
uart0: <16550 or compatible> at port 0x3f8-0x3ff irq 4 flags 0x10 pnpid PNP0501 on isa0
ppc0: <Standard parallel printer port> at port 0x378-0x37f,0x778-0x77b irq 7 pnpid PNP0400 on isa0
uart1: <16550 or compatible> at port 0x2f8-0x2ff irq 3 pnpid PNP0501 on isa0
orm0: <ISA Option ROMs> at iomem 0xc0000-0xc7fff,0xc8000-0xd27ff,0xef000-0xeffff pnpid ORM0000 on isa0
sc0: <System console> at flags 0x100 on isa0
vga0: <Generic ISA VGA> at port 0x3c0-0x3df iomem 0xa0000-0xbffff on isa0
This is all fine, but I'd like to add a kernel device driver that uses the I/O port range 0x6000-0x6007. So my approach is to add a child to the ISA bus, and have the driver use bus_alloc_resource_any(9) to get a hold off the port range. Unfortunately, bus_alloc_resource_any(9) fails :-(
Code:
alix# kldload /tmp/glxiic.ko
glxiic0: <AMD CS5536 SMBus/I2C controller> at port 0x6000-0x6007 on isa0
glxiic0: bus_alloc_resource_any(9) failed!
device_attach: glxiic0 attach returned 6
I read the developer's handbook and it seems the ISA bus driver needs to be told what I/O port range resource ID 0 corresponds to. So I added an identify routine to my driver that uses bus_set_resource(9) to set up the resource ID (I couldn't get the device.hints mechanism to work):
Code:
uint64_t lbar;
u_long ioport, port_start, port_count;
int rc;
lbar = rdmsr(MSR_LBAR_SMB);
if (!(lbar & (1LL << 32))) {
if (bootverbose) {
device_printf(dev, "I/O memory mapping is not enabled by platform "
"firmware!\n");
}
goto out;
}
ioport = (lbar & 0xFFF8);
rc = bus_get_resource(dev, SYS_RES_IOPORT, 0, &port_start, &port_count);
if ((port_start == ioport) && (port_count == RES_SIZE)) {
goto out;
}
rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, ioport, RES_SIZE);
if (bootverbose && rc) {
device_printf(dev, "bus_set_resource(9) failed with rc=%i\n", rc);
}
Here's the code in the attach routine that fails:
Code:
sc->res_id = 0;
sc->res_io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->res_id,
RF_ACTIVE);
if (sc->res_io == NULL) {
device_printf(dev, "bus_alloc_resource_any(9) failed!\n");
goto out;
}
What am I doing wrong? Has the isab code already allocated the port range? If so, how do I get it to give me a hold off the resource?