diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2009-04-12 17:56:59 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2009-04-12 17:56:59 +0000 |
commit | aa9adddfca7778c5667f7860a75baa5bb033974b (patch) | |
tree | 7dec8c6dffa27bc6479cb6a3e68ebadaff05e334 /sys/arch/sgi/pci/ioc.c | |
parent | 2829c2629cd9ab9ba5ca8dd4c7a45d3e3755098e (diff) |
Interrupt support for IOC3 and its subdevices (currently limited to the
SuperIO part, the Ethernet part needs a whole driver); kernel now boot
single user (or bsd.rd). Joint work with jsing@
Diffstat (limited to 'sys/arch/sgi/pci/ioc.c')
-rw-r--r-- | sys/arch/sgi/pci/ioc.c | 227 |
1 files changed, 217 insertions, 10 deletions
diff --git a/sys/arch/sgi/pci/ioc.c b/sys/arch/sgi/pci/ioc.c index b02d4abb9ea..e00a3bc59d0 100644 --- a/sys/arch/sgi/pci/ioc.c +++ b/sys/arch/sgi/pci/ioc.c @@ -1,8 +1,8 @@ -/* $OpenBSD: ioc.c,v 1.2 2009/03/29 21:53:52 sthen Exp $ */ +/* $OpenBSD: ioc.c,v 1.3 2009/04/12 17:56:58 miod Exp $ */ /* * Copyright (c) 2008 Joel Sing. - * Copyright (c) 2008 Miodrag Vallat. + * Copyright (c) 2008, 2009 Miodrag Vallat. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -41,6 +41,7 @@ #include <dev/onewire/onewirereg.h> #include <dev/onewire/onewirevar.h> +#include <sgi/dev/if_efreg.h> #include <sgi/dev/owmacvar.h> #include <sgi/xbow/xbow.h> @@ -51,6 +52,16 @@ int ioc_search_onewire(struct device *, void *, void *); int ioc_search_mundane(struct device *, void *, void *); int ioc_print(void *, const char *); +struct ioc_intr { + struct ioc_softc *ii_ioc; + + int (*ii_func)(void *); + void *ii_arg; + + struct evcount ii_count; + int ii_level; +}; + struct ioc_softc { struct device sc_dev; @@ -59,6 +70,11 @@ struct ioc_softc { bus_space_tag_t sc_memt; bus_space_handle_t sc_memh; bus_dma_tag_t sc_dmat; + pci_chipset_tag_t sc_pc; + + void *sc_sih; /* SuperIO interrupt */ + void *sc_eih; /* Ethernet interrupt */ + struct ioc_intr *sc_intr[IOC_NDEVS]; struct onewire_bus sc_bus; @@ -73,6 +89,10 @@ struct cfdriver ioc_cd = { NULL, "ioc", DV_DULL, }; +void ioc_intr_dispatch(struct ioc_softc *, int); +int ioc_intr_ethernet(void *); +int ioc_intr_superio(void *); + int iocow_reset(void *); int iocow_read_bit(struct ioc_softc *); int iocow_send_bit(void *, int); @@ -102,8 +122,8 @@ ioc_print(void *aux, const char *iocname) if (iaa->iaa_base != 0) printf(" base 0x%08x", iaa->iaa_base); - if (iaa->iaa_intr != 0) - printf(" irq %d", iaa->iaa_intr); + if (iaa->iaa_dev != 0) + printf(" dev %d", iaa->iaa_dev); return (UNCONF); } @@ -113,9 +133,12 @@ ioc_attach(struct device *parent, struct device *self, void *aux) { struct ioc_softc *sc = (struct ioc_softc *)self; struct pci_attach_args *pa = aux; + pci_intr_handle_t sih, eih; bus_space_tag_t memt; bus_space_handle_t memh; bus_size_t memsize; + uint32_t data; + int dev; if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 0, &memt, &memh, NULL, &memsize, 0)) { @@ -123,9 +146,66 @@ ioc_attach(struct device *parent, struct device *self, void *aux) return; } + sc->sc_pc = pa->pa_pc; sc->sc_dmat = pa->pa_dmat; /* + * Initialise IOC3 ASIC. + */ + data = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + data |= PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_PARITY_ENABLE | + PCI_COMMAND_SERR_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, data); + + /* + * IOC3 is not a real PCI device - it's a poor wrapper over a set + * of convenience chips. And it actually needs to use two interrupts, + * one for the superio chip, and the other for the Ethernet chip. + * + * Since our pci layer doesn't handle this, we cheat and compute + * the superio interrupt cookie ourselves. This is ugly, and + * depends on xbridge knowledge. + * + * (What the above means is that you should wear peril-sensitive + * sunglasses from now on). + * + * To make things ever worse, some IOC3 boards (real boards, not + * on-board components) lack the Ethernet component. We should + * eventually handle them there, but it's not worth doing yet... + * (and we'll need to parse the ownum serial numbers to know + * this anyway) + */ + + if (pci_intr_map(pa, &eih) != 0) { + printf(": failed to map interrupt!\n"); + goto unmap; + } + sih = eih + 2; /* XXX ACK GAG BARF */ + + /* + * Register the superio interrupt. + */ + sc->sc_sih = pci_intr_establish(sc->sc_pc, sih, IPL_TTY, + ioc_intr_superio, sc, self->dv_xname); + if (sc->sc_sih == NULL) { + printf(": failed to establish superio interrupt!\n"); + goto unmap; + } + + /* + * Register the ethernet interrupt. + */ + sc->sc_eih = pci_intr_establish(sc->sc_pc, eih, IPL_NET, + ioc_intr_ethernet, sc, self->dv_xname); + if (sc->sc_eih == NULL) { + printf(": failed to establish ethernet interrupt!\n"); + goto unregister; + } + + printf(": superio %s", pci_intr_string(sc->sc_pc, sih)); + printf(", ethernet %s", pci_intr_string(sc->sc_pc, eih)); + + /* * Build a suitable bus_space_handle by rebasing the xbridge * inherited one to our BAR, and restoring the original * non-swapped subword access methods. @@ -137,9 +217,8 @@ ioc_attach(struct device *parent, struct device *self, void *aux) sc->sc_mem_bus_space = malloc(sizeof (*sc->sc_mem_bus_space), M_DEVBUF, M_NOWAIT); if (sc->sc_mem_bus_space == NULL) { - bus_space_unmap(memt, memh, memsize); printf(": can't allocate bus_space\n"); - return; + goto unregister2; } bcopy(memt, sc->sc_mem_bus_space, sizeof(*sc->sc_mem_bus_space)); @@ -152,7 +231,17 @@ ioc_attach(struct device *parent, struct device *self, void *aux) sc->sc_memt = sc->sc_mem_bus_space; sc->sc_memh = memh; - printf("\n"); + /* Initialise interrupt handling structures. */ + for (dev = 0; dev < IOC_NDEVS; dev++) + sc->sc_intr[dev] = NULL; + + /* + * Acknowledge all pending interrupts, and disable them. + */ + bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IEC, ~0x0); + bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES, 0x0); + bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR, + bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR)); /* * Attach the 1-Wire interface first, other sub-devices may @@ -164,6 +253,15 @@ ioc_attach(struct device *parent, struct device *self, void *aux) * Attach other sub-devices. */ config_search(ioc_search_mundane, self, aux); + + return; + +unregister2: + pci_intr_disestablish(sc->sc_pc, sc->sc_eih); +unregister: + pci_intr_disestablish(sc->sc_pc, sc->sc_sih); +unmap: + bus_space_unmap(memt, memh, memsize); } int @@ -185,9 +283,9 @@ ioc_search_mundane(struct device *parent, void *vcf, void *args) else iaa.iaa_base = cf->cf_loc[0]; if (cf->cf_loc[1] == -1) - iaa.iaa_intr = 0; + iaa.iaa_dev = 0; else - iaa.iaa_intr = cf->cf_loc[1]; + iaa.iaa_dev = cf->cf_loc[1]; if (sc->sc_owmac != NULL) memcpy(iaa.iaa_enaddr, sc->sc_owmac->sc_enaddr, 6); @@ -231,7 +329,7 @@ ioc_search_onewire(struct device *parent, void *vcf, void *args) oba.oba_bus = &sc->sc_bus; oba.oba_flags = ONEWIRE_SCAN_NOW | ONEWIRE_NO_PERIODIC_SCAN; - /* in case onewire is disabled in UKC */ + /* In case onewire is disabled in UKC... */ if ((*cf->cf_attach->ca_match)(parent, cf, &oba) == 0) return 0; @@ -333,3 +431,112 @@ iocow_pulse(struct ioc_softc *sc, int pulse, int data) return (mcr_value & 1); } + +/* + * Interrupt handling. + */ + +/* + * List of interrupt bits to enable for each device. + * + * We intentionnaly mask the RX high water bits, as they apparently never + * clear on some machines regardless of what we do. + */ +const uint32_t ioc_intrbits[IOC_NDEVS] = { + 0x000001ff & ~0x00000004, /* serial A */ + 0x0003fe00 & ~0x00000800, /* serial B */ + 0x003c0000, /* parallel port */ + 0x00400000, /* PS/2 port */ + 0x08000000, /* rtc */ + 0x00000000 /* Ethernet (handled differently) */ +}; + +void * +ioc_intr_establish(void *cookie, u_long dev, int level, int (*func)(void *), + void *arg, char *name) +{ + struct ioc_softc *sc = cookie; + struct ioc_intr *ii; + + dev--; + if (dev < 0 || dev >= IOC_NDEVS) + return NULL; + + ii = (struct ioc_intr *)malloc(sizeof(*ii), M_DEVBUF, M_NOWAIT); + if (ii == NULL) + return NULL; + + ii->ii_ioc = sc; + ii->ii_func = func; + ii->ii_arg = arg; + ii->ii_level = level; + + evcount_attach(&ii->ii_count, name, &ii->ii_level, &evcount_intr); + sc->sc_intr[dev] = ii; + + /* enable hardware source if necessary */ + bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES, + ioc_intrbits[dev]); + + return (ii); +} + +int +ioc_intr_superio(void *v) +{ + struct ioc_softc *sc = (struct ioc_softc *)v; + uint32_t pending; + int dev; + + pending = bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IR) & + bus_space_read_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IES); + + if (pending == 0) + return 0; + + /* Disable pending interrupts */ + bus_space_write_4(sc->sc_memt, sc->sc_memh, IOC3_SIO_IEC, pending); + + for (dev = 0; dev < IOC_NDEVS - 1 /* skip Ethernet */; dev++) { + if (pending & ioc_intrbits[dev]) { + ioc_intr_dispatch(sc, dev); + + /* Ack, then reenable, pending interrupts */ + bus_space_write_4(sc->sc_memt, sc->sc_memh, + IOC3_SIO_IR, pending & ioc_intrbits[dev]); + bus_space_write_4(sc->sc_memt, sc->sc_memh, + IOC3_SIO_IES, pending & ioc_intrbits[dev]); + } + } + + return 1; +} + +int +ioc_intr_ethernet(void *v) +{ + struct ioc_softc *sc = (struct ioc_softc *)v; + uint32_t stat; + + stat = bus_space_read_4(sc->sc_memt, sc->sc_memh, EF_INTR_STATUS); + + if (stat == 0) + return 0; + + ioc_intr_dispatch(sc, IOCDEV_EF); + bus_space_write_4(sc->sc_memt, sc->sc_memh, EF_INTR_STATUS, stat); + + return 1; +} + +void +ioc_intr_dispatch(struct ioc_softc *sc, int dev) +{ + struct ioc_intr *ii; + + /* Call registered interrupt function. */ + if ((ii = sc->sc_intr[dev]) != NULL && ii->ii_func != NULL) { + if ((*ii->ii_func)(ii->ii_arg) != 0) + ii->ii_count.ec_count++; + } +} |