gpio(4) support for Intel ICH southbridges.
Tested on Intel SS4200-E (ICH7), and Acorp 6A815EPD (ICH2) motherboards, on amd64 and i386 ports respectively. It should be noted that the majority of boards with ICH chips do not expose the GPIO pins for off-board use. For instance, aside from the three exposed-on-a-header pins on the 6A815EPD, another pin is also used to control write protect on the FWH. The SS4200 exposes the GPIO on a header that connects to the 10 LEDs on the front panel, as well as a tact switch on the back panel.
This commit is contained in:
parent
d1373a044c
commit
bd5cb77445
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: gpio.4,v 1.15 2009/09/25 19:37:03 mbalmer Exp $
|
||||
.\" $NetBSD: gpio.4,v 1.16 2009/09/27 17:55:32 jakllsch Exp $
|
||||
.\" $OpenBSD: gpio.4,v 1.5 2004/11/23 09:39:29 reyk Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
|
||||
@ -27,6 +27,7 @@
|
||||
.Cd "gpio* at gcscpcib?"
|
||||
.Cd "gpio* at gpiosim?"
|
||||
.Cd "gpio* at gscpcib?"
|
||||
.Cd "gpio* at ichlpcib?"
|
||||
.Cd "gpio* at nsclpcsio?"
|
||||
.Cd "gpio* at ppbus?"
|
||||
.Pp
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: ichlpcib.4,v 1.8 2009/05/18 12:32:05 wiz Exp $
|
||||
.\" $NetBSD: ichlpcib.4,v 1.9 2009/09/27 17:55:32 jakllsch Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2004 The NetBSD Foundation, Inc.
|
||||
.\" All rights reserved.
|
||||
@ -37,6 +37,7 @@
|
||||
.Cd "ichlpcib* at pci? dev ? function ?"
|
||||
.Cd "hpet0 at ichlpcib?"
|
||||
.Cd "isa0 at ichlpcib?"
|
||||
.Cd "gpio* at ichlpcib?"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
@ -62,8 +63,14 @@ via the
|
||||
node.
|
||||
A value of 0 will use the low frequency (low power) and a 1 will
|
||||
enable the high frequency (full power).
|
||||
.It
|
||||
General Purpose Input/Output.
|
||||
The ICH provides up to 64 I/O pins which can be accessed through the
|
||||
.Xr gpio 4
|
||||
framework.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr gpio 4 ,
|
||||
.Xr sysctl 8 ,
|
||||
.Xr wdogctl 8
|
||||
.Sh HISTORY
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: files.pci,v 1.7 2008/08/03 19:32:03 joerg Exp $
|
||||
# $NetBSD: files.pci,v 1.8 2009/09/27 17:55:31 jakllsch Exp $
|
||||
|
||||
device aapic
|
||||
attach aapic at pci
|
||||
@ -33,7 +33,7 @@ file arch/x86/pci/amdtemp.c amdtemp
|
||||
|
||||
# PCI-LPC bridges
|
||||
define hpetichbus {}
|
||||
device ichlpcib: acpipmtimer, isabus, sysmon_wdog, hpetichbus
|
||||
device ichlpcib: acpipmtimer, isabus, sysmon_wdog, hpetichbus, gpiobus
|
||||
attach ichlpcib at pci
|
||||
attach hpet at hpetichbus with ichlpcib_hpet
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ichlpcib.c,v 1.19 2009/08/18 17:47:46 dyoung Exp $ */
|
||||
/* $NetBSD: ichlpcib.c,v 1.20 2009/09/27 17:55:31 jakllsch Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2004 The NetBSD Foundation, Inc.
|
||||
@ -39,7 +39,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.19 2009/08/18 17:47:46 dyoung Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.20 2009/09/27 17:55:31 jakllsch Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
@ -47,12 +47,14 @@ __KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.19 2009/08/18 17:47:46 dyoung Exp $")
|
||||
#include <sys/device.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/timetc.h>
|
||||
#include <sys/gpio.h>
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcidevs.h>
|
||||
|
||||
#include <dev/gpio/gpiovar.h>
|
||||
#include <dev/sysmon/sysmonvar.h>
|
||||
|
||||
#include <dev/ic/acpipmtimer.h>
|
||||
@ -62,6 +64,9 @@ __KERNEL_RCSID(0, "$NetBSD: ichlpcib.c,v 1.19 2009/08/18 17:47:46 dyoung Exp $")
|
||||
|
||||
#include "hpet.h"
|
||||
#include "pcibvar.h"
|
||||
#include "gpio.h"
|
||||
|
||||
#define LPCIB_GPIO_NPINS 64
|
||||
|
||||
struct lpcib_softc {
|
||||
/* we call pcibattach() which assumes this starts like this: */
|
||||
@ -87,6 +92,16 @@ struct lpcib_softc {
|
||||
uint32_t sc_hpet_reg;
|
||||
#endif
|
||||
|
||||
#if NGPIO > 0
|
||||
device_t sc_gpiobus;
|
||||
kmutex_t sc_gpio_mtx;
|
||||
bus_space_tag_t sc_gpio_iot;
|
||||
bus_space_handle_t sc_gpio_ioh;
|
||||
bus_size_t sc_gpio_ios;
|
||||
struct gpio_chipset_tag sc_gpio_gc;
|
||||
gpio_pin_t sc_gpio_pins[LPCIB_GPIO_NPINS];
|
||||
#endif
|
||||
|
||||
/* Speedstep */
|
||||
pcireg_t sc_pmcon_orig;
|
||||
|
||||
@ -133,6 +148,14 @@ static void lpcib_hpet_configure(device_t);
|
||||
static int lpcib_hpet_unconfigure(device_t, int);
|
||||
#endif
|
||||
|
||||
#if NGPIO > 0
|
||||
static void lpcib_gpio_configure(device_t);
|
||||
static int lpcib_gpio_unconfigure(device_t, int);
|
||||
static int lpcib_gpio_pin_read(void *, int);
|
||||
static void lpcib_gpio_pin_write(void *, int, int);
|
||||
static void lpcib_gpio_pin_ctl(void *, int, int);
|
||||
#endif
|
||||
|
||||
struct lpcib_softc *speedstep_cookie; /* XXX */
|
||||
|
||||
CFATTACH_DECL2_NEW(ichlpcib, sizeof(struct lpcib_softc),
|
||||
@ -263,6 +286,11 @@ lpcibattach(device_t parent, device_t self, void *aux)
|
||||
lpcib_hpet_configure(self);
|
||||
#endif
|
||||
|
||||
#if NGPIO > 0
|
||||
/* Set up GPIO */
|
||||
lpcib_gpio_configure(self);
|
||||
#endif
|
||||
|
||||
/* Install power handler */
|
||||
if (!pmf_device_register1(self, lpcib_suspend, lpcib_resume,
|
||||
lpcib_shutdown))
|
||||
@ -275,6 +303,10 @@ lpcibchilddet(device_t self, device_t child)
|
||||
struct lpcib_softc *sc = device_private(self);
|
||||
uint32_t val;
|
||||
|
||||
if (sc->sc_gpiobus == child) {
|
||||
sc->sc_gpiobus = NULL;
|
||||
return;
|
||||
}
|
||||
if (sc->sc_hpetbus != child) {
|
||||
pcibchilddet(self, child);
|
||||
return;
|
||||
@ -313,7 +345,7 @@ lpcibchilddet(device_t self, device_t child)
|
||||
}
|
||||
}
|
||||
|
||||
#if NHPET > 0
|
||||
#if NHPET > 0 || NGPIO > 0
|
||||
/* XXX share this with sys/arch/i386/pci/elan520.c */
|
||||
static bool
|
||||
ifattr_match(const char *snull, const char *t)
|
||||
@ -325,13 +357,20 @@ ifattr_match(const char *snull, const char *t)
|
||||
static int
|
||||
lpcibrescan(device_t self, const char *ifattr, const int *locators)
|
||||
{
|
||||
#if NHPET > 0
|
||||
#if NHPET > 0 || NGPIO > 0
|
||||
struct lpcib_softc *sc = device_private(self);
|
||||
#endif
|
||||
|
||||
#if NHPET > 0
|
||||
if (ifattr_match(ifattr, "hpetichbus") && sc->sc_hpetbus == NULL)
|
||||
lpcib_hpet_configure(self);
|
||||
#endif
|
||||
|
||||
#if NGPIO > 0
|
||||
if (ifattr_match(ifattr, "gpiobus") && sc->sc_gpiobus == NULL)
|
||||
lpcib_gpio_configure(self);
|
||||
#endif
|
||||
|
||||
return pcibrescan(self, ifattr, locators);
|
||||
}
|
||||
|
||||
@ -348,6 +387,11 @@ lpcibdetach(device_t self, int flags)
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
#if NGPIO > 0
|
||||
if ((rc = lpcib_gpio_unconfigure(self, flags)) != 0)
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
/* Set up SpeedStep. */
|
||||
speedstep_unconfigure(self);
|
||||
|
||||
@ -989,3 +1033,183 @@ lpcib_hpet_unconfigure(device_t self, int flags)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NGPIO > 0
|
||||
static void
|
||||
lpcib_gpio_configure(device_t self)
|
||||
{
|
||||
struct lpcib_softc *sc = device_private(self);
|
||||
struct gpiobus_attach_args gba;
|
||||
pcireg_t gpio_cntl;
|
||||
uint32_t use, io, bit;
|
||||
int pin, shift, base_reg, cntl_reg, reg;
|
||||
|
||||
/* this implies ICH >= 6, and thus different mapreg */
|
||||
if (sc->sc_has_rcba) {
|
||||
base_reg = LPCIB_PCI_GPIO_BASE_ICH6;
|
||||
cntl_reg = LPCIB_PCI_GPIO_CNTL_ICH6;
|
||||
} else {
|
||||
base_reg = LPCIB_PCI_GPIO_BASE;
|
||||
cntl_reg = LPCIB_PCI_GPIO_CNTL;
|
||||
}
|
||||
|
||||
gpio_cntl = pci_conf_read(sc->sc_pcib.sc_pc, sc->sc_pcib.sc_tag,
|
||||
cntl_reg);
|
||||
|
||||
/* Is GPIO enabled? */
|
||||
if ((gpio_cntl & LPCIB_PCI_GPIO_CNTL_EN) == 0)
|
||||
return;
|
||||
|
||||
if (pci_mapreg_map(&sc->sc_pa, base_reg, PCI_MAPREG_TYPE_IO, 0,
|
||||
&sc->sc_gpio_iot, &sc->sc_gpio_ioh,
|
||||
NULL, &sc->sc_gpio_ios)) {
|
||||
aprint_error_dev(self, "can't map general purpose i/o space\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_init(&sc->sc_gpio_mtx, MUTEX_DEFAULT, IPL_NONE);
|
||||
|
||||
for (pin = 0; pin < LPCIB_GPIO_NPINS; pin++) {
|
||||
sc->sc_gpio_pins[pin].pin_num = pin;
|
||||
|
||||
/* Read initial state */
|
||||
reg = (pin < 32) ? LPCIB_GPIO_GPIO_USE_SEL : LPCIB_GPIO_GPIO_USE_SEL2;
|
||||
use = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
|
||||
reg = (pin < 32) ? LPCIB_GPIO_GP_IO_SEL : LPCIB_GPIO_GP_IO_SEL;
|
||||
io = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, 4);
|
||||
shift = pin % 32;
|
||||
bit = __BIT(shift);
|
||||
|
||||
if ((use & bit) != 0) {
|
||||
sc->sc_gpio_pins[pin].pin_caps =
|
||||
GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
|
||||
if (pin < 32)
|
||||
sc->sc_gpio_pins[pin].pin_caps |=
|
||||
GPIO_PIN_PULSATE;
|
||||
if ((io & bit) != 0)
|
||||
sc->sc_gpio_pins[pin].pin_flags =
|
||||
GPIO_PIN_INPUT;
|
||||
else
|
||||
sc->sc_gpio_pins[pin].pin_flags =
|
||||
GPIO_PIN_OUTPUT;
|
||||
} else
|
||||
sc->sc_gpio_pins[pin].pin_caps = 0;
|
||||
|
||||
if (lpcib_gpio_pin_read(sc, pin) == 0)
|
||||
sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_LOW;
|
||||
else
|
||||
sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_HIGH;
|
||||
|
||||
}
|
||||
|
||||
/* Create controller tag */
|
||||
sc->sc_gpio_gc.gp_cookie = sc;
|
||||
sc->sc_gpio_gc.gp_pin_read = lpcib_gpio_pin_read;
|
||||
sc->sc_gpio_gc.gp_pin_write = lpcib_gpio_pin_write;
|
||||
sc->sc_gpio_gc.gp_pin_ctl = lpcib_gpio_pin_ctl;
|
||||
|
||||
memset(&gba, 0, sizeof(gba));
|
||||
|
||||
gba.gba_gc = &sc->sc_gpio_gc;
|
||||
gba.gba_pins = sc->sc_gpio_pins;
|
||||
gba.gba_npins = LPCIB_GPIO_NPINS;
|
||||
|
||||
sc->sc_gpiobus = config_found_ia(self, "gpiobus", &gba, gpiobus_print);
|
||||
}
|
||||
|
||||
static int
|
||||
lpcib_gpio_unconfigure(device_t self, int flags)
|
||||
{
|
||||
struct lpcib_softc *sc = device_private(self);
|
||||
int rc;
|
||||
|
||||
if (sc->sc_gpiobus != NULL &&
|
||||
(rc = config_detach(sc->sc_gpiobus, flags)) != 0)
|
||||
return rc;
|
||||
|
||||
mutex_destroy(&sc->sc_gpio_mtx);
|
||||
|
||||
bus_space_unmap(sc->sc_gpio_iot, sc->sc_gpio_ioh, sc->sc_gpio_ios);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lpcib_gpio_pin_read(void *arg, int pin)
|
||||
{
|
||||
struct lpcib_softc *sc = arg;
|
||||
uint32_t data;
|
||||
int reg, shift;
|
||||
|
||||
reg = (pin < 32) ? LPCIB_GPIO_GP_LVL : LPCIB_GPIO_GP_LVL2;
|
||||
shift = pin % 32;
|
||||
|
||||
mutex_enter(&sc->sc_gpio_mtx);
|
||||
data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
|
||||
mutex_exit(&sc->sc_gpio_mtx);
|
||||
|
||||
return (__SHIFTOUT(data, __BIT(shift)) ? GPIO_PIN_HIGH : GPIO_PIN_LOW);
|
||||
}
|
||||
|
||||
static void
|
||||
lpcib_gpio_pin_write(void *arg, int pin, int value)
|
||||
{
|
||||
struct lpcib_softc *sc = arg;
|
||||
uint32_t data;
|
||||
int reg, shift;
|
||||
|
||||
reg = (pin < 32) ? LPCIB_GPIO_GP_LVL : LPCIB_GPIO_GP_LVL2;
|
||||
shift = pin % 32;
|
||||
|
||||
mutex_enter(&sc->sc_gpio_mtx);
|
||||
|
||||
data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
|
||||
|
||||
if(value)
|
||||
data |= __BIT(shift);
|
||||
else
|
||||
data &= ~__BIT(shift);
|
||||
|
||||
bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
|
||||
|
||||
mutex_exit(&sc->sc_gpio_mtx);
|
||||
}
|
||||
|
||||
static void
|
||||
lpcib_gpio_pin_ctl(void *arg, int pin, int flags)
|
||||
{
|
||||
struct lpcib_softc *sc = arg;
|
||||
uint32_t data;
|
||||
int reg, shift;
|
||||
|
||||
shift = pin % 32;
|
||||
reg = (pin < 32) ? LPCIB_GPIO_GP_IO_SEL : LPCIB_GPIO_GP_IO_SEL2;
|
||||
|
||||
mutex_enter(&sc->sc_gpio_mtx);
|
||||
|
||||
data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
|
||||
|
||||
if (flags & GPIO_PIN_OUTPUT)
|
||||
data &= ~__BIT(shift);
|
||||
|
||||
if (flags & GPIO_PIN_INPUT)
|
||||
data |= __BIT(shift);
|
||||
|
||||
bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
|
||||
|
||||
|
||||
if (pin < 32) {
|
||||
reg = LPCIB_GPIO_GPO_BLINK;
|
||||
data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
|
||||
|
||||
if (flags & GPIO_PIN_PULSATE)
|
||||
data |= __BIT(shift);
|
||||
else
|
||||
data &= ~__BIT(shift);
|
||||
|
||||
bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
|
||||
}
|
||||
|
||||
mutex_exit(&sc->sc_gpio_mtx);
|
||||
}
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: i82801lpcreg.h,v 1.9 2009/09/21 16:18:31 jakllsch Exp $ */
|
||||
/* $NetBSD: i82801lpcreg.h,v 1.10 2009/09/27 17:55:32 jakllsch Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2004 The NetBSD Foundation, Inc.
|
||||
@ -50,6 +50,7 @@
|
||||
/* GPIO config registers ICH0-ICH5 */
|
||||
#define LPCIB_PCI_GPIO_BASE 0x58
|
||||
#define LPCIB_PCI_GPIO_CNTL 0x5c
|
||||
#define LPCIB_PCI_GPIO_CNTL_EN (1 << 4)
|
||||
#define LPCIB_PCI_PIRQA_ROUT 0x60
|
||||
#define LPCIB_PCI_PIRQB_ROUT 0x61
|
||||
#define LPCIB_PCI_PIRQC_ROUT 0x62
|
||||
|
Loading…
Reference in New Issue
Block a user