ISA Device Driver and bus_alloc_resource_any(9)

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:

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?
 
So after digging through the bus and PCI code, it seems the generic PCI-ISA driver (isab) has allocated the resources. It does that because the device announces itself as a PCI-ISA bridge and claims certain memory and I/O resources in its BARs. Further, the PCI code will only assign resources to its direct children, so a device that sits below isab or isa in the device tree will not be able to claim resources that have been allocated by the PCI code.

My solution is to replace the generic isab driver with one that attaches specifically to the vendor and device IDs of the relevant CS5536 function. I can then grab the resources from the PCI code and assign it to my children.

I don't know if this is the "right" solution, but it seems to work so far. It has a down-side, however, because now I need to run a custom kernel and can't just build modules.
 
Hi!
How is your work on the I2C driver going? I have an ALIX too and I would like to use the I2C that is built-in. Have you succeeded?
 
Back
Top