mirror of
https://github.com/opnsense/src.git
synced 2026-06-09 08:43:19 -04:00
apuled: import APU 1-3 LED kernel module
Taken from: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=189772 PR: https://github.com/opnsense/core/issues/2114
This commit is contained in:
parent
1283790d68
commit
f3a6d39cc4
3 changed files with 691 additions and 0 deletions
|
|
@ -38,6 +38,7 @@ SUBDIR= \
|
|||
${_amdsmn} \
|
||||
${_amdtemp} \
|
||||
${_aout} \
|
||||
${_apuled} \
|
||||
${_arcmsr} \
|
||||
${_allwinner} \
|
||||
${_armv8crypto} \
|
||||
|
|
@ -715,6 +716,7 @@ _agp= agp
|
|||
.if ${MACHINE_CPUARCH} == "i386" || !empty(COMPAT_FREEBSD32_ENABLED)
|
||||
_aout= aout
|
||||
.endif
|
||||
_apuled= apuled
|
||||
_bios= bios
|
||||
.if ${MK_SOURCELESS_UCODE} != "no"
|
||||
_bxe= bxe
|
||||
|
|
|
|||
4
sys/modules/apuled/Makefile
Normal file
4
sys/modules/apuled/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
KMOD= apuled
|
||||
SRCS= apuled.c device_if.h bus_if.h isa_if.h pci_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
685
sys/modules/apuled/apuled.c
Normal file
685
sys/modules/apuled/apuled.c
Normal file
|
|
@ -0,0 +1,685 @@
|
|||
/*-
|
||||
* 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 <sys/lock.h>
|
||||
#include <sys/mutex.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>
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(apuled, isa, apuled_driver, 0, 0);
|
||||
Loading…
Reference in a new issue