apu1c led driver
John Baldwin
jhb at freebsd.org
Mon May 5 18:29:26 UTC 2014
On Tuesday, April 22, 2014 10:40:28 am Ian Lepore wrote:
> On Mon, 2014-04-21 at 22:01 -0400, Larry Baird wrote:
> > There exists a nice simple linux driver for the leds on a pc engines apu1c
> > board at http://daduke.org/linux/apu/. Converting driver to use led(4)
> > and run on FreeBSD seemed straight forward. Or that is until I realized
> > I don't know how to alloc and write to a fixed set of I/O ports. I believe
> > the magic happens by using bus_alloc_resource(). Code below attempts to
allow
> > control of the second of three leds on the apu1c board. Once I get that
> > working, it should be easy to extend driver to support all three leds.
> > What is the correct way to allocate and write to a a set of I/O ports at
> > address 0xFED801BD?
> >
> > #include <sys/param.h>
> > #include <sys/bus.h>
> > #include <sys/kernel.h>
> > #include <sys/module.h>
> > #include <sys/rman.h>
> > #include <x86/bus.h>
> > #include <dev/led/led.h>
> >
> > #define BASEADDR (0xFED801BD)
> > #define LEDON (0x8)
> > #define LEDOFF (0xC8)
> >
> > #define GPIO_187 187 // MODESW
> > #define GPIO_189 189 // LED1#
> > #define GPIO_190 190 // LED2#
> > #define GPIO_191 191 // LED3#
> >
> > struct apuled_softc {
> > device_t sc_dev;
> > int sc_rid;
> > int sc_type;
> > int sc_offset;
> > struct resource *sc_res;
> > void *sc_led1;
> > };
> >
> > /*
> > * Device methods.
> > */
> > static int apuled_probe(device_t dev);
> > static int apuled_attach(device_t dev);
> > static int apuled_detach(device_t dev);
> >
> > static device_method_t apuled_methods[] = {
> > /* Device interface */
> > DEVMETHOD(device_probe, apuled_probe),
> > DEVMETHOD(device_attach, apuled_attach),
> > DEVMETHOD(device_detach, apuled_detach),
> >
> > DEVMETHOD_END
> > };
> >
> > static driver_t apuled_driver = {
> > "apuled",
> > apuled_methods,
> > sizeof(struct apuled_softc),
> > };
> >
> > static devclass_t apuled_devclass;
> > DRIVER_MODULE(apuled, pci, apuled_driver, apuled_devclass, NULL, NULL);
> >
> >
> > static int
> > apuled_probe(device_t dev)
> > {
> > device_set_desc(dev, "APU led");
> >
> > return (BUS_PROBE_GENERIC);
> > }
> >
> > static void
> > led_func(void *ptr, int onoff)
> > {
> > struct apuled_softc *sc = (struct apuled_softc *)ptr;
> > u_int8_t value;
> >
> > if ( onoff ) {
> > value = LEDON;
> > } else {
> > value = LEDOFF;
> > }
> >
> > bus_write_1(sc->sc_res, 1, value);
> > }
> >
> > static int
> > apuled_attach(device_t dev)
> > {
> > struct apuled_softc *sc = device_get_softc(dev);
> >
> > sc->sc_dev = dev;
> > sc->sc_rid = 1;
> > sc->sc_type = SYS_RES_IOPORT;
> >
> > if ( (sc->sc_res = bus_alloc_resource( sc->sc_dev,
> > sc->sc_type,
> > &sc->sc_rid,
> > BASEADDR,
> > BASEADDR + 4,
> > 4,
> > RF_ACTIVE)) == NULL ) {
> > device_printf( sc->sc_dev, "Unable to allocate bus resource\n" );
> > return ENXIO;
> >
> > } else if ( (sc->sc_led1 = led_create(led_func, sc, "led1")) == NULL )
{
> > device_printf( sc->sc_dev, "Unable to create LED 1\n" );
> > return ENXIO;
> >
> > } else {
> > device_printf( sc->sc_dev, "LED 1 created\n" );
> > }
> >
> > return (0);
> > }
> >
> > int
> > apuled_detach(device_t dev)
> > {
> > struct apuled_softc *sc = device_get_softc(dev);
> >
> > if ( sc->sc_led1 != NULL ) {
> > led_destroy( sc->sc_led1 );
> > }
> >
> > if ( sc->sc_res != NULL ) {
> > bus_release_resource( sc->sc_dev, sc->sc_type, sc->sc_rid, sc-
>sc_res );
> > }
> >
> > return (0);
> > }
> >
>
> Generally rather than specifying the physical addresses as constants in
> the device driver, the address space and/or IO port resources are
> managed by the driver for the bus the device sits on. For something
> like LEDs and GPIOs that aren't self-identifying devices on a bus and
> aren't described by ACPI or other system-provided metadata, the 'hints'
> mechanism gives you a way to provide the resource metadata
> in /boot/loader.conf.
>
> The device.hints(5) manpage has some helpful info. Typically you need
> to provide an 'at' hint to say which bus the device is on. I'm not sure
> what's right for your LED/GPIO device; I've only ever used at=isa. For
> your device, it looks like you also need maddr/msize hints, then
> bus_alloc_resource_any() with an rid of 0 and a type of MEMORY should
> get you a bus_space handle for hardware access.
ISA devices can hardcode ports this way, but this is written as a PCI driver,
so it isn't going to work. Larry, you can try changing your driver to be
an ISA driver by changing the second arg to DRIVER_MODULE() from 'pci' to
'isa'. Is there a way you can probe something or check a BIOS string to
detect this driver? If so, you should auto-create a device from an identify
routine (then you don't need any hints). Your probe routine should fail for
ISA devices with a PNP ID so you don't attach to random things like serial
ports, etc.
--
John Baldwin
More information about the freebsd-hackers
mailing list