Skip site navigation (1)Skip section navigation (2)
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>