123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- /*
- * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
- * The President and Fellows of Harvard College.
- *
- * 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.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY 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 UNIVERSITY 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.
- */
- /*
- * Machine-independent LAMEbus code.
- */
- #include <types.h>
- #include <lib.h>
- #include <cpu.h>
- #include <spinlock.h>
- #include <current.h>
- #include <lamebus/lamebus.h>
- /* Register offsets within each config region */
- #define CFGREG_VID 0 /* Vendor ID */
- #define CFGREG_DID 4 /* Device ID */
- #define CFGREG_DRL 8 /* Device Revision Level */
- /* LAMEbus controller private registers (offsets within its config region) */
- #define CTLREG_RAMSZ 0x200
- #define CTLREG_IRQS 0x204
- #define CTLREG_PWR 0x208
- #define CTLREG_IRQE 0x20c
- #define CTLREG_CPUS 0x210
- #define CTLREG_CPUE 0x214
- #define CTLREG_SELF 0x218
- /* LAMEbus CPU control registers (offsets within each per-cpu region) */
- #define CTLCPU_CIRQE 0x000
- #define CTLCPU_CIPI 0x004
- #define CTLCPU_CRAM 0x300
- /*
- * Read a config register for the given slot.
- */
- static
- inline
- uint32_t
- read_cfg_register(struct lamebus_softc *lb, int slot, uint32_t offset)
- {
- /* Note that lb might be NULL on some platforms in some contexts. */
- offset += LB_CONFIG_SIZE*slot;
- return lamebus_read_register(lb, LB_CONTROLLER_SLOT, offset);
- }
- /*
- * Write a config register for a given slot.
- */
- static
- inline
- void
- write_cfg_register(struct lamebus_softc *lb, int slot, uint32_t offset,
- uint32_t val)
- {
- offset += LB_CONFIG_SIZE*slot;
- lamebus_write_register(lb, LB_CONTROLLER_SLOT, offset, val);
- }
- /*
- * Read one of the bus controller's registers.
- */
- static
- inline
- uint32_t
- read_ctl_register(struct lamebus_softc *lb, uint32_t offset)
- {
- /* Note that lb might be NULL on some platforms in some contexts. */
- return read_cfg_register(lb, LB_CONTROLLER_SLOT, offset);
- }
- /*
- * Write one of the bus controller's registers.
- */
- static
- inline
- void
- write_ctl_register(struct lamebus_softc *lb, uint32_t offset, uint32_t val)
- {
- write_cfg_register(lb, LB_CONTROLLER_SLOT, offset, val);
- }
- /*
- * Write one of the bus controller's CPU control registers.
- */
- static
- inline
- void
- write_ctlcpu_register(struct lamebus_softc *lb, unsigned hw_cpunum,
- uint32_t offset, uint32_t val)
- {
- offset += LB_CTLCPU_OFFSET + hw_cpunum * LB_CTLCPU_SIZE;
- lamebus_write_register(lb, LB_CONTROLLER_SLOT, offset, val);
- }
- /*
- * Find and create secondary CPUs.
- */
- void
- lamebus_find_cpus(struct lamebus_softc *lamebus)
- {
- uint32_t cpumask, self, bit, val;
- unsigned i, numcpus, bootcpu;
- unsigned hwnum[32];
- cpumask = read_ctl_register(lamebus, CTLREG_CPUS);
- self = read_ctl_register(lamebus, CTLREG_SELF);
- numcpus = 0;
- bootcpu = 0;
- for (i=0; i<32; i++) {
- bit = (uint32_t)1 << i;
- if ((cpumask & bit) != 0) {
- if (self & bit) {
- bootcpu = numcpus;
- curcpu->c_hardware_number = i;
- }
- hwnum[numcpus] = i;
- numcpus++;
- }
- }
- for (i=0; i<numcpus; i++) {
- if (i != bootcpu) {
- cpu_create(hwnum[i]);
- }
- }
- /*
- * By default, route all interrupts only to the boot cpu. We
- * could be arbitrarily more elaborate, up to things like
- * dynamic load balancing.
- */
- for (i=0; i<numcpus; i++) {
- if (i != bootcpu) {
- val = 0;
- }
- else {
- val = 0xffffffff;
- }
- write_ctlcpu_register(lamebus, hwnum[i], CTLCPU_CIRQE, val);
- }
- }
- /*
- * Start up secondary CPUs.
- *
- * The first word of the CRAM area is set to the entry point for new
- * CPUs; the second to the (software) CPU number. Note that the logic
- * here assumes the boot CPU is CPU 0 and the others are 1-N as
- * created in the function above. This is fine if all CPUs are on
- * LAMEbus; if in some environment there are other CPUs about as well
- * this logic will have to be made more complex.
- */
- void
- lamebus_start_cpus(struct lamebus_softc *lamebus)
- {
- uint32_t cpumask, self, bit;
- uint32_t ctlcpuoffset;
- uint32_t *cram;
- unsigned i;
- unsigned cpunum;
- cpumask = read_ctl_register(lamebus, CTLREG_CPUS);
- self = read_ctl_register(lamebus, CTLREG_SELF);
- /* Poke in the startup address. */
- cpunum = 1;
- for (i=0; i<32; i++) {
- bit = (uint32_t)1 << i;
- if ((cpumask & bit) != 0) {
- if (self & bit) {
- continue;
- }
- ctlcpuoffset = LB_CTLCPU_OFFSET + i * LB_CTLCPU_SIZE;
- cram = lamebus_map_area(lamebus,
- LB_CONTROLLER_SLOT,
- ctlcpuoffset + CTLCPU_CRAM);
- cram[0] = (uint32_t)cpu_start_secondary;
- cram[1] = cpunum++;
- }
- }
- /* Now, enable them all. */
- write_ctl_register(lamebus, CTLREG_CPUE, cpumask);
- }
- /*
- * Probe function.
- *
- * Given a LAMEbus, look for a device that's not already been marked
- * in use, has the specified IDs, and has a device revision level in
- * the specified range (which is inclusive on both ends.)
- *
- * Returns the slot number found (0-31) or -1 if nothing suitable was
- * found.
- */
- int
- lamebus_probe(struct lamebus_softc *sc,
- uint32_t vendorid, uint32_t deviceid,
- uint32_t lowver, uint32_t highver)
- {
- int slot;
- uint32_t val;
- /*
- * Because the slot information in sc is used when dispatching
- * interrupts, disable interrupts while working with it.
- */
- spinlock_acquire(&sc->ls_lock);
- for (slot=0; slot<LB_NSLOTS; slot++) {
- if (sc->ls_slotsinuse & (1<<slot)) {
- /* Slot already in use; skip */
- continue;
- }
- val = read_cfg_register(sc, slot, CFGREG_VID);
- if (val!=vendorid) {
- /* Wrong vendor id */
- continue;
- }
- val = read_cfg_register(sc, slot, CFGREG_DID);
- if (val != deviceid) {
- /* Wrong device id */
- continue;
- }
- val = read_cfg_register(sc, slot, CFGREG_DRL);
- if (val < lowver || val > highver) {
- /* Unsupported device revision */
- continue;
- }
- /* Found something */
- spinlock_release(&sc->ls_lock);
- return slot;
- }
- /* Found nothing */
- spinlock_release(&sc->ls_lock);
- return -1;
- }
- /*
- * Mark that a slot is in use.
- * This prevents the probe routine from returning the same device over
- * and over again.
- */
- void
- lamebus_mark(struct lamebus_softc *sc, int slot)
- {
- uint32_t mask = ((uint32_t)1) << slot;
- KASSERT(slot>=0 && slot < LB_NSLOTS);
- spinlock_acquire(&sc->ls_lock);
- if ((sc->ls_slotsinuse & mask)!=0) {
- panic("lamebus_mark: slot %d already in use\n", slot);
- }
- sc->ls_slotsinuse |= mask;
- spinlock_release(&sc->ls_lock);
- }
- /*
- * Mark that a slot is no longer in use.
- */
- void
- lamebus_unmark(struct lamebus_softc *sc, int slot)
- {
- uint32_t mask = ((uint32_t)1) << slot;
- KASSERT(slot>=0 && slot < LB_NSLOTS);
- spinlock_acquire(&sc->ls_lock);
- if ((sc->ls_slotsinuse & mask)==0) {
- panic("lamebus_mark: slot %d not marked in use\n", slot);
- }
- sc->ls_slotsinuse &= ~mask;
- spinlock_release(&sc->ls_lock);
- }
- /*
- * Register a function (and a device context pointer) to be called
- * when a particular slot signals an interrupt.
- */
- void
- lamebus_attach_interrupt(struct lamebus_softc *sc, int slot,
- void *devdata,
- void (*irqfunc)(void *devdata))
- {
- uint32_t mask = ((uint32_t)1) << slot;
- KASSERT(slot>=0 && slot < LB_NSLOTS);
- spinlock_acquire(&sc->ls_lock);
- if ((sc->ls_slotsinuse & mask)==0) {
- panic("lamebus_attach_interrupt: slot %d not marked in use\n",
- slot);
- }
- KASSERT(sc->ls_devdata[slot]==NULL);
- KASSERT(sc->ls_irqfuncs[slot]==NULL);
- sc->ls_devdata[slot] = devdata;
- sc->ls_irqfuncs[slot] = irqfunc;
-
- spinlock_release(&sc->ls_lock);
- }
- /*
- * Unregister a function that was being called when a particular slot
- * signaled an interrupt.
- */
- void
- lamebus_detach_interrupt(struct lamebus_softc *sc, int slot)
- {
- uint32_t mask = ((uint32_t)1) << slot;
- KASSERT(slot>=0 && slot < LB_NSLOTS);
- spinlock_acquire(&sc->ls_lock);
- if ((sc->ls_slotsinuse & mask)==0) {
- panic("lamebus_detach_interrupt: slot %d not marked in use\n",
- slot);
- }
- KASSERT(sc->ls_irqfuncs[slot]!=NULL);
- sc->ls_devdata[slot] = NULL;
- sc->ls_irqfuncs[slot] = NULL;
-
- spinlock_release(&sc->ls_lock);
- }
- /*
- * Mask/unmask an interrupt using the global IRQE register.
- */
- void
- lamebus_mask_interrupt(struct lamebus_softc *lamebus, int slot)
- {
- uint32_t bits, mask = ((uint32_t)1) << slot;
- KASSERT(slot >= 0 && slot < LB_NSLOTS);
- spinlock_acquire(&lamebus->ls_lock);
- bits = read_ctl_register(lamebus, CTLREG_IRQE);
- bits &= ~mask;
- write_ctl_register(lamebus, CTLREG_IRQE, bits);
- spinlock_release(&lamebus->ls_lock);
- }
- void
- lamebus_unmask_interrupt(struct lamebus_softc *lamebus, int slot)
- {
- uint32_t bits, mask = ((uint32_t)1) << slot;
- KASSERT(slot >= 0 && slot < LB_NSLOTS);
- spinlock_acquire(&lamebus->ls_lock);
- bits = read_ctl_register(lamebus, CTLREG_IRQE);
- bits |= mask;
- write_ctl_register(lamebus, CTLREG_IRQE, bits);
- spinlock_release(&lamebus->ls_lock);
- }
- /*
- * LAMEbus interrupt handling function. (Machine-independent!)
- */
- void
- lamebus_interrupt(struct lamebus_softc *lamebus)
- {
- /*
- * Note that despite the fact that "spl" stands for "set
- * priority level", we don't actually support interrupt
- * priorities. When an interrupt happens, we look through the
- * slots to find the first interrupting device and call its
- * interrupt routine, no matter what that device is.
- *
- * Note that the entire LAMEbus uses only one on-cpu interrupt line.
- * Thus, we do not use any on-cpu interrupt priority system either.
- */
- int slot;
- uint32_t mask;
- uint32_t irqs;
- void (*handler)(void *);
- void *data;
- /* For keeping track of how many bogus things happen in a row. */
- static int duds = 0;
- int duds_this_time = 0;
- /* and we better have a valid bus instance. */
- KASSERT(lamebus != NULL);
- /* Lock the softc */
- spinlock_acquire(&lamebus->ls_lock);
- /*
- * Read the LAMEbus controller register that tells us which
- * slots are asserting an interrupt condition.
- */
- irqs = read_ctl_register(lamebus, CTLREG_IRQS);
- if (irqs == 0) {
- /*
- * Huh? None of them? Must be a glitch.
- */
- kprintf("lamebus: stray interrupt on cpu %u\n",
- curcpu->c_number);
- duds++;
- duds_this_time++;
- /*
- * We could just return now, but instead we'll
- * continue ahead. Because irqs == 0, nothing in the
- * loop will execute, and passing through it gets us
- * to the code that checks how many duds we've
- * seen. This is important, because we just might get
- * a stray interrupt that latches itself on. If that
- * happens, we're pretty much toast, but it's better
- * to panic and hopefully reset the system than to
- * loop forever printing "stray interrupt".
- */
- }
- /*
- * Go through the bits in the value we got back to see which
- * ones are set.
- */
- for (mask=1, slot=0; slot<LB_NSLOTS; mask<<=1, slot++) {
- if ((irqs & mask) == 0) {
- /* Nope. */
- continue;
- }
- /*
- * This slot is signalling an interrupt.
- */
-
- if ((lamebus->ls_slotsinuse & mask)==0) {
- /*
- * No device driver is using this slot.
- */
- duds++;
- duds_this_time++;
- continue;
- }
- if (lamebus->ls_irqfuncs[slot]==NULL) {
- /*
- * The device driver hasn't installed an interrupt
- * handler.
- */
- duds++;
- duds_this_time++;
- continue;
- }
- /*
- * Call the interrupt handler. Release the spinlock
- * while we do so, in case other CPUs are handling
- * interrupts on other devices.
- */
- handler = lamebus->ls_irqfuncs[slot];
- data = lamebus->ls_devdata[slot];
- spinlock_release(&lamebus->ls_lock);
- handler(data);
- spinlock_acquire(&lamebus->ls_lock);
- /*
- * Reload the mask of pending IRQs - if we just called
- * hardclock, we might not have come back to this
- * context for some time, and it might have changed.
- */
- irqs = read_ctl_register(lamebus, CTLREG_IRQS);
- }
- /*
- * If we get interrupts for a slot with no driver or no
- * interrupt handler, it's fairly serious. Because LAMEbus
- * uses level-triggered interrupts, if we don't shut off the
- * condition, we'll keep getting interrupted continuously and
- * the system will make no progress. But we don't know how to
- * do that if there's no driver or no interrupt handler.
- *
- * So, if we get too many dud interrupts, panic, since it's
- * better to panic and reset than to hang.
- *
- * If we get through here without seeing any duds this time,
- * the condition, whatever it was, has gone away. It might be
- * some stupid device we don't have a driver for, or it might
- * have been an electrical transient. In any case, warn and
- * clear the dud count.
- */
- if (duds_this_time == 0 && duds > 0) {
- kprintf("lamebus: %d dud interrupts\n", duds);
- duds = 0;
- }
- if (duds > 10000) {
- panic("lamebus: too many (%d) dud interrupts\n", duds);
- }
- /* Unlock the softc */
- spinlock_release(&lamebus->ls_lock);
- }
- /*
- * Have the bus controller power the system off.
- */
- void
- lamebus_poweroff(struct lamebus_softc *lamebus)
- {
- /*
- * Write 0 to the power register to shut the system off.
- */
- cpu_irqoff();
- write_ctl_register(lamebus, CTLREG_PWR, 0);
- /* The power doesn't go off instantly... so halt the cpu. */
- cpu_halt();
- }
- /*
- * Ask the bus controller how much memory we have.
- */
- uint32_t
- lamebus_ramsize(void)
- {
- /*
- * Note that this has to work before bus initialization.
- * On machines where lamebus_read_register doesn't work
- * before bus initialization, this function can't be used
- * for initial RAM size lookup.
- */
- return read_ctl_register(NULL, CTLREG_RAMSZ);
- }
- /*
- * Turn on or off the interprocessor interrupt line for a given CPU.
- */
- void
- lamebus_assert_ipi(struct lamebus_softc *lamebus, struct cpu *target)
- {
- write_ctlcpu_register(lamebus, target->c_hardware_number,
- CTLCPU_CIPI, 1);
- }
- void
- lamebus_clear_ipi(struct lamebus_softc *lamebus, struct cpu *target)
- {
- write_ctlcpu_register(lamebus, target->c_hardware_number,
- CTLCPU_CIPI, 0);
- }
- /*
- * Initial setup.
- * Should be called from mainbus_bootstrap().
- */
- struct lamebus_softc *
- lamebus_init(void)
- {
- struct lamebus_softc *lamebus;
- int i;
- /* Allocate space for lamebus data */
- lamebus = kmalloc(sizeof(struct lamebus_softc));
- if (lamebus==NULL) {
- panic("lamebus_init: Out of memory\n");
- }
- spinlock_init(&lamebus->ls_lock);
- /*
- * Initialize the LAMEbus data structure.
- */
- lamebus->ls_slotsinuse = 1 << LB_CONTROLLER_SLOT;
- for (i=0; i<LB_NSLOTS; i++) {
- lamebus->ls_devdata[i] = NULL;
- lamebus->ls_irqfuncs[i] = NULL;
- }
- return lamebus;
- }
|