/*
 * Copyright 1991-1998 by Open Software Foundation, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */
/*
 * MkLinux
 */

/*
 *	File:		rtclock.c
 *	Purpose:
 *		Routines for handling the machine dependent
 *		battery-backed clock. On the Sequent, this
 *		clock is generated by the SCED based time-
 *		of-day register.
 */

#include <sys/types.h>
#include <kern/clock.h>

#include <sqt/cfg.h>
#include <sqt/clock.h>
#include <sqt/slic.h>
#include <sqt/ioconf.h>
#include <sqt/vm_defs.h>
#include <sqt/hwparam.h>
#include <sqt/intctl.h>

#include <sqtsec/sec.h>

extern	u_char	cons_scsi;		/* console scsi slic address */
struct	sec_cib *todcib;
struct	sec_gmode todgm;	/* getmodes command */
struct	sec_smode todsm;	/* setmodes command */

int		bbc_config(), bbc_init();
kern_return_t	bbc_gettime(), bbc_settime();
kern_return_t	bbc_getattr(), bbc_setattr();

struct clock_ops  bbc_ops = {
	bbc_config,	bbc_init,	bbc_gettime,	bbc_settime,
	bbc_getattr,	bbc_setattr,	0,		0,
};

#define	LOCK_BBC()	splhi()
#define	UNLOCK_BBC(s)	splx(s)

/*
 * Configure battery-backed clock.
 */
int
bbc_config()
{
	int		BbcFlag;
	int		i;

	if (BbcFlag = (int)todcib) {
		/*
		 * Find console SCED and check if the TOD clock has
		 * failed powerup diagnostics.
		 */
		for (i = 0; i < NSEC; i++) {
			/* is SEC there? */
			if ((SECvec & (1 << i)) == 0)
				continue;
			if (SEC_desc[i].sec_is_cons)
				break;
		}
		if (SEC_desc[i].sec_diag_flags & CFG_S_TOD)
			BbcFlag = 0;
	}
	if (BbcFlag)
		printf("battery clock configured\n");
	else
		printf("WARNING: Battery Clock Failure!\n");
	return (BbcFlag);
}

/*
 * Initialize battery-backed clock.
 */
int
bbc_init()
{
	/* nothing to do here unless we wanted to check the
	   sanity of the time value of the clock at boot */
	return (1);
}

/*
 * Get the current clock time.
 */
kern_return_t
bbc_gettime(cur_time)
	tvalspec_t	*cur_time;	/* OUT */
{
	unsigned int	todr;
	struct sec_gmode *todgmptr;
	int		s;

	todgmptr = &todgm;
	todgmptr->gm_status = 0;
	todcib->cib_inst = SINST_GETMODE;
	todcib->cib_status = KVTOPHYS(&todgm, int *);

	s = LOCK_BBC();
	mIntr(cons_scsi, TODCLKBIN, SDEV_TOD);
	UNLOCK_BBC(s);

	while ((todgmptr->gm_status & SINST_INSDONE) == 0)
		continue;
	if (todgmptr->gm_status != SINST_INSDONE)
		return (KERN_FAILURE);

	cur_time->tv_sec  = todgmptr->gm_un.gm_tod.tod_newtime;
	cur_time->tv_nsec = 0;
	return (KERN_SUCCESS);
}

/*
 * Set the current clock time to the SCED tod device.
 */
kern_return_t
bbc_settime(new_time)
	tvalspec_t	*new_time;
{
	int		s;
	struct sec_smode *todsmptr;

	todsmptr = &todsm;
	todsmptr->sm_status = 0;
	todsmptr->sm_un.sm_tod.tod_freq = TODFREQ;
	todcib->cib_inst = SINST_SETMODE;
	todcib->cib_status = KVTOPHYS(&todsm, int *);

	/*
	 * Set the time. Bin 3 is sufficient, helps avoid SLIC-bus lockup.
	 */
	s = LOCK_BBC();
	todsmptr->sm_un.sm_tod.tod_newtime = new_time->tv_sec;
	mIntr(cons_scsi, 3, SDEV_TOD);
	UNLOCK_BBC(s);

	while ((todsmptr->sm_status & SINST_INSDONE) == 0)
		continue;
	if (todsmptr->sm_status != SINST_INSDONE)
		return (KERN_FAILURE);
	return (KERN_SUCCESS);
}

/*
 * Get clock device attributes.
 */
kern_return_t
bbc_getattr(flavor, attr, count)
	clock_flavor_t		flavor;
	clock_attr_t		attr;		/* OUT */
	mach_msg_type_number_t	*count;		/* IN/OUT */
{
	if (*count != 1)
		return (KERN_FAILURE);
	switch (flavor) {

	case CLOCK_GET_TIME_RES:	/* >0 res */
		*(clock_res_t *) attr = NSEC_PER_SEC;
		break;

	case CLOCK_MAP_TIME_RES:	/* =0 no mapping */
	case CLOCK_ALARM_CURRES:	/* =0 no alarm */
	case CLOCK_ALARM_MINRES:
	case CLOCK_ALARM_MAXRES:
		*(clock_res_t *) attr = 0;
		break;

	default:
		return (KERN_INVALID_VALUE);
	}
	return (KERN_SUCCESS);
}

/*
 * Set clock device attributes.
 */
kern_return_t
bbc_setattr(flavor, attr, count)
	clock_flavor_t		flavor;
	clock_attr_t		attr;		/* IN */
	mach_msg_type_number_t	count;		/* IN */
{
	return (KERN_FAILURE);
}

/*
 * Interrupt service routine.
 */
bbc_intr()
{
	;
}
