188 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * Driver for LAMEbus clock/timer card
 | 
						|
 */
 | 
						|
#include <types.h>
 | 
						|
#include <lib.h>
 | 
						|
#include <spl.h>
 | 
						|
#include <clock.h>
 | 
						|
#include <platform/bus.h>
 | 
						|
#include <lamebus/ltimer.h>
 | 
						|
#include "autoconf.h"
 | 
						|
 | 
						|
/* Registers (offsets within slot) */
 | 
						|
#define LT_REG_SEC    0     /* time of day: seconds */
 | 
						|
#define LT_REG_NSEC   4     /* time of day: nanoseconds */
 | 
						|
#define LT_REG_ROE    8     /* Restart On countdown-timer Expiry flag */
 | 
						|
#define LT_REG_IRQ    12    /* Interrupt status register */
 | 
						|
#define LT_REG_COUNT  16    /* Time for countdown timer (usec) */
 | 
						|
#define LT_REG_SPKR   20    /* Beep control */
 | 
						|
 | 
						|
/* Granularity of countdown timer (usec) */
 | 
						|
#define LT_GRANULARITY   1000000
 | 
						|
 | 
						|
static bool havetimerclock;
 | 
						|
 | 
						|
/*
 | 
						|
 * Setup routine called by autoconf stuff when an ltimer is found.
 | 
						|
 */
 | 
						|
int
 | 
						|
config_ltimer(struct ltimer_softc *lt, int ltimerno)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * Running on System/161 2.x, we always use the processor
 | 
						|
	 * on-chip timer for hardclock and we don't need ltimer as
 | 
						|
	 * hardclock.
 | 
						|
	 *
 | 
						|
	 * Ideally there should be code here that will use an ltimer
 | 
						|
	 * for hardclock if nothing else is available; e.g. if we
 | 
						|
	 * wanted to make OS/161 2.x run on System/161 1.x. However,
 | 
						|
	 * that requires a good bit more infrastructure for handling
 | 
						|
	 * timers than we have and it doesn't seem worthwhile.
 | 
						|
	 *
 | 
						|
	 * It would also require some hacking, because all CPUs need
 | 
						|
	 * to receive timer interrupts. (Exercise: how would you make
 | 
						|
	 * sure all CPUs receive exactly one timer interrupt? Remember
 | 
						|
	 * that LAMEbus uses level-triggered interrupts, so the
 | 
						|
	 * hardware interrupt line will cause repeated interrupts if
 | 
						|
	 * it's not reset on the device; but if it's reset on the
 | 
						|
	 * device before all CPUs manage to see it, those CPUs won't
 | 
						|
	 * be interrupted at all.)
 | 
						|
	 *
 | 
						|
	 * Note that the beep and rtclock devices *do* attach to
 | 
						|
	 * ltimer.
 | 
						|
	 */
 | 
						|
	(void)ltimerno;
 | 
						|
	lt->lt_hardclock = 0;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We do, however, use ltimer for the timer clock, since the
 | 
						|
	 * on-chip timer can't do that.
 | 
						|
	 */
 | 
						|
	if (!havetimerclock) {
 | 
						|
		havetimerclock = true;
 | 
						|
		lt->lt_timerclock = 1;
 | 
						|
 | 
						|
		/* Wire it to go off once every second. */
 | 
						|
		bus_write_register(lt->lt_bus, lt->lt_buspos, LT_REG_ROE, 1);
 | 
						|
		bus_write_register(lt->lt_bus, lt->lt_buspos, LT_REG_COUNT,
 | 
						|
				   LT_GRANULARITY);
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Interrupt handler.
 | 
						|
 */
 | 
						|
void
 | 
						|
ltimer_irq(void *vlt)
 | 
						|
{
 | 
						|
	struct ltimer_softc *lt = vlt;
 | 
						|
	uint32_t val;
 | 
						|
 | 
						|
	val = bus_read_register(lt->lt_bus, lt->lt_buspos, LT_REG_IRQ);
 | 
						|
	if (val) {
 | 
						|
		/*
 | 
						|
		 * Only call hardclock if we're responsible for hardclock.
 | 
						|
		 * (Any additional timer devices are unused.)
 | 
						|
		 */
 | 
						|
		if (lt->lt_hardclock) {
 | 
						|
			hardclock();
 | 
						|
		}
 | 
						|
		/*
 | 
						|
		 * Likewise for timerclock.
 | 
						|
		 */
 | 
						|
		if (lt->lt_timerclock) {
 | 
						|
			timerclock();
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * The timer device will beep if you write to the beep register. It
 | 
						|
 * doesn't matter what value you write. This function is called if
 | 
						|
 * the beep device is attached to this timer.
 | 
						|
 */
 | 
						|
void
 | 
						|
ltimer_beep(void *vlt)
 | 
						|
{
 | 
						|
	struct ltimer_softc *lt = vlt;
 | 
						|
 | 
						|
	bus_write_register(lt->lt_bus, lt->lt_buspos, LT_REG_SPKR, 440);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * The timer device also has a realtime clock on it.
 | 
						|
 * This function gets called if the rtclock device is attached
 | 
						|
 * to this timer.
 | 
						|
 */
 | 
						|
void
 | 
						|
ltimer_gettime(void *vlt, struct timespec *ts)
 | 
						|
{
 | 
						|
	struct ltimer_softc *lt = vlt;
 | 
						|
	uint32_t secs1, secs2;
 | 
						|
	int spl;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Read the seconds twice, on either side of the nanoseconds.
 | 
						|
	 * If nsecs is small, use the *later* value of seconds, in case
 | 
						|
	 * the nanoseconds turned over between the time we got the earlier
 | 
						|
	 * value and the time we got nsecs.
 | 
						|
	 *
 | 
						|
	 * Note that the clock in the ltimer device is accurate down
 | 
						|
	 * to a single processor cycle, so this might actually matter
 | 
						|
	 * now and then.
 | 
						|
	 *
 | 
						|
	 * Do it with interrupts off on the current processor to avoid
 | 
						|
	 * getting garbage if we get an interrupt among the register
 | 
						|
	 * reads.
 | 
						|
	 */
 | 
						|
 | 
						|
	spl = splhigh();
 | 
						|
 | 
						|
	secs1 = bus_read_register(lt->lt_bus, lt->lt_buspos,
 | 
						|
				  LT_REG_SEC);
 | 
						|
	ts->tv_nsec = bus_read_register(lt->lt_bus, lt->lt_buspos,
 | 
						|
				   LT_REG_NSEC);
 | 
						|
	secs2 = bus_read_register(lt->lt_bus, lt->lt_buspos,
 | 
						|
				  LT_REG_SEC);
 | 
						|
 | 
						|
	splx(spl);
 | 
						|
 | 
						|
	if (ts->tv_nsec < 5000000) {
 | 
						|
		ts->tv_sec = secs2;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		ts->tv_sec = secs1;
 | 
						|
	}
 | 
						|
}
 |