FreeBSD Bugzilla – Attachment 182445 Details for
Bug 189772
[NEW DRIVER] apuled(4): LED driver on PC Engines APU (1/2/3) boards
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
Updated APU led driver that support APU1, APU2, and APU3
apuled.c (text/plain), 16.03 KB, created by
lab
on 2017-05-09 15:36:18 UTC
(
hide
)
Description:
Updated APU led driver that support APU1, APU2, and APU3
Filename:
MIME Type:
Creator:
lab
Created:
2017-05-09 15:36:18 UTC
Size:
16.03 KB
patch
obsolete
>/*- > * Copyright (c) 2014-2017 Larry Baird > * All rights reserved. > * > * Feedback provided by Ermal Luci. > * > * Used information from daduke's linux driver (https://daduke.org/linux/apu2) > * > * Redistribution and use in source and binary forms, with or without > * modification, are permitted provided that the following conditions > * are met: > * 1. Redistributions of source code must retain the above copyright > * notice, this list of conditions and the following disclaimer. > * 2. Redistributions in binary form must reproduce the above copyright > * notice, this list of conditions and the following disclaimer in the > * documentation and/or other materials provided with the distribution. > * > * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > * SUCH DAMAGE. > */ > >#include <sys/param.h> >#include <sys/conf.h> >#include <sys/bus.h> >#include <sys/priv.h> >#include <sys/types.h> >#include <sys/uio.h> >#include <sys/proc.h> >#include <dev/pci/pcireg.h> >#include <dev/pci/pcivar.h> >#include <sys/kernel.h> >#include <sys/systm.h> >#include <sys/module.h> >#include <sys/rman.h> >#include <x86/bus.h> >#include <isa/isavar.h> >#include <dev/led/led.h> > >#if __FreeBSD_version < 1100000 ># define kern_getenv(a) getenv(a) >#endif // __FreeBSD_version < 1100000 > >static struct mtx gpio_lock; >MTX_SYSINIT(gpio_lock, &gpio_lock, "gpio lock", MTX_SPIN); > >/* > * Basic idea is to create two MMIO memory resources. One for LEDs and > * one for button on front of APU. Then create a device for each LED and > * the button. On an apu3 also create a device for switch for switching SIMs. > */ > >/* See dev/amdsbwd/amdsbwd.c for magic numbers for southbridges */ > >/* SB7xx RRG 2.3.3.1.1. */ >#define AMDSB_PMIO_INDEX 0xcd6 >#define AMDSB_PMIO_DATA (PMIO_INDEX + 1) >#define AMDSB_PMIO_WIDTH 2 > >#define AMDSB_SMBUS_DEVID 0x43851002 >#define AMDFCH_SMBUS_DEVID 0x780b1022 > >/* SB8xx RRG 2.3.7. */ >#define AMDSB8_MMIO_BASE_ADDR_FIND 0x24 > >/* Here are some magic numbers from APU1 BIOS. */ >#define SB_GPIO_OFFSET 0x100 >#define GPIO_187 187 // APU1 MODESW >#define GPIO_188 188 // APU1 Unknown ?? >#define GPIO_189 189 // APU1 LED1# >#define GPIO_190 190 // APU1 LED2# >#define GPIO_191 191 // APU1 LED3# > >#define SB_GPIO_ON 0x08 >#define SB_GPIO_OFF 0xC8 > >/* Here are some magic numbers for APU2. */ >#define AMDFCH41_MMIO_ADDR 0xfed80000u >#define FCH_GPIO_OFFSET 0x1500 >#define FCH_GPIO_BASE (AMDFCH41_MMIO_ADDR + FCH_GPIO_OFFSET) >#define FCH_GPIO_SIZE 0x300 >#define FCH_GPIO_BIT_WRITE 22 >#define FCH_GPIO_BIT_READ 16 >#define FCH_GPIO_BIT_DIR 23 >#define GPIO_68 68 // APU2/3 LED1# >#define GPIO_69 69 // APU2/3 LED2# >#define GPIO_70 70 // APU2/3 LED3# >#define GPIO_89 89 // APU2/3 MODESW >#define GPIO_90 90 // APU3 SIM switcher > >struct apu_cdev { > struct resource *res; > bus_size_t offset; > struct cdev *cdev; > uint32_t devid; >}; > >struct apu_rid { > int rid; > int rid_type; > struct resource *res; >}; > >struct apu_softc { > int sc_model; > uint32_t sc_devid; > struct apu_rid sc_rid[2]; ># define IDX_RID_LED 0 ># define IDX_RID_MODESW 1 > struct apu_cdev sc_led[3]; > struct apu_cdev sc_sw[2]; ># define IDX_SW_MODE 0 ># define IDX_SW_SIM 1 >}; > >/* > * Mode switch methods. > */ >static int sw_open(struct cdev *dev, int flags, int fmt, struct thread *td); >static int sw_close(struct cdev *dev, int flags, int fmt, struct thread *td); >static int sw_read(struct cdev *dev, struct uio *uio, int ioflag); >static int sw_write(struct cdev *dev, struct uio *uio, int ioflag); > >static void >apu_led_callback(void *ptr, int onoff); > >static struct cdevsw modesw_cdev = { > .d_version = D_VERSION, > .d_open = sw_open, > .d_read = sw_read, > .d_close = sw_close, > .d_name = "modesw", >}; > >static struct cdevsw simsw_cdev = { > .d_version = D_VERSION, > .d_open = sw_open, > .d_read = sw_read, > .d_write = sw_write, > .d_close = sw_close, > .d_name = "simsw", >}; > >static int >hw_is_apu( void ) >{ > int apu = 0; > char *maker; > char *product; > > maker = kern_getenv("smbios.system.maker"); > if (maker != NULL) { > if (strcasecmp("PC Engines", maker) == 0) { > product = kern_getenv("smbios.system.product"); > if (product != NULL) { > if (strcasecmp("APU", product) == 0) > apu = 1; > else if (strcasecmp("apu2", product) == 0) > apu = 2; > else if (strcasecmp("apu3", product) == 0) > apu = 3; > else if (strcasecmp("apu4", product) == 0) > apu = 4; > > freeenv(product); > } > } > > freeenv(maker); > } > > return (apu); >} > >static void >sb_gpio_write( struct resource *res, bus_size_t offset, int active ) >{ > u_int8_t value; > > value = bus_read_1(res, offset); > > if (active) > value = SB_GPIO_ON; > else > value = SB_GPIO_OFF; > > bus_write_1(res, offset, value); >} > >static char >sb_gpio_read( struct resource *res, bus_size_t offset ) >{ > uint8_t value; > char ch; > > /* Is mode switch pressed? */ > value = bus_read_1(res, offset); > > if (value == 0x28 ) > ch = '1'; > else > ch = '0'; > > return (ch); >} > >/* > * gpio methods. > */ >static void >fch_gpio_dir_set( struct resource *res, bus_size_t offset, int out ) >{ > u_int32_t value; > u_int32_t dir_bit = 1 << FCH_GPIO_BIT_DIR; > > value = bus_read_4(res, offset); > > if (out) > value |= dir_bit; > else > value &= ~dir_bit; > > bus_write_4(res, offset, value); >} > >static char >fch_gpio_read( struct resource *res, bus_size_t offset ) >{ > uint32_t value; > char ch; > u_int32_t read_bit = 1 << FCH_GPIO_BIT_READ; > > /* Is mode switch pressed? */ > value = bus_read_4(res, offset); > > if (!(value & read_bit)) > ch = '1'; > else > ch = '0'; > > return (ch); >} > >static void >fch_gpio_write( struct resource *res, bus_size_t offset, int active ) >{ > u_int32_t value; > u_int32_t write_bit = 1 << FCH_GPIO_BIT_WRITE; > > value = bus_read_4(res, offset); > > if (active) > value &= ~write_bit; > else > value |= write_bit; > > bus_write_4(res, offset, value); >} > > >/* Check to see if this is an APU board we support? */ >static void >apuled_identify(driver_t *driver, device_t parent) >{ > device_t child; > device_t smb; > uint32_t devid; > > if (resource_disabled("apuled", 0)) > return; > > if (device_find_child(parent, "apuled", -1) != NULL) > return; > > /* Do was have expected south bridge chipset? */ > smb = pci_find_bsf(0, 20, 0); > if (smb == NULL) > return; > > devid = pci_get_devid(smb); > > switch (hw_is_apu()) { > case 1: > if (devid != AMDSB_SMBUS_DEVID) > return; > break; > case 2: > case 3: > case 4: > if (devid != AMDFCH_SMBUS_DEVID) > return; > break; > default: > return; > } > > /* Everything looks good, enable probe */ > child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "apuled", -1); > if (child == NULL) > device_printf(parent, "apuled: bus add child failed\n"); >} > >static int >apu_probe_sb(device_t dev, struct apu_softc *sc) >{ > struct resource *res; > int rc; > uint32_t gpio_mmio_base; > int rid; > int i; > > /* Find the ACPImmioAddr base address */ > rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, AMDSB_PMIO_INDEX, > AMDSB_PMIO_WIDTH); > if (rc != 0) { > device_printf(dev, "bus_set_resource for MMIO failed\n"); > return (ENXIO); > } > > rid = 0; > res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0ul, ~0ul, > AMDSB_PMIO_WIDTH, RF_ACTIVE | RF_SHAREABLE); > > if (res == NULL) { > device_printf(dev, "bus_alloc_resource for MMIO failed.\n"); > return (ENXIO); > } > > /* Find base address of memory mapped WDT registers. */ > /* This will probable be 0xfed80000 */ > for (gpio_mmio_base = 0, i = 0; i < 4; i++) { > gpio_mmio_base <<= 8; > bus_write_1(res, 0, AMDSB8_MMIO_BASE_ADDR_FIND + 3 - i); > gpio_mmio_base |= bus_read_1(res, 1); > } > gpio_mmio_base &= ~0x07u; > > if (bootverbose) > device_printf(dev, "MMIO base adddress 0x%x\n", gpio_mmio_base); > > bus_release_resource(dev, SYS_RES_IOPORT, rid, res); > bus_delete_resource(dev, SYS_RES_IOPORT, rid); > > /* Set memory resource for LEDs. */ > rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, > gpio_mmio_base + SB_GPIO_OFFSET + GPIO_189, > (GPIO_191 - GPIO_189) + 1); > if (rc != 0) { > device_printf(dev, "bus_set_resource for LEDs failed\n"); > return (ENXIO); > } > > /* Set memory resource for switches. */ > rc = bus_set_resource(dev, SYS_RES_MEMORY, 1, > gpio_mmio_base + SB_GPIO_OFFSET + GPIO_187, 1); > if (rc != 0) { > device_printf(dev, "bus_set_resource for switches failed\n"); > return (ENXIO); > } > > return (0); >} > >static int >apu_probe_fch(device_t dev, struct apu_softc *sc) >{ > int rc; > u_long count; > > /* Set memory resource for LEDs. */ > rc = bus_set_resource(dev, SYS_RES_MEMORY, 0, > FCH_GPIO_BASE + (GPIO_68 * sizeof(uint32_t)), > ((GPIO_70 - GPIO_68) + 1) * sizeof(uint32_t) ); > if (rc != 0) { > device_printf(dev, "bus_set_resource for LEDs failed\n"); > return (ENXIO); > } > > /* Set memory resource for switches. */ > if (sc->sc_model == 3) > count = sizeof(uint32_t) * 2; > else > count = sizeof(uint32_t); > rc = bus_set_resource(dev, SYS_RES_MEMORY, 1, > FCH_GPIO_BASE + (GPIO_89 * sizeof(uint32_t)), count ); > if (rc != 0) { > device_printf(dev, "bus_set_resource for switches failed\n"); > return (ENXIO); > } > > return (0); >} > >/* > * APU LED device methods. > */ >static int >apuled_probe(device_t dev) >{ > int error; > char buf[100]; > struct apu_softc *sc = device_get_softc(dev); > device_t smb; > > /* Make sure we do not claim some ISA PNP device. */ > if (isa_get_logicalid(dev) != 0) > return (ENXIO); > > sc->sc_model = hw_is_apu(); > if (sc->sc_model == 0) > return (ENXIO); > > smb = pci_find_bsf(0, 20, 0); > if (smb == NULL) > return (ENXIO); > > sc->sc_devid = pci_get_devid(smb); > > snprintf(buf, sizeof(buf), "APU%d", sc->sc_model); > device_set_desc_copy(dev, buf ); > > switch( sc->sc_devid ) { > case AMDSB_SMBUS_DEVID: > error = apu_probe_sb(dev, sc); > if (error) > return error; > break; > case AMDFCH_SMBUS_DEVID: > error = apu_probe_fch(dev, sc); > if (error) > return error; > break; > default: /* Should never reach here. */ > device_printf(dev, "Unexpected APU south bridge\n" ); > return (ENXIO); > break; > } > > return (0); >} > >static int >apuled_attach(device_t dev) >{ > struct apu_softc *sc = device_get_softc(dev); > int i; > int j; > > for (i = 0; i < sizeof(sc->sc_rid)/sizeof(sc->sc_rid[0]); i++) { > sc->sc_rid[i].res = NULL; > sc->sc_rid[i].rid_type = SYS_RES_MEMORY; > sc->sc_rid[i].rid = i; > } > > for (i = 0; i < sizeof(sc->sc_rid)/sizeof(sc->sc_rid[0]); i++) { > sc->sc_rid[i].res = bus_alloc_resource_any( dev, > sc->sc_rid[i].rid_type, &sc->sc_rid[i].rid, > RF_ACTIVE | RF_SHAREABLE); > if (sc->sc_rid[i].res == NULL) { > device_printf( dev, "Unable to allocate memory region %d\n", i ); > for (j = 0; j < i; j++) { > bus_release_resource(dev, sc->sc_rid[j].rid_type, > sc->sc_rid[j].rid, sc->sc_rid[j].res); > sc->sc_rid[j].res = NULL; > bus_delete_resource(dev, sc->sc_rid[i].rid_type, > sc->sc_rid[i].rid ); > } > return (ENXIO); > } > } > > if (sc->sc_devid == AMDFCH_SMBUS_DEVID) > fch_gpio_dir_set( sc->sc_rid[IDX_RID_MODESW].res, 0, FALSE ); > > for (i = 0; i < sizeof(sc->sc_sw)/sizeof(sc->sc_sw[0]); i++) > sc->sc_sw[i].cdev = NULL; > > sc->sc_sw[IDX_SW_MODE].cdev = make_dev(&modesw_cdev, 0, UID_ROOT, > GID_WHEEL, 0440, "modesw"); > if (sc->sc_sw[IDX_SW_MODE].cdev == NULL) { > device_printf( dev, "Unable to make modesw\n" ); > } else { > sc->sc_sw[IDX_SW_MODE].cdev->si_drv1 = &sc->sc_sw[IDX_SW_MODE]; > sc->sc_sw[IDX_SW_MODE].res = sc->sc_rid[IDX_RID_MODESW].res; > sc->sc_sw[IDX_SW_MODE].offset = 0; > sc->sc_sw[IDX_SW_MODE].devid = sc->sc_devid; > } > > if (sc->sc_model == 3) { > sc->sc_sw[IDX_SW_SIM].cdev = make_dev(&simsw_cdev, 0, UID_ROOT, > GID_WHEEL, 0440, "simsw"); > if (sc->sc_sw[IDX_SW_SIM].cdev == NULL) { > device_printf( dev, "Unable to make simsw\n" ); > } else { > sc->sc_sw[IDX_SW_SIM].cdev->si_drv1 = &sc->sc_sw[IDX_SW_SIM]; > sc->sc_sw[IDX_SW_SIM].res = sc->sc_rid[IDX_RID_MODESW].res; > sc->sc_sw[IDX_SW_SIM].offset = sizeof(uint32_t); > sc->sc_sw[IDX_SW_SIM].devid = sc->sc_devid; > } > } > > for (i = 0; i < sizeof(sc->sc_led)/sizeof(sc->sc_led[0]); i++) { > char name[30]; > > snprintf( name, sizeof(name), "led%d", i + 1 ); > > sc->sc_led[i].res = sc->sc_rid[IDX_RID_LED].res; > sc->sc_led[i].devid = sc->sc_devid; > > switch (sc->sc_devid) { > case AMDSB_SMBUS_DEVID: > sc->sc_led[i].offset = i; > break; > case AMDFCH_SMBUS_DEVID: > sc->sc_led[i].offset = i * sizeof(uint32_t); > fch_gpio_dir_set(sc->sc_led[i].res, > sc->sc_led[i].offset, TRUE); > break; > default: > break; > } > > /* Make sure power LED stays on by default */ > sc->sc_led[i].cdev = led_create_state(apu_led_callback, > &sc->sc_led[i], name, i == 0); > > if (sc->sc_led[i].cdev == NULL) > device_printf(dev, "%s creation failed\n", name); > } > > return (0); >} > >static int >apuled_detach(device_t dev) >{ > struct apu_softc *sc = device_get_softc(dev); > int i; > > for (i = 0; i < sizeof(sc->sc_led)/sizeof(sc->sc_led[0]); i++) > if (sc->sc_led[i].cdev != NULL) { > /* Restore LEDs to stating state */ > if (i == 0) > apu_led_callback(&sc->sc_led[i], TRUE); > else > apu_led_callback(&sc->sc_led[i], FALSE); > > led_destroy(sc->sc_led[i].cdev); > } > > for (i = 0; i < sizeof(sc->sc_sw)/sizeof(sc->sc_sw[0]); i++) > if (sc->sc_sw[i].cdev != NULL) > destroy_dev(sc->sc_sw[i].cdev); > > for (i = 0; i < sizeof(sc->sc_rid)/sizeof(sc->sc_rid[0]); i++) { > if (sc->sc_rid[i].res != NULL) { > bus_release_resource(dev, sc->sc_rid[i].rid_type, > sc->sc_rid[i].rid, sc->sc_rid[i].res); > bus_delete_resource(dev, sc->sc_rid[i].rid_type, > sc->sc_rid[i].rid ); > } > } > > return (0); >} > >static int >sw_open(struct cdev *dev __unused, int flags __unused, int fmt __unused, > struct thread *td) >{ > int error; > > error = priv_check(td, PRIV_IO); > if (error != 0) > return (error); > error = securelevel_gt(td->td_ucred, 0); > > return (error); >} > >static int >sw_read(struct cdev *dev, struct uio *uio, int ioflag) { > struct apu_cdev *sw = (struct apu_cdev *)dev->si_drv1; > char ch = '0'; > int error; > > mtx_lock_spin(&gpio_lock); > > switch (sw->devid) { > case AMDSB_SMBUS_DEVID: > ch = sb_gpio_read( sw->res, sw->offset ); > break; > case AMDFCH_SMBUS_DEVID: > fch_gpio_dir_set( sw->res, sw->offset, FALSE ); > ch = fch_gpio_read( sw->res, sw->offset ); > break; > default: > break; > } > > mtx_unlock_spin(&gpio_lock); > > error = uiomove(&ch, sizeof(ch), uio); > return (error); >} > >static int >sw_write(struct cdev *dev, struct uio *uio, int ioflag) { > struct apu_cdev *sw = (struct apu_cdev *)dev->si_drv1; > char ch; > int error; > > error = uiomove(&ch, sizeof(ch), uio); > if (error) > return (error); > > mtx_lock_spin(&gpio_lock); > > switch (sw->devid) { > case AMDSB_SMBUS_DEVID: > break; > case AMDFCH_SMBUS_DEVID: > fch_gpio_dir_set( sw->res, sw->offset, TRUE ); > fch_gpio_write(sw->res, sw->offset, ch); > break; > default: > break; > } > > mtx_unlock_spin(&gpio_lock); > > return (0); >} > >static int >sw_close(struct cdev *dev __unused, int flags __unused, int fmt __unused, > struct thread *td __unused) >{ > return (0); >} > >static void >apu_led_callback(void *ptr, int onoff) >{ > struct apu_cdev *led = (struct apu_cdev *)ptr; > > mtx_lock_spin(&gpio_lock); > > switch (led->devid) { > case AMDSB_SMBUS_DEVID: > sb_gpio_write( led->res, led->offset, onoff ); > break; > case AMDFCH_SMBUS_DEVID: > fch_gpio_write( led->res, led->offset, onoff ); > break; > default: > break; > } > > mtx_unlock_spin(&gpio_lock); >} > >static device_method_t apuled_methods[] = { > /* Device interface */ > DEVMETHOD(device_probe, apuled_probe), > DEVMETHOD(device_attach, apuled_attach), > DEVMETHOD(device_detach, apuled_detach), > DEVMETHOD(device_identify, apuled_identify), > > DEVMETHOD_END >}; > >static driver_t apuled_driver = { > "apuled", > apuled_methods, > sizeof(struct apu_softc), >}; > >static devclass_t apuled_devclass; >DRIVER_MODULE(apuled, isa, apuled_driver, apuled_devclass, NULL, NULL);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 189772
:
142703
|
157102
|
178995
| 182445 |
217302