/*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* Driver for LAMEbus clock/timer card31*/32#include <types.h>33#include <lib.h>34#include <spl.h>35#include <clock.h>36#include <platform/bus.h>37#include <lamebus/ltimer.h>38#include "autoconf.h"3940/* Registers (offsets within slot) */41#define LT_REG_SEC 0 /* time of day: seconds */42#define LT_REG_NSEC 4 /* time of day: nanoseconds */43#define LT_REG_ROE 8 /* Restart On countdown-timer Expiry flag */44#define LT_REG_IRQ 12 /* Interrupt status register */45#define LT_REG_COUNT 16 /* Time for countdown timer (usec) */46#define LT_REG_SPKR 20 /* Beep control */4748/* Granularity of countdown timer (usec) */49#define LT_GRANULARITY 10000005051static bool havetimerclock;5253/*54* Setup routine called by autoconf stuff when an ltimer is found.55*/56int57config_ltimer(struct ltimer_softc *lt, int ltimerno)58{59/*60* Running on System/161 2.x, we always use the processor61* on-chip timer for hardclock and we don't need ltimer as62* hardclock.63*64* Ideally there should be code here that will use an ltimer65* for hardclock if nothing else is available; e.g. if we66* wanted to make OS/161 2.x run on System/161 1.x. However,67* that requires a good bit more infrastructure for handling68* timers than we have and it doesn't seem worthwhile.69*70* It would also require some hacking, because all CPUs need71* to receive timer interrupts. (Exercise: how would you make72* sure all CPUs receive exactly one timer interrupt? Remember73* that LAMEbus uses level-triggered interrupts, so the74* hardware interrupt line will cause repeated interrupts if75* it's not reset on the device; but if it's reset on the76* device before all CPUs manage to see it, those CPUs won't77* be interrupted at all.)78*79* Note that the beep and rtclock devices *do* attach to80* ltimer.81*/82(void)ltimerno;83lt->lt_hardclock = 0;8485/*86* We do, however, use ltimer for the timer clock, since the87* on-chip timer can't do that.88*/89if (!havetimerclock) {90havetimerclock = true;91lt->lt_timerclock = 1;9293/* Wire it to go off once every second. */94bus_write_register(lt->lt_bus, lt->lt_buspos, LT_REG_ROE, 1);95bus_write_register(lt->lt_bus, lt->lt_buspos, LT_REG_COUNT,96LT_GRANULARITY);97}9899return 0;100}101102/*103* Interrupt handler.104*/105void106ltimer_irq(void *vlt)107{108struct ltimer_softc *lt = vlt;109uint32_t val;110111val = bus_read_register(lt->lt_bus, lt->lt_buspos, LT_REG_IRQ);112if (val) {113/*114* Only call hardclock if we're responsible for hardclock.115* (Any additional timer devices are unused.)116*/117if (lt->lt_hardclock) {118hardclock();119}120/*121* Likewise for timerclock.122*/123if (lt->lt_timerclock) {124timerclock();125}126}127}128129/*130* The timer device will beep if you write to the beep register. It131* doesn't matter what value you write. This function is called if132* the beep device is attached to this timer.133*/134void135ltimer_beep(void *vlt)136{137struct ltimer_softc *lt = vlt;138139bus_write_register(lt->lt_bus, lt->lt_buspos, LT_REG_SPKR, 440);140}141142/*143* The timer device also has a realtime clock on it.144* This function gets called if the rtclock device is attached145* to this timer.146*/147void148ltimer_gettime(void *vlt, time_t *secs, uint32_t *nsecs)149{150struct ltimer_softc *lt = vlt;151uint32_t secs1, secs2;152int spl;153154/*155* Read the seconds twice, on either side of the nanoseconds.156* If nsecs is small, use the *later* value of seconds, in case157* the nanoseconds turned over between the time we got the earlier158* value and the time we got nsecs.159*160* Note that the clock in the ltimer device is accurate down161* to a single processor cycle, so this might actually matter162* now and then.163*164* Do it with interrupts off on the current processor to avoid165* getting garbage if we get an interrupt among the register166* reads.167*/168169spl = splhigh();170171secs1 = bus_read_register(lt->lt_bus, lt->lt_buspos,172LT_REG_SEC);173*nsecs = bus_read_register(lt->lt_bus, lt->lt_buspos,174LT_REG_NSEC);175secs2 = bus_read_register(lt->lt_bus, lt->lt_buspos,176LT_REG_SEC);177178splx(spl);179180if (*nsecs < 5000000) {181*secs = secs2;182}183else {184*secs = secs1;185}186}187188189