/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* Machine-independent LAMEbus code.31*/3233#include <types.h>34#include <lib.h>35#include <cpu.h>36#include <spinlock.h>37#include <current.h>38#include <lamebus/lamebus.h>3940/* Register offsets within each config region */41#define CFGREG_VID 0 /* Vendor ID */42#define CFGREG_DID 4 /* Device ID */43#define CFGREG_DRL 8 /* Device Revision Level */4445/* LAMEbus controller private registers (offsets within its config region) */46#define CTLREG_RAMSZ 0x20047#define CTLREG_IRQS 0x20448#define CTLREG_PWR 0x20849#define CTLREG_IRQE 0x20c50#define CTLREG_CPUS 0x21051#define CTLREG_CPUE 0x21452#define CTLREG_SELF 0x2185354/* LAMEbus CPU control registers (offsets within each per-cpu region) */55#define CTLCPU_CIRQE 0x00056#define CTLCPU_CIPI 0x00457#define CTLCPU_CRAM 0x300585960/*61* Read a config register for the given slot.62*/63static64inline65uint32_t66read_cfg_register(struct lamebus_softc *lb, int slot, uint32_t offset)67{68/* Note that lb might be NULL on some platforms in some contexts. */69offset += LB_CONFIG_SIZE*slot;70return lamebus_read_register(lb, LB_CONTROLLER_SLOT, offset);71}7273/*74* Write a config register for a given slot.75*/76static77inline78void79write_cfg_register(struct lamebus_softc *lb, int slot, uint32_t offset,80uint32_t val)81{82offset += LB_CONFIG_SIZE*slot;83lamebus_write_register(lb, LB_CONTROLLER_SLOT, offset, val);84}8586/*87* Read one of the bus controller's registers.88*/89static90inline91uint32_t92read_ctl_register(struct lamebus_softc *lb, uint32_t offset)93{94/* Note that lb might be NULL on some platforms in some contexts. */95return read_cfg_register(lb, LB_CONTROLLER_SLOT, offset);96}9798/*99* Write one of the bus controller's registers.100*/101static102inline103void104write_ctl_register(struct lamebus_softc *lb, uint32_t offset, uint32_t val)105{106write_cfg_register(lb, LB_CONTROLLER_SLOT, offset, val);107}108109/*110* Write one of the bus controller's CPU control registers.111*/112static113inline114void115write_ctlcpu_register(struct lamebus_softc *lb, unsigned hw_cpunum,116uint32_t offset, uint32_t val)117{118offset += LB_CTLCPU_OFFSET + hw_cpunum * LB_CTLCPU_SIZE;119lamebus_write_register(lb, LB_CONTROLLER_SLOT, offset, val);120}121122/*123* Find and create secondary CPUs.124*/125void126lamebus_find_cpus(struct lamebus_softc *lamebus)127{128uint32_t cpumask, self, bit, val;129unsigned i, numcpus, bootcpu;130unsigned hwnum[32];131132cpumask = read_ctl_register(lamebus, CTLREG_CPUS);133self = read_ctl_register(lamebus, CTLREG_SELF);134135numcpus = 0;136bootcpu = 0;137for (i=0; i<32; i++) {138bit = (uint32_t)1 << i;139if ((cpumask & bit) != 0) {140if (self & bit) {141bootcpu = numcpus;142curcpu->c_hardware_number = i;143}144hwnum[numcpus] = i;145numcpus++;146}147}148149for (i=0; i<numcpus; i++) {150if (i != bootcpu) {151cpu_create(hwnum[i]);152}153}154155/*156* By default, route all interrupts only to the boot cpu. We157* could be arbitrarily more elaborate, up to things like158* dynamic load balancing.159*/160161for (i=0; i<numcpus; i++) {162if (i != bootcpu) {163val = 0;164}165else {166val = 0xffffffff;167}168write_ctlcpu_register(lamebus, hwnum[i], CTLCPU_CIRQE, val);169}170}171172/*173* Start up secondary CPUs.174*175* The first word of the CRAM area is set to the entry point for new176* CPUs; the second to the (software) CPU number. Note that the logic177* here assumes the boot CPU is CPU 0 and the others are 1-N as178* created in the function above. This is fine if all CPUs are on179* LAMEbus; if in some environment there are other CPUs about as well180* this logic will have to be made more complex.181*/182void183lamebus_start_cpus(struct lamebus_softc *lamebus)184{185uint32_t cpumask, self, bit;186uint32_t ctlcpuoffset;187uint32_t *cram;188unsigned i;189unsigned cpunum;190191cpumask = read_ctl_register(lamebus, CTLREG_CPUS);192self = read_ctl_register(lamebus, CTLREG_SELF);193194/* Poke in the startup address. */195cpunum = 1;196for (i=0; i<32; i++) {197bit = (uint32_t)1 << i;198if ((cpumask & bit) != 0) {199if (self & bit) {200continue;201}202ctlcpuoffset = LB_CTLCPU_OFFSET + i * LB_CTLCPU_SIZE;203cram = lamebus_map_area(lamebus,204LB_CONTROLLER_SLOT,205ctlcpuoffset + CTLCPU_CRAM);206cram[0] = (uint32_t)cpu_start_secondary;207cram[1] = cpunum++;208}209}210211/* Now, enable them all. */212write_ctl_register(lamebus, CTLREG_CPUE, cpumask);213}214215/*216* Probe function.217*218* Given a LAMEbus, look for a device that's not already been marked219* in use, has the specified IDs, and has a device revision level in220* the specified range (which is inclusive on both ends.)221*222* Returns the slot number found (0-31) or -1 if nothing suitable was223* found.224*/225226int227lamebus_probe(struct lamebus_softc *sc,228uint32_t vendorid, uint32_t deviceid,229uint32_t lowver, uint32_t highver)230{231int slot;232uint32_t val;233234/*235* Because the slot information in sc is used when dispatching236* interrupts, disable interrupts while working with it.237*/238239spinlock_acquire(&sc->ls_lock);240241for (slot=0; slot<LB_NSLOTS; slot++) {242if (sc->ls_slotsinuse & (1<<slot)) {243/* Slot already in use; skip */244continue;245}246247val = read_cfg_register(sc, slot, CFGREG_VID);248if (val!=vendorid) {249/* Wrong vendor id */250continue;251}252253val = read_cfg_register(sc, slot, CFGREG_DID);254if (val != deviceid) {255/* Wrong device id */256continue;257}258259val = read_cfg_register(sc, slot, CFGREG_DRL);260if (val < lowver || val > highver) {261/* Unsupported device revision */262continue;263}264265/* Found something */266267spinlock_release(&sc->ls_lock);268return slot;269}270271/* Found nothing */272273spinlock_release(&sc->ls_lock);274return -1;275}276277/*278* Mark that a slot is in use.279* This prevents the probe routine from returning the same device over280* and over again.281*/282void283lamebus_mark(struct lamebus_softc *sc, int slot)284{285uint32_t mask = ((uint32_t)1) << slot;286KASSERT(slot>=0 && slot < LB_NSLOTS);287288spinlock_acquire(&sc->ls_lock);289290if ((sc->ls_slotsinuse & mask)!=0) {291panic("lamebus_mark: slot %d already in use\n", slot);292}293294sc->ls_slotsinuse |= mask;295296spinlock_release(&sc->ls_lock);297}298299/*300* Mark that a slot is no longer in use.301*/302void303lamebus_unmark(struct lamebus_softc *sc, int slot)304{305uint32_t mask = ((uint32_t)1) << slot;306KASSERT(slot>=0 && slot < LB_NSLOTS);307308spinlock_acquire(&sc->ls_lock);309310if ((sc->ls_slotsinuse & mask)==0) {311panic("lamebus_mark: slot %d not marked in use\n", slot);312}313314sc->ls_slotsinuse &= ~mask;315316spinlock_release(&sc->ls_lock);317}318319/*320* Register a function (and a device context pointer) to be called321* when a particular slot signals an interrupt.322*/323void324lamebus_attach_interrupt(struct lamebus_softc *sc, int slot,325void *devdata,326void (*irqfunc)(void *devdata))327{328uint32_t mask = ((uint32_t)1) << slot;329KASSERT(slot>=0 && slot < LB_NSLOTS);330331spinlock_acquire(&sc->ls_lock);332333if ((sc->ls_slotsinuse & mask)==0) {334panic("lamebus_attach_interrupt: slot %d not marked in use\n",335slot);336}337338KASSERT(sc->ls_devdata[slot]==NULL);339KASSERT(sc->ls_irqfuncs[slot]==NULL);340341sc->ls_devdata[slot] = devdata;342sc->ls_irqfuncs[slot] = irqfunc;343344spinlock_release(&sc->ls_lock);345}346347/*348* Unregister a function that was being called when a particular slot349* signaled an interrupt.350*/351void352lamebus_detach_interrupt(struct lamebus_softc *sc, int slot)353{354uint32_t mask = ((uint32_t)1) << slot;355KASSERT(slot>=0 && slot < LB_NSLOTS);356357spinlock_acquire(&sc->ls_lock);358359if ((sc->ls_slotsinuse & mask)==0) {360panic("lamebus_detach_interrupt: slot %d not marked in use\n",361slot);362}363364KASSERT(sc->ls_irqfuncs[slot]!=NULL);365366sc->ls_devdata[slot] = NULL;367sc->ls_irqfuncs[slot] = NULL;368369spinlock_release(&sc->ls_lock);370}371372/*373* Mask/unmask an interrupt using the global IRQE register.374*/375void376lamebus_mask_interrupt(struct lamebus_softc *lamebus, int slot)377{378uint32_t bits, mask = ((uint32_t)1) << slot;379KASSERT(slot >= 0 && slot < LB_NSLOTS);380381spinlock_acquire(&lamebus->ls_lock);382bits = read_ctl_register(lamebus, CTLREG_IRQE);383bits &= ~mask;384write_ctl_register(lamebus, CTLREG_IRQE, bits);385spinlock_release(&lamebus->ls_lock);386}387388void389lamebus_unmask_interrupt(struct lamebus_softc *lamebus, int slot)390{391uint32_t bits, mask = ((uint32_t)1) << slot;392KASSERT(slot >= 0 && slot < LB_NSLOTS);393394spinlock_acquire(&lamebus->ls_lock);395bits = read_ctl_register(lamebus, CTLREG_IRQE);396bits |= mask;397write_ctl_register(lamebus, CTLREG_IRQE, bits);398spinlock_release(&lamebus->ls_lock);399}400401402/*403* LAMEbus interrupt handling function. (Machine-independent!)404*/405void406lamebus_interrupt(struct lamebus_softc *lamebus)407{408/*409* Note that despite the fact that "spl" stands for "set410* priority level", we don't actually support interrupt411* priorities. When an interrupt happens, we look through the412* slots to find the first interrupting device and call its413* interrupt routine, no matter what that device is.414*415* Note that the entire LAMEbus uses only one on-cpu interrupt line.416* Thus, we do not use any on-cpu interrupt priority system either.417*/418419int slot;420uint32_t mask;421uint32_t irqs;422void (*handler)(void *);423void *data;424425/* For keeping track of how many bogus things happen in a row. */426static int duds = 0;427int duds_this_time = 0;428429/* and we better have a valid bus instance. */430KASSERT(lamebus != NULL);431432/* Lock the softc */433spinlock_acquire(&lamebus->ls_lock);434435/*436* Read the LAMEbus controller register that tells us which437* slots are asserting an interrupt condition.438*/439irqs = read_ctl_register(lamebus, CTLREG_IRQS);440441if (irqs == 0) {442/*443* Huh? None of them? Must be a glitch.444*/445kprintf("lamebus: stray interrupt on cpu %u\n",446curcpu->c_number);447duds++;448duds_this_time++;449450/*451* We could just return now, but instead we'll452* continue ahead. Because irqs == 0, nothing in the453* loop will execute, and passing through it gets us454* to the code that checks how many duds we've455* seen. This is important, because we just might get456* a stray interrupt that latches itself on. If that457* happens, we're pretty much toast, but it's better458* to panic and hopefully reset the system than to459* loop forever printing "stray interrupt".460*/461}462463/*464* Go through the bits in the value we got back to see which465* ones are set.466*/467468for (mask=1, slot=0; slot<LB_NSLOTS; mask<<=1, slot++) {469if ((irqs & mask) == 0) {470/* Nope. */471continue;472}473474/*475* This slot is signalling an interrupt.476*/477478if ((lamebus->ls_slotsinuse & mask)==0) {479/*480* No device driver is using this slot.481*/482duds++;483duds_this_time++;484continue;485}486487if (lamebus->ls_irqfuncs[slot]==NULL) {488/*489* The device driver hasn't installed an interrupt490* handler.491*/492duds++;493duds_this_time++;494continue;495}496497/*498* Call the interrupt handler. Release the spinlock499* while we do so, in case other CPUs are handling500* interrupts on other devices.501*/502handler = lamebus->ls_irqfuncs[slot];503data = lamebus->ls_devdata[slot];504spinlock_release(&lamebus->ls_lock);505506handler(data);507508spinlock_acquire(&lamebus->ls_lock);509510/*511* Reload the mask of pending IRQs - if we just called512* hardclock, we might not have come back to this513* context for some time, and it might have changed.514*/515516irqs = read_ctl_register(lamebus, CTLREG_IRQS);517}518519520/*521* If we get interrupts for a slot with no driver or no522* interrupt handler, it's fairly serious. Because LAMEbus523* uses level-triggered interrupts, if we don't shut off the524* condition, we'll keep getting interrupted continuously and525* the system will make no progress. But we don't know how to526* do that if there's no driver or no interrupt handler.527*528* So, if we get too many dud interrupts, panic, since it's529* better to panic and reset than to hang.530*531* If we get through here without seeing any duds this time,532* the condition, whatever it was, has gone away. It might be533* some stupid device we don't have a driver for, or it might534* have been an electrical transient. In any case, warn and535* clear the dud count.536*/537538if (duds_this_time == 0 && duds > 0) {539kprintf("lamebus: %d dud interrupts\n", duds);540duds = 0;541}542543if (duds > 10000) {544panic("lamebus: too many (%d) dud interrupts\n", duds);545}546547/* Unlock the softc */548spinlock_release(&lamebus->ls_lock);549}550551/*552* Have the bus controller power the system off.553*/554void555lamebus_poweroff(struct lamebus_softc *lamebus)556{557/*558* Write 0 to the power register to shut the system off.559*/560561cpu_irqoff();562write_ctl_register(lamebus, CTLREG_PWR, 0);563564/* The power doesn't go off instantly... so halt the cpu. */565cpu_halt();566}567568/*569* Ask the bus controller how much memory we have.570*/571uint32_t572lamebus_ramsize(void)573{574/*575* Note that this has to work before bus initialization.576* On machines where lamebus_read_register doesn't work577* before bus initialization, this function can't be used578* for initial RAM size lookup.579*/580581return read_ctl_register(NULL, CTLREG_RAMSZ);582}583584/*585* Turn on or off the interprocessor interrupt line for a given CPU.586*/587void588lamebus_assert_ipi(struct lamebus_softc *lamebus, struct cpu *target)589{590write_ctlcpu_register(lamebus, target->c_hardware_number,591CTLCPU_CIPI, 1);592}593594void595lamebus_clear_ipi(struct lamebus_softc *lamebus, struct cpu *target)596{597write_ctlcpu_register(lamebus, target->c_hardware_number,598CTLCPU_CIPI, 0);599}600601/*602* Initial setup.603* Should be called from mainbus_bootstrap().604*/605struct lamebus_softc *606lamebus_init(void)607{608struct lamebus_softc *lamebus;609int i;610611/* Allocate space for lamebus data */612lamebus = kmalloc(sizeof(struct lamebus_softc));613if (lamebus==NULL) {614panic("lamebus_init: Out of memory\n");615}616617spinlock_init(&lamebus->ls_lock);618619/*620* Initialize the LAMEbus data structure.621*/622lamebus->ls_slotsinuse = 1 << LB_CONTROLLER_SLOT;623624for (i=0; i<LB_NSLOTS; i++) {625lamebus->ls_devdata[i] = NULL;626lamebus->ls_irqfuncs[i] = NULL;627}628629return lamebus;630}631632633