FreeBSD support for the Winbond W83L518D card reader controller

I have tried hard to talk to the W83L518D device, but failed repeatedly.
There is hope that, some day, someone of the real FreeBSD kernel programmers will work on MMC support, until then, I think I will stick to my USB card reader. Sigh.

I am writing a driver for the Winbond W83L518D card reader controller under FreeBSD 6.0 (-STABLE as of Nov 2, 2005). This chip can (in theory) support smart card readers, memory sticks, SD and MMC cards, and serve as a General Purpose I/O controller on some of its lines. In my notebook, the SD/MMC card function is used for the SD card slot.

Using the GPIO functions works just fine (see below), so I suppose accessing the control registers does work well.

The device seems to be attached to the ISA bus, so I suppose ISA-like routines must be used - this matches what I have seen in e.g. the ppc driver, which also uses the ISA attach routines for ACPI devices:

[thiemo@fey:~]> devinfo -v | grep _SB_.C046.C059
        isab0 pnpinfo vendor=0x8086 device=0x24cc subvendor=0x0000 subdevice=0x0000 class=0x060100 at slot=31 function=0 handle=\_SB_.C046.C059
[ ... ]
    unknown pnpinfo _HID=WEC0518 _UID=0 at handle=\_SB_.C046.C059.C140
[ ... ]

Here, the "unknown" device has the HID defined for the card reader controller chip, and the ACPI handle looks like a child of where the isab0 is attached.


In the driver's probe function, I call ACPI_ID_PROBE to find out if such a controller is attached to the system. If so, I request the device's selftest result by

ACPI_EVALUATE_OBJECT(bus, sc->sc_dev, "_STA", NULL, &buf)

When I load the module, it logs:

feynman kernel: wbcr0: <Winbond Integrated Media Reader W83L518D> port 0-0x7 irq 0 drq 4 on acpi0

With the driver attached this way, I can read a version number from the ioport allocated, and it matches the one documented in the datasheet (28946). This looks like even though the ioport address looks unusual to me, it is working in some way. This is backed by the GPIO test described below.

The next step would be writing some address into two registers of the ioport, and then accessing the SD card slot functions through this: Detecting if a card was present, and finally accessing the card should work through this. Unfortunately, I cannot get this to work, but I think the first question I have is if I access the ioport correctly so far as both port 0-0x7 and irq0 seem strange to me.

One idea was to make this device just a "bus", with the actual card devices attaching below it, maybe comparable to atkbdc - but I have no idea if this is neccessary and would solve the problem.

The mentioned attempts to access the card's port I have put into the attach() routine.


In the attach routine, I allocate an ioport, and set the base to something else:

  /* get ioport address, set to default if 0 */  
  error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &port, NULL);
  if (error) {
    device_printf(dev, "bus_get_resource() returned %d\n", error);
  } else
    device_printf(dev, "bus_get_resource() says ioport is %#x\n",
      (int) port);
  if (port == 0) {
    device_printf(dev, "setting ioport to %#x\n", WBCR_IOPORT);
    bus_set_resource(dev, SYS_RES_IOPORT, 0, (u_long)WBCR_IOPORT, 0x8);

  sc->rid_ioport = 0;
  sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid_ioport,
        (u_long)WBCR_IOPORT, (u_long)(WBCR_IOPORT + 8), 8, RF_ACTIVE);
  if (sc->ioport == NULL) {
    device_printf(dev, "I/O port resource allocation failed.\n");
    return ENXIO;
  sc->io_start = rman_get_start(sc->ioport);
  sc->spacehandle = rman_get_bushandle(sc->ioport);
  sc->spacetag = rman_get_bustag(sc->ioport);

When doing this, I cannot read the right version number from the control address ("EFER" in the datasheet) any more - maybe 0x248 is the wrong address... I assumed that what the operating system gives me (0x0) was the way to go - but then again, 0x0 seems quite strange. Sigh. Now I am playing around with addresses and offsets without any clear plan, and frequently fail.

The whole output of the device_printf()s looks like this now (after having attached the driver more than once, otherwise there are those strange ioport numbers):

wbcr0: <Winbond Integrated Media Reader W83L518D> port 0x248-0x24f irq 6 drq 4 on acpi0
wbcr0: bus_get_resource() says ioport is 0x248
wbcr0: efer_addr == 0x2e
wbcr0: Clockindex: 255
wbcr0: Card present (0xff).
wbcr0: Status returns 0xff


The controller also supports using some pins of the chip as general purpose I/O (GPIO). Luckily, one of those pins is used as LED port to reflect usage of the card slot when the chip acts as SD/MMC controller - I have tried setting it to GPIO mode and making the LED light by writing to the respective registers in the ioport I allocated: And it worked fine, the LED lighted up.

So it looks like reading and writing both work in some way.

Activating the GPIO test happens through defining WITH_GPIOTEST when compiling the module. It might be, though, that it does not work in the "current" code distributed here.

Complete source

If you want to, you can get the current state of development here: wbcr-current.tgz

If you have any idea what I am doing wrong, please drop me a mail with the hint to Thank you.