Attach two new devices to the AMD Elan SC520 System Controller,
elansc(4). elanpex(4) is for PCI exception reporting. I've already found some kernel bugs by reading the exceptions reported. Beware that it will spam the console a lot while the kernel and pcictl(8) probe non-existing addresses in PCI configuration space. elanpar(4) protects the kernel text from writes by the CPU and by PCI bus masters. As you might guess, this is not compatible with setting breakpoints using a debugger; detach the device using 'drvctl -d elanpar0' before you try to set breakpoints. In the future, I hope to extend elanpar(4) to provide general-purpose RAM write-protection.
This commit is contained in:
parent
9cfd7f5c79
commit
42bde7e9fa
@ -1,4 +1,4 @@
|
||||
# $NetBSD: ALL,v 1.155 2008/01/16 09:37:05 ad Exp $
|
||||
# $NetBSD: ALL,v 1.156 2008/01/21 08:04:51 dyoung Exp $
|
||||
# From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp
|
||||
#
|
||||
# ALL machine description file
|
||||
@ -17,7 +17,7 @@ include "arch/i386/conf/std.i386"
|
||||
|
||||
options INCLUDE_CONFIG_FILE # embed config file in kernel binary
|
||||
|
||||
#ident "ALL-$Revision: 1.155 $"
|
||||
#ident "ALL-$Revision: 1.156 $"
|
||||
|
||||
maxusers 32 # estimated number of users
|
||||
|
||||
@ -435,6 +435,8 @@ options PCI_INTR_FIXUP # fixup PCI interrupt routing
|
||||
# System Controllers
|
||||
elansc* at pci? dev ? function ? # AMD Elan SC520 System Controller
|
||||
gpio* at elansc?
|
||||
elanpar* at elansc?
|
||||
elanpex* at elansc?
|
||||
|
||||
# PCI bridges
|
||||
amdpcib* at pci? dev ? function ? # AMD 8111 PCI-ISA w/ HPET
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: files.i386,v 1.330 2008/01/16 09:37:07 ad Exp $
|
||||
# $NetBSD: files.i386,v 1.331 2008/01/21 08:04:51 dyoung Exp $
|
||||
#
|
||||
# new style config file for i386 architecture
|
||||
#
|
||||
@ -176,10 +176,18 @@ file arch/i386/pci/pcic_pci_machdep.c pcic_pci
|
||||
include "arch/x86/pci/files.pci"
|
||||
|
||||
# AMD Elan SC520 System Controller (PCI-Host bridge)
|
||||
device elansc: sysmon_wdog, gpiobus
|
||||
define elanparbus { }
|
||||
define elanpexbus { }
|
||||
device elansc: sysmon_wdog, gpiobus, elanparbus, elanpexbus
|
||||
attach elansc at pci
|
||||
file arch/i386/pci/elan520.c elansc
|
||||
|
||||
device elanpar
|
||||
attach elanpar at elanparbus
|
||||
|
||||
device elanpex
|
||||
attach elanpex at elanpexbus
|
||||
|
||||
# AMD Geode CS5535 Companion IDE controller
|
||||
device gcscide: ata, ata_dma, ata_udma, pciide_common, wdc_common
|
||||
attach gcscide at pci
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: elan520.c,v 1.21 2008/01/08 04:47:44 dyoung Exp $ */
|
||||
/* $NetBSD: elan520.c,v 1.22 2008/01/21 08:04:51 dyoung Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2002 The NetBSD Foundation, Inc.
|
||||
@ -47,10 +47,11 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
__KERNEL_RCSID(0, "$NetBSD: elan520.c,v 1.21 2008/01/08 04:47:44 dyoung Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: elan520.c,v 1.22 2008/01/21 08:04:51 dyoung Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/device.h>
|
||||
#include <sys/gpio.h>
|
||||
#include <sys/mutex.h>
|
||||
@ -73,8 +74,16 @@ __KERNEL_RCSID(0, "$NetBSD: elan520.c,v 1.21 2008/01/08 04:47:44 dyoung Exp $");
|
||||
|
||||
#include <dev/sysmon/sysmonvar.h>
|
||||
|
||||
#define ELAN_IRQ 1
|
||||
#define IDT_PROT_SIZE PAGE_SIZE
|
||||
|
||||
struct elansc_softc {
|
||||
struct device sc_dev;
|
||||
device_t sc_par;
|
||||
device_t sc_pex;
|
||||
|
||||
pci_chipset_tag_t sc_pc;
|
||||
pcitag_t sc_tag;
|
||||
bus_space_tag_t sc_memt;
|
||||
bus_space_handle_t sc_memh;
|
||||
int sc_echobug;
|
||||
@ -82,6 +91,13 @@ struct elansc_softc {
|
||||
kmutex_t sc_mtx;
|
||||
|
||||
struct sysmon_wdog sc_smw;
|
||||
void *sc_eih;
|
||||
void *sc_pih;
|
||||
void *sc_sh;
|
||||
uint8_t sc_mpicmode;
|
||||
uint8_t sc_picicr;
|
||||
int sc_idtpar;
|
||||
int sc_textpar;
|
||||
#if NGPIO > 0
|
||||
/* GPIO interface */
|
||||
struct gpio_chipset_tag sc_gpio_gc;
|
||||
@ -89,17 +105,33 @@ struct elansc_softc {
|
||||
#endif
|
||||
};
|
||||
|
||||
int elansc_wpvnmi = 1;
|
||||
int elansc_pcinmi = 1;
|
||||
int elansc_do_protect_idt = 0;
|
||||
|
||||
#if NGPIO > 0
|
||||
static int elansc_gpio_pin_read(void *, int);
|
||||
static void elansc_gpio_pin_write(void *, int, int);
|
||||
static void elansc_gpio_pin_ctl(void *, int, int);
|
||||
#endif
|
||||
|
||||
static void elansc_print_par(device_t, int, uint32_t);
|
||||
static void elanpex_intr_establish(device_t, struct elansc_softc *);
|
||||
static void elanpar_intr_establish(device_t, struct elansc_softc *);
|
||||
static void elanpex_intr_disestablish(struct elansc_softc *);
|
||||
static void elanpar_intr_disestablish(struct elansc_softc *);
|
||||
|
||||
static void
|
||||
elansc_childdetached(device_t self, device_t child)
|
||||
{
|
||||
/* elansc does not presently keep a pointer to children such
|
||||
* as the gpio, so there is nothing to do.
|
||||
struct elansc_softc *sc = device_private(self);
|
||||
|
||||
if (child == sc->sc_par)
|
||||
sc->sc_par = NULL;
|
||||
if (child == sc->sc_pex)
|
||||
sc->sc_pex = NULL;
|
||||
/* elansc does not presently keep a pointer to
|
||||
* the gpio, so there is nothing to do if it is detached.
|
||||
*/
|
||||
}
|
||||
|
||||
@ -252,6 +284,345 @@ static const char *elansc_speeds[] = {
|
||||
"(reserved 11)",
|
||||
};
|
||||
|
||||
static int
|
||||
elanpar_intr(void *arg)
|
||||
{
|
||||
struct elansc_softc *sc = arg;
|
||||
uint16_t wpvsta;
|
||||
unsigned win;
|
||||
uint32_t par;
|
||||
const char *wpvstr;
|
||||
|
||||
wpvsta = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_WPVSTA);
|
||||
|
||||
if ((wpvsta & MMCR_WPVSTA_WPV_STA) == 0)
|
||||
return 0;
|
||||
|
||||
win = __SHIFTOUT(wpvsta, MMCR_WPVSTA_WPV_WINDOW);
|
||||
|
||||
par = bus_space_read_4(sc->sc_memt, sc->sc_memh, MMCR_PAR(win));
|
||||
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WPVSTA,
|
||||
MMCR_WPVSTA_WPV_STA);
|
||||
|
||||
switch (wpvsta & MMCR_WPVSTA_WPV_MSTR) {
|
||||
case MMCR_WPVSTA_WPV_MSTR_CPU:
|
||||
wpvstr = "cpu";
|
||||
break;
|
||||
case MMCR_WPVSTA_WPV_MSTR_PCI:
|
||||
wpvstr = "pci";
|
||||
break;
|
||||
case MMCR_WPVSTA_WPV_MSTR_GP:
|
||||
wpvstr = "gp";
|
||||
break;
|
||||
default:
|
||||
wpvstr = "unknown";
|
||||
break;
|
||||
}
|
||||
aprint_error_dev(sc->sc_par,
|
||||
"%s violated write-protect window %u\n", wpvstr, win);
|
||||
elansc_print_par(sc->sc_par, win, par);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
elanpex_intr(void *arg)
|
||||
{
|
||||
static struct {
|
||||
const char *string;
|
||||
bool nonfatal;
|
||||
} cmd[16] = {
|
||||
[0] = {.string = "not latched"}
|
||||
, [1] = {.string = "special cycle"}
|
||||
, [2] = {.string = "i/o read"}
|
||||
, [3] = {.string = "i/o write"}
|
||||
, [4] = {.string = "4"}
|
||||
, [5] = {.string = "5"}
|
||||
, [6] = {.string = "memory rd"}
|
||||
, [7] = {.string = "memory wr"}
|
||||
, [8] = {.string = "8"}
|
||||
, [9] = {.string = "9"}
|
||||
, [10] = {.string = "cfg rd", .nonfatal = true}
|
||||
, [11] = {.string = "cfg wr"}
|
||||
, [12] = {.string = "memory rd mul"}
|
||||
, [13] = {.string = "dual-address cycle"}
|
||||
, [14] = {.string = "memory rd line"}
|
||||
, [15] = {.string = "memory wr & inv"}
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uint16_t bit;
|
||||
const char *msg;
|
||||
} mmsg[] = {
|
||||
{MMCR_HBMSTIRQSTA_M_RTRTO_IRQ_STA, "retry timeout"}
|
||||
, {MMCR_HBMSTIRQSTA_M_TABRT_IRQ_STA, "target abort"}
|
||||
, {MMCR_HBMSTIRQSTA_M_MABRT_IRQ_STA, "abort"}
|
||||
, {MMCR_HBMSTIRQSTA_M_SERR_IRQ_STA, "system error"}
|
||||
, {MMCR_HBMSTIRQSTA_M_RPER_IRQ_STA, "received parity error"}
|
||||
, {MMCR_HBMSTIRQSTA_M_DPER_IRQ_STA, "detected parity error"}
|
||||
}, tmsg[] = {
|
||||
{MMCR_HBTGTIRQSTA_T_DLYTO_IRQ_STA, "delayed txn timeout"}
|
||||
, {MMCR_HBTGTIRQSTA_T_APER_IRQ_STA, "address parity"}
|
||||
, {MMCR_HBTGTIRQSTA_T_DPER_IRQ_STA, "data parity"}
|
||||
};
|
||||
uint8_t pciarbsta;
|
||||
uint16_t mstcmd, mstirq, tgtid, tgtirq;
|
||||
uint32_t mstaddr;
|
||||
uint16_t mstack = 0, tgtack = 0;
|
||||
int fatal = 0, i, handled = 0;
|
||||
struct elansc_softc *sc = arg;
|
||||
|
||||
pciarbsta = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_PCIARBSTA);
|
||||
mstirq = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBMSTIRQSTA);
|
||||
mstaddr = bus_space_read_4(sc->sc_memt, sc->sc_memh, MMCR_MSTINTADD);
|
||||
tgtirq = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBTGTIRQSTA);
|
||||
|
||||
if ((pciarbsta & MMCR_PCIARBSTA_GNT_TO_STA) != 0) {
|
||||
aprint_error_dev(sc->sc_pex,
|
||||
"grant time-out, GNT%" __PRIuBITS "# asserted\n",
|
||||
__SHIFTOUT(pciarbsta, MMCR_PCIARBSTA_GNT_TO_ID));
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_PCIARBSTA,
|
||||
MMCR_PCIARBSTA_GNT_TO_STA);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
mstcmd = __SHIFTOUT(mstirq, MMCR_HBMSTIRQSTA_M_CMD_IRQ_ID);
|
||||
|
||||
for (i = 0; i < __arraycount(mmsg); i++) {
|
||||
if ((mstirq & mmsg[i].bit) == 0)
|
||||
continue;
|
||||
aprint_error_dev(sc->sc_pex,
|
||||
"%s %08" PRIx32 " master %s\n",
|
||||
cmd[mstcmd].string, mstaddr, mmsg[i].msg);
|
||||
|
||||
mstack |= mmsg[i].bit;
|
||||
if (!cmd[mstcmd].nonfatal)
|
||||
fatal = true;
|
||||
}
|
||||
|
||||
tgtid = __SHIFTOUT(tgtirq, MMCR_HBTGTIRQSTA_T_IRQ_ID);
|
||||
|
||||
for (i = 0; i < __arraycount(tmsg); i++) {
|
||||
if ((tgtirq & tmsg[i].bit) == 0)
|
||||
continue;
|
||||
aprint_error_dev(sc->sc_pex, "%1x target %s\n", tgtid,
|
||||
tmsg[i].msg);
|
||||
tgtack |= tmsg[i].bit;
|
||||
}
|
||||
|
||||
/* acknowledge interrupts */
|
||||
if (tgtack != 0) {
|
||||
handled = true;
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_HBTGTIRQSTA,
|
||||
tgtack);
|
||||
}
|
||||
if (mstack != 0) {
|
||||
handled = true;
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_HBMSTIRQSTA,
|
||||
mstack);
|
||||
}
|
||||
return fatal ? 0 : (handled ? 1 : 0);
|
||||
}
|
||||
|
||||
#define elansc_print_1(__dev, __sc, __reg) \
|
||||
do { \
|
||||
aprint_debug_dev(__dev, \
|
||||
"%s: %s %02" PRIx8 "\n", __func__, #__reg, \
|
||||
bus_space_read_1((__sc)->sc_memt, (__sc)->sc_memh, __reg)); \
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
static void
|
||||
elansc_print_par(device_t dev, int i, uint32_t par)
|
||||
{
|
||||
uint32_t addr, sz, unit;
|
||||
const char *tgtstr;
|
||||
|
||||
switch (par & MMCR_PAR_TARGET) {
|
||||
default:
|
||||
case MMCR_PAR_TARGET_OFF:
|
||||
tgtstr = "off";
|
||||
break;
|
||||
case MMCR_PAR_TARGET_GPIO:
|
||||
tgtstr = "gpio";
|
||||
break;
|
||||
case MMCR_PAR_TARGET_GPMEM:
|
||||
tgtstr = "gpmem";
|
||||
break;
|
||||
case MMCR_PAR_TARGET_PCI:
|
||||
tgtstr = "pci";
|
||||
break;
|
||||
case MMCR_PAR_TARGET_BOOTCS:
|
||||
tgtstr = "bootcs";
|
||||
break;
|
||||
case MMCR_PAR_TARGET_ROMCS1:
|
||||
tgtstr = "romcs1";
|
||||
break;
|
||||
case MMCR_PAR_TARGET_ROMCS2:
|
||||
tgtstr = "romcs2";
|
||||
break;
|
||||
case MMCR_PAR_TARGET_SDRAM:
|
||||
tgtstr = "sdram";
|
||||
break;
|
||||
}
|
||||
if ((par & MMCR_PAR_TARGET) == MMCR_PAR_TARGET_GPIO) {
|
||||
unit = 1;
|
||||
sz = __SHIFTOUT(par, MMCR_PAR_IO_SZ);
|
||||
addr = __SHIFTOUT(par, MMCR_PAR_IO_ST_ADR);
|
||||
} else if ((par & MMCR_PAR_PG_SZ) != 0) {
|
||||
unit = 64 * 1024;
|
||||
sz = __SHIFTOUT(par, MMCR_PAR_64KB_SZ);
|
||||
addr = __SHIFTOUT(par, MMCR_PAR_64KB_ST_ADR);
|
||||
} else {
|
||||
unit = 4 * 1024;
|
||||
sz = __SHIFTOUT(par, MMCR_PAR_4KB_SZ);
|
||||
addr = __SHIFTOUT(par, MMCR_PAR_4KB_ST_ADR);
|
||||
}
|
||||
|
||||
aprint_debug_dev(dev,
|
||||
"PAR[%d] %08" PRIx32 " tgt %s attr %1" __PRIxBITS
|
||||
" start %08" PRIx32 " size %" PRIu32 "\n",
|
||||
i, par, tgtstr, __SHIFTOUT(par, MMCR_PAR_ATTR),
|
||||
addr * unit, (sz + 1) * unit);
|
||||
}
|
||||
|
||||
static void
|
||||
elansc_print_all_par(device_t dev,
|
||||
bus_space_tag_t memt, bus_space_handle_t memh)
|
||||
{
|
||||
int i;
|
||||
uint32_t par;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
par = bus_space_read_4(memt, memh, MMCR_PAR(i));
|
||||
elansc_print_par(dev, i, par);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
elansc_alloc_par(bus_space_tag_t memt, bus_space_handle_t memh)
|
||||
{
|
||||
int i;
|
||||
uint32_t par;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
|
||||
par = bus_space_read_4(memt, memh, MMCR_PAR(i));
|
||||
|
||||
if ((par & MMCR_PAR_TARGET) == MMCR_PAR_TARGET_OFF)
|
||||
break;
|
||||
}
|
||||
if (i == 16)
|
||||
return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static void
|
||||
elansc_disable_par(bus_space_tag_t memt, bus_space_handle_t memh, int idx)
|
||||
{
|
||||
uint32_t par;
|
||||
par = bus_space_read_4(memt, memh, MMCR_PAR(idx));
|
||||
par &= ~MMCR_PAR_TARGET;
|
||||
par |= MMCR_PAR_TARGET_OFF;
|
||||
bus_space_write_4(memt, memh, MMCR_PAR(idx), par);
|
||||
}
|
||||
|
||||
static int
|
||||
elansc_protect_text(device_t self, struct elansc_softc *sc)
|
||||
{
|
||||
int i;
|
||||
uint32_t par;
|
||||
uint32_t protsize, unprotsize;
|
||||
const uint32_t sfkb = 64 * 1024;
|
||||
paddr_t start_pa, end_pa;
|
||||
extern char kernel_text, etext;
|
||||
bus_space_tag_t memt;
|
||||
bus_space_handle_t memh;
|
||||
|
||||
memt = sc->sc_memt;
|
||||
memh = sc->sc_memh;
|
||||
|
||||
if (!pmap_extract(pmap_kernel(), (vaddr_t)&kernel_text, &start_pa) ||
|
||||
!pmap_extract(pmap_kernel(), (vaddr_t)&etext, &end_pa))
|
||||
return -1;
|
||||
|
||||
if (&etext - &kernel_text != end_pa - start_pa) {
|
||||
aprint_error_dev(self, "kernel text may not be contiguous\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((i = elansc_alloc_par(memt, memh)) == -1) {
|
||||
aprint_error_dev(self, "cannot allocate PAR\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
par = bus_space_read_4(memt, memh, MMCR_PAR(i));
|
||||
|
||||
aprint_debug_dev(self,
|
||||
"protect kernel text at physical addresses %p - %p\n",
|
||||
(void *)start_pa, (void *)end_pa);
|
||||
|
||||
unprotsize = sfkb - start_pa % sfkb;
|
||||
start_pa += unprotsize;
|
||||
unprotsize += end_pa % sfkb;
|
||||
end_pa -= end_pa % sfkb;
|
||||
|
||||
aprint_debug_dev(self,
|
||||
"actually protect kernel text at physical addresses %p - %p\n",
|
||||
(void *)start_pa, (void *)end_pa);
|
||||
|
||||
aprint_verbose_dev(self,
|
||||
"%" PRIu32 " bytes of kernel text are unprotected\n", unprotsize);
|
||||
|
||||
protsize = end_pa - start_pa;
|
||||
|
||||
/* clear PG_SZ, attribute, target, size, address. */
|
||||
par = MMCR_PAR_TARGET_SDRAM | MMCR_PAR_ATTR_NOWRITE | MMCR_PAR_PG_SZ;
|
||||
par |= __SHIFTIN(protsize / sfkb - 1, MMCR_PAR_64KB_SZ);
|
||||
par |= __SHIFTIN(start_pa / sfkb, MMCR_PAR_64KB_ST_ADR);
|
||||
bus_space_write_4(memt, memh, MMCR_PAR(i), par);
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
elansc_protect_idt(struct elansc_softc *sc)
|
||||
{
|
||||
int i;
|
||||
uint32_t par;
|
||||
extern paddr_t idt_paddr;
|
||||
bus_space_tag_t memt;
|
||||
bus_space_handle_t memh;
|
||||
|
||||
memt = sc->sc_memt;
|
||||
memh = sc->sc_memh;
|
||||
|
||||
if (elansc_do_protect_idt == 0)
|
||||
return -1;
|
||||
|
||||
if ((i = elansc_alloc_par(memt, memh)) == -1)
|
||||
return -1;
|
||||
|
||||
par = bus_space_read_4(memt, memh, MMCR_PAR(i));
|
||||
|
||||
aprint_debug_dev(sc->sc_par, "protect IDT at paddr %p\n",
|
||||
(const void *)idt_paddr);
|
||||
|
||||
/* clear PG_SZ, attribute, target, size, address. */
|
||||
par = MMCR_PAR_TARGET_SDRAM | MMCR_PAR_ATTR_NOWRITE;
|
||||
par |= __SHIFTIN(IDT_PROT_SIZE / PAGE_SIZE - 1, MMCR_PAR_4KB_SZ);
|
||||
par |= __SHIFTIN(idt_paddr / PAGE_SIZE, MMCR_PAR_4KB_ST_ADR);
|
||||
bus_space_write_4(memt, memh, MMCR_PAR(i), par);
|
||||
return i;
|
||||
}
|
||||
|
||||
static void
|
||||
elanpex_intr_ack(bus_space_tag_t memt, bus_space_handle_t memh)
|
||||
{
|
||||
bus_space_write_1(memt, memh, MMCR_PCIARBSTA,
|
||||
MMCR_PCIARBSTA_GNT_TO_STA);
|
||||
bus_space_write_2(memt, memh, MMCR_HBTGTIRQSTA, MMCR_TGTIRQ_ACT);
|
||||
bus_space_write_2(memt, memh, MMCR_HBMSTIRQSTA, MMCR_MSTIRQ_ACT);
|
||||
}
|
||||
|
||||
static bool
|
||||
elansc_suspend(device_t dev)
|
||||
{
|
||||
@ -314,19 +685,312 @@ elansc_detach(device_t self, int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
elansc_intr_establish(device_t dev, int (*handler)(void *), void *arg)
|
||||
{
|
||||
struct pic *pic;
|
||||
void *ih;
|
||||
|
||||
if ((pic = intr_findpic(ELAN_IRQ)) == NULL) {
|
||||
aprint_error_dev(dev, "PIC for irq %d not found\n",
|
||||
ELAN_IRQ);
|
||||
return NULL;
|
||||
} else if ((ih = intr_establish(ELAN_IRQ, pic, ELAN_IRQ,
|
||||
IST_LEVEL, IPL_HIGH, handler, arg)) == NULL) {
|
||||
aprint_error_dev(dev,
|
||||
"could not establish interrupt\n");
|
||||
return NULL;
|
||||
}
|
||||
aprint_verbose_dev(dev, "interrupting at irq %d\n", ELAN_IRQ);
|
||||
return ih;
|
||||
}
|
||||
|
||||
static bool
|
||||
elanpex_resume(device_t self)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(device_parent(self));
|
||||
|
||||
elanpex_intr_establish(self, sc);
|
||||
return sc->sc_eih != NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
elanpex_suspend(device_t self)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(device_parent(self));
|
||||
|
||||
elanpex_intr_disestablish(sc);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
elanpar_resume(device_t self)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(device_parent(self));
|
||||
|
||||
elanpar_intr_establish(self, sc);
|
||||
return sc->sc_pih != NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
elanpar_suspend(device_t self)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(device_parent(self));
|
||||
|
||||
elanpar_intr_disestablish(sc->sc_pih);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
elanpex_intr_establish(device_t self, struct elansc_softc *sc)
|
||||
{
|
||||
uint8_t sysarbctl;
|
||||
uint16_t pcihostmap, mstirq, tgtirq;
|
||||
|
||||
pcihostmap = bus_space_read_2(sc->sc_memt, sc->sc_memh,
|
||||
MMCR_PCIHOSTMAP);
|
||||
/* Priority P2 (Master PIC IR1) */
|
||||
pcihostmap &= ~MMCR_PCIHOSTMAP_PCI_IRQ_MAP;
|
||||
pcihostmap |= __SHIFTIN(__BIT(ELAN_IRQ), MMCR_PCIHOSTMAP_PCI_IRQ_MAP);
|
||||
if (elansc_pcinmi)
|
||||
pcihostmap |= MMCR_PCIHOSTMAP_PCI_NMI_ENB;
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_PCIHOSTMAP,
|
||||
pcihostmap);
|
||||
|
||||
elanpex_intr_ack(sc->sc_memt, sc->sc_memh);
|
||||
|
||||
sysarbctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_SYSARBCTL);
|
||||
mstirq = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBMSTIRQCTL);
|
||||
tgtirq = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBTGTIRQCTL);
|
||||
|
||||
sysarbctl |= MMCR_SYSARBCTL_GNT_TO_INT_ENB;
|
||||
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_RTRTO_IRQ_ENB;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_TABRT_IRQ_ENB;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_MABRT_IRQ_ENB;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_SERR_IRQ_ENB;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_RPER_IRQ_ENB;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_DPER_IRQ_ENB;
|
||||
|
||||
tgtirq |= MMCR_HBTGTIRQCTL_T_DLYTO_IRQ_ENB;
|
||||
tgtirq |= MMCR_HBTGTIRQCTL_T_APER_IRQ_ENB;
|
||||
tgtirq |= MMCR_HBTGTIRQCTL_T_DPER_IRQ_ENB;
|
||||
|
||||
if (elansc_pcinmi) {
|
||||
sc->sc_eih = nmi_establish(elanpex_intr, sc);
|
||||
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_RTRTO_IRQ_SEL;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_TABRT_IRQ_SEL;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_MABRT_IRQ_SEL;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_SERR_IRQ_SEL;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_RPER_IRQ_SEL;
|
||||
mstirq |= MMCR_HBMSTIRQCTL_M_DPER_IRQ_SEL;
|
||||
|
||||
tgtirq |= MMCR_HBTGTIRQCTL_T_DLYTO_IRQ_SEL;
|
||||
tgtirq |= MMCR_HBTGTIRQCTL_T_APER_IRQ_SEL;
|
||||
tgtirq |= MMCR_HBTGTIRQCTL_T_DPER_IRQ_SEL;
|
||||
} else
|
||||
sc->sc_eih = elansc_intr_establish(self, elanpex_intr, sc);
|
||||
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_SYSARBCTL, sysarbctl);
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_HBMSTIRQCTL, mstirq);
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_HBTGTIRQCTL, tgtirq);
|
||||
}
|
||||
|
||||
static void
|
||||
elanpex_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(parent);
|
||||
|
||||
aprint_naive(": PCI Exceptions\n");
|
||||
aprint_normal(": AMD Elan SC520 PCI Exceptions\n");
|
||||
|
||||
elanpex_intr_establish(self, sc);
|
||||
|
||||
aprint_debug_dev(self, "HBMSTIRQCTL %04x\n",
|
||||
bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBMSTIRQCTL));
|
||||
|
||||
aprint_debug_dev(self, "HBTGTIRQCTL %04x\n",
|
||||
bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBTGTIRQCTL));
|
||||
|
||||
aprint_debug_dev(self, "PCIHOSTMAP %04x\n",
|
||||
bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_PCIHOSTMAP));
|
||||
|
||||
pci_conf_write(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG,
|
||||
pci_conf_read(sc->sc_pc, sc->sc_tag, PCI_COMMAND_STATUS_REG) |
|
||||
PCI_COMMAND_PARITY_ENABLE|PCI_COMMAND_SERR_ENABLE);
|
||||
|
||||
if (!pmf_device_register(self, elanpex_suspend, elanpex_resume))
|
||||
aprint_error_dev(self, "could not establish power hooks\n");
|
||||
}
|
||||
|
||||
static void
|
||||
elanpex_intr_disestablish(struct elansc_softc *sc)
|
||||
{
|
||||
uint8_t sysarbctl;
|
||||
uint16_t pcihostmap, mstirq, tgtirq;
|
||||
|
||||
sysarbctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_SYSARBCTL);
|
||||
sysarbctl &= ~MMCR_SYSARBCTL_GNT_TO_INT_ENB;
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_SYSARBCTL, sysarbctl);
|
||||
|
||||
mstirq = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBMSTIRQCTL);
|
||||
mstirq &= ~MMCR_HBMSTIRQCTL_M_RTRTO_IRQ_ENB;
|
||||
mstirq &= ~MMCR_HBMSTIRQCTL_M_TABRT_IRQ_ENB;
|
||||
mstirq &= ~MMCR_HBMSTIRQCTL_M_MABRT_IRQ_ENB;
|
||||
mstirq &= ~MMCR_HBMSTIRQCTL_M_SERR_IRQ_ENB;
|
||||
mstirq &= ~MMCR_HBMSTIRQCTL_M_RPER_IRQ_ENB;
|
||||
mstirq &= ~MMCR_HBMSTIRQCTL_M_DPER_IRQ_ENB;
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_HBMSTIRQCTL, mstirq);
|
||||
|
||||
tgtirq = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_HBTGTIRQCTL);
|
||||
tgtirq &= ~MMCR_HBTGTIRQCTL_T_DLYTO_IRQ_ENB;
|
||||
tgtirq &= ~MMCR_HBTGTIRQCTL_T_APER_IRQ_ENB;
|
||||
tgtirq &= ~MMCR_HBTGTIRQCTL_T_DPER_IRQ_ENB;
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_HBTGTIRQCTL, tgtirq);
|
||||
|
||||
pcihostmap = bus_space_read_2(sc->sc_memt, sc->sc_memh,
|
||||
MMCR_PCIHOSTMAP);
|
||||
/* Priority P2 (Master PIC IR1) */
|
||||
pcihostmap &= ~MMCR_PCIHOSTMAP_PCI_IRQ_MAP;
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_PCIHOSTMAP,
|
||||
pcihostmap);
|
||||
|
||||
if (elansc_pcinmi)
|
||||
nmi_disestablish(sc->sc_eih);
|
||||
else
|
||||
intr_disestablish(sc->sc_eih);
|
||||
sc->sc_eih = NULL;
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
elanpex_detach(device_t self, int flags)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(device_parent(self));
|
||||
|
||||
pmf_device_deregister(self);
|
||||
elanpex_intr_disestablish(sc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
elanpar_intr_establish(device_t self, struct elansc_softc *sc)
|
||||
{
|
||||
uint8_t adddecctl, wpvmap;
|
||||
|
||||
wpvmap = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_WPVMAP);
|
||||
wpvmap &= ~MMCR_WPVMAP_INT_MAP;
|
||||
if (elansc_wpvnmi)
|
||||
wpvmap |= MMCR_WPVMAP_INT_NMI;
|
||||
else
|
||||
wpvmap |= __SHIFTIN(__BIT(ELAN_IRQ), MMCR_WPVMAP_INT_MAP);
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_WPVMAP, wpvmap);
|
||||
|
||||
/* clear interrupt status */
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WPVSTA,
|
||||
MMCR_WPVSTA_WPV_STA);
|
||||
|
||||
/* establish interrupt */
|
||||
if (elansc_wpvnmi)
|
||||
sc->sc_pih = nmi_establish(elanpar_intr, sc);
|
||||
else
|
||||
sc->sc_pih = elansc_intr_establish(self, elanpar_intr, sc);
|
||||
|
||||
adddecctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_ADDDECCTL);
|
||||
adddecctl |= MMCR_ADDDECCTL_WPV_INT_ENB;
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_ADDDECCTL, adddecctl);
|
||||
}
|
||||
|
||||
static void
|
||||
elanpar_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(parent);
|
||||
|
||||
aprint_naive(": Programmable Address Regions\n");
|
||||
aprint_normal(": AMD Elan SC520 Programmable Address Regions\n");
|
||||
|
||||
elansc_print_1(self, sc, MMCR_WPVMAP);
|
||||
elansc_print_all_par(self, sc->sc_memt, sc->sc_memh);
|
||||
|
||||
sc->sc_idtpar = elansc_protect_idt(sc);
|
||||
sc->sc_textpar = elansc_protect_text(self, sc);
|
||||
|
||||
elanpar_intr_establish(self, sc);
|
||||
|
||||
elansc_print_1(self, sc, MMCR_ADDDECCTL);
|
||||
|
||||
if (!pmf_device_register(self, elanpar_suspend, elanpar_resume))
|
||||
aprint_error_dev(self, "could not establish power hooks\n");
|
||||
}
|
||||
|
||||
static void
|
||||
elanpar_intr_disestablish(struct elansc_softc *sc)
|
||||
{
|
||||
uint8_t adddecctl, wpvmap;
|
||||
|
||||
/* disable interrupt, acknowledge it, disestablish our
|
||||
* handler, unmap it
|
||||
*/
|
||||
adddecctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_ADDDECCTL);
|
||||
adddecctl &= ~MMCR_ADDDECCTL_WPV_INT_ENB;
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_ADDDECCTL, adddecctl);
|
||||
|
||||
bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WPVSTA,
|
||||
MMCR_WPVSTA_WPV_STA);
|
||||
|
||||
if (elansc_wpvnmi)
|
||||
nmi_disestablish(sc->sc_pih);
|
||||
else
|
||||
intr_disestablish(sc->sc_pih);
|
||||
sc->sc_pih = NULL;
|
||||
|
||||
wpvmap = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_WPVMAP);
|
||||
wpvmap &= ~MMCR_WPVMAP_INT_MAP;
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_WPVMAP, wpvmap);
|
||||
}
|
||||
|
||||
static int
|
||||
elanpar_detach(device_t self, int flags)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(device_parent(self));
|
||||
|
||||
pmf_device_deregister(self);
|
||||
|
||||
if (sc->sc_textpar != -1) {
|
||||
elansc_disable_par(sc->sc_memt, sc->sc_memh, sc->sc_textpar);
|
||||
sc->sc_textpar = -1;
|
||||
}
|
||||
if (sc->sc_idtpar != -1) {
|
||||
elansc_disable_par(sc->sc_memt, sc->sc_memh, sc->sc_idtpar);
|
||||
sc->sc_idtpar = -1;
|
||||
}
|
||||
|
||||
elanpar_intr_disestablish(sc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
elansc_attach(device_t parent, device_t self, void *aux)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(self);
|
||||
struct pci_attach_args *pa = aux;
|
||||
uint16_t rev;
|
||||
uint8_t ressta, cpuctl;
|
||||
uint8_t cpuctl, picicr, ressta;
|
||||
#if 0
|
||||
struct pci_conf_state pcf;
|
||||
#endif
|
||||
#if NGPIO > 0
|
||||
struct gpiobus_attach_args gba;
|
||||
int pin;
|
||||
int reg, shift;
|
||||
int pin, reg, shift;
|
||||
uint16_t data;
|
||||
#endif
|
||||
sc->sc_pc = pa->pa_pc;
|
||||
sc->sc_tag = pa->pa_tag;
|
||||
|
||||
aprint_naive(": System Controller\n");
|
||||
aprint_normal(": AMD Elan SC520 System Controller\n");
|
||||
@ -377,13 +1041,39 @@ elansc_attach(device_t parent, device_t self, void *aux)
|
||||
"WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n");
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA, ressta);
|
||||
|
||||
elansc_print_1(self, sc, MMCR_MPICMODE);
|
||||
elansc_print_1(self, sc, MMCR_SL1PICMODE);
|
||||
elansc_print_1(self, sc, MMCR_SL2PICMODE);
|
||||
elansc_print_1(self, sc, MMCR_PICICR);
|
||||
|
||||
sc->sc_mpicmode = bus_space_read_1(sc->sc_memt, sc->sc_memh,
|
||||
MMCR_MPICMODE);
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_MPICMODE,
|
||||
sc->sc_mpicmode | __BIT(ELAN_IRQ));
|
||||
|
||||
sc->sc_picicr = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_PICICR);
|
||||
picicr = sc->sc_picicr;
|
||||
if (elansc_pcinmi || elansc_wpvnmi)
|
||||
picicr |= MMCR_PICICR_NMI_ENB;
|
||||
#if 0
|
||||
/* PC/AT compatibility */
|
||||
picicr |= MMCR_PICICR_S1_GINT_MODE|MMCR_PICICR_M_GINT_MODE;
|
||||
#endif
|
||||
bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_PICICR, picicr);
|
||||
|
||||
elansc_print_1(self, sc, MMCR_PICICR);
|
||||
elansc_print_1(self, sc, MMCR_MPICMODE);
|
||||
|
||||
mutex_enter(&sc->sc_mtx);
|
||||
/* Set up the watchdog registers with some defaults. */
|
||||
elansc_wdogctl_write(sc, WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30);
|
||||
|
||||
/* ...and clear it. */
|
||||
elansc_wdogctl_reset(sc);
|
||||
mutex_exit(&sc->sc_mtx);
|
||||
|
||||
pmf_device_register(self, elansc_suspend, elansc_resume);
|
||||
if (!pmf_device_register(self, elansc_suspend, elansc_resume))
|
||||
aprint_error_dev(self, "could not establish power hooks\n");
|
||||
|
||||
#if NGPIO > 0
|
||||
/* Initialize GPIO pins array */
|
||||
@ -416,6 +1106,8 @@ elansc_attach(device_t parent, device_t self, void *aux)
|
||||
gba.gba_pins = sc->sc_gpio_pins;
|
||||
gba.gba_npins = ELANSC_PIO_NPINS;
|
||||
|
||||
sc->sc_par = config_found_ia(&sc->sc_dev, "elanparbus", NULL, NULL);
|
||||
sc->sc_pex = config_found_ia(&sc->sc_dev, "elanpexbus", NULL, NULL);
|
||||
/* Attach GPIO framework */
|
||||
config_found_ia(&sc->sc_dev, "gpiobus", &gba, gpiobus_print);
|
||||
#endif /* NGPIO */
|
||||
@ -434,6 +1126,28 @@ elansc_attach(device_t parent, device_t self, void *aux)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
elanpex_match(device_t parent, struct cfdata *match, void *aux)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(parent);
|
||||
|
||||
return sc->sc_pex == NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
elanpar_match(device_t parent, struct cfdata *match, void *aux)
|
||||
{
|
||||
struct elansc_softc *sc = device_private(parent);
|
||||
|
||||
return sc->sc_par == NULL;
|
||||
}
|
||||
|
||||
CFATTACH_DECL_NEW(elanpar, sizeof(struct device),
|
||||
elanpar_match, elanpar_attach, elanpar_detach, NULL);
|
||||
|
||||
CFATTACH_DECL_NEW(elanpex, sizeof(struct device),
|
||||
elanpex_match, elanpex_attach, elanpex_detach, NULL);
|
||||
|
||||
CFATTACH_DECL2(elansc, sizeof(struct elansc_softc),
|
||||
elansc_match, elansc_attach, elansc_detach, NULL, NULL,
|
||||
elansc_childdetached);
|
||||
|
Loading…
Reference in New Issue
Block a user