apu1c led driver

Ian Lepore ian at FreeBSD.org
Tue Apr 22 14:40:39 UTC 2014


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.

-- Ian




More information about the freebsd-hackers mailing list