Date: Mon, 5 May 2014 14:03:20 -0400 From: John Baldwin <jhb@freebsd.org> To: freebsd-hackers@freebsd.org Cc: Larry Baird <lab@gta.com>, Ian Lepore <ian@freebsd.org> Subject: Re: apu1c led driver Message-ID: <201405051403.20390.jhb@freebsd.org> In-Reply-To: <1398177628.1124.406.camel@revolution.hippie.lan> References: <20140422020109.GA57760@gta.com> <1398177628.1124.406.camel@revolution.hippie.lan>
next in thread | previous in thread | raw e-mail | index | archive | help
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
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405051403.20390.jhb>