/*
 *  arch/s390/kl_s390_util.c
 *    S/390 utility functions
 *
 *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH,
 *                       IBM Corporation
 *    Author(s): Michael Holzheu (holzheu@de.ibm.com),
 *
 *    Bugreports to: <Linux390@de.ibm.com>
 */

#include <klib.h>
#include <asm/kl_s390_util.h>

/*
 *  s390_get_cpu_lowcore
 *
 *  Return the lowcore of the given cpu
 */

void* s390_get_cpu_lowcore(int cpu)
{
	kaddr_t  lc_ptr;
	void*  lc = NULL;
	syment_t *lowcore_sym;
	int _LOWCORE_SZ;

	if(!(_LOWCORE_SZ = kl_struct_len("_lowcore"))) {
		goto out;
	}

	/* find out addresses of lowcores */

	if(!(lowcore_sym = kl_lkup_symname("lowcore_ptr"))) {
		goto out;
	}

	GET_BLOCK(lowcore_sym->s_addr + cpu * KL_NBPW, KL_NBPW, &lc_ptr);

	/* get lowcore of cpu */

	if(!(lc = kl_alloc_block(_LOWCORE_SZ, K_TEMP))) {
		goto out;
	}

	GET_BLOCK(lc_ptr, _LOWCORE_SZ, lc);
	if (KL_ERROR) {
		kl_free_block(lc);
		lc = NULL;
	}	
out:
	return lc;
}

/*
 *  s390_free_cpu_lowcore 
 *
 *  free the lowcore of the given cpu
 */

void s390_free_cpu_lowcore(void* lc)
{
        if(lc) {
                kl_free_block(lc);
	}
}

/*
 *  s390_get_nr_of_cpus
 *
 *  return number of System cpus
 */

int s390_get_nr_of_cpus()
{
	int cpus;
	GET_BLOCK(kl_lkup_symname("smp_num_cpus")->s_addr,sizeof(int),&cpus);
	return cpus;
}

/*
 *  s390_tod_to_timeval
 *
 *  convert tod (s390 Time of day) to timeval
 */

void s390_tod_to_timeval(uint64_t todval, struct timeval *xtime)
{
#if 0
        const int high_bit = 0x80000000L;
        const int c_f4240 = 0xf4240L;
        const int c_7a120 = 0x7a120;

        /* adjust todclock to 1970 */
        todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);

        /* We have to divide the 64 bit value todval by 4096
         * (because the 2^12 bit is the one that changes every
         * microsecond) and then split it into seconds and
         * microseconds. A value of max (2^52-1) divided by
         * the value 0xF4240 can yield a max result of approx
         * (2^32.068). Thats to big to fit into a signed int
         *   ... hacking time!
         */
        asm volatile ("L     2,%1\n\t"
                      "LR    3,2\n\t"
                      "SRL   2,12\n\t"
                      "SLL   3,20\n\t"
                      "L     4,%O1+4(%R1)\n\t"
                      "SRL   4,12\n\t"
                      "OR    3,4\n\t"   /* now R2/R3 contain (todval >> 12) */
                      "SR    4,4\n\t"
                      "CL    2,%2\n\t"
                      "JL    .+12\n\t"
                      "S     2,%2\n\t"
                      "L     4,%3\n\t"
                      "D     2,%4\n\t"
                      "OR    3,4\n\t"
                      "ST    2,%O0+4(%R0)\n\t"
                      "ST    3,%0":"=m" (*xtime):"m"(todval),
                      "m"(c_7a120), "m"(high_bit), "m"(c_f4240)
                      :"cc", "memory", "2", "3", "4");
#else
    /* adjust todclock to 1970 */
    todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);

    todval >>= 12;
    xtime->tv_sec  = todval / 1000000;
    xtime->tv_usec = todval % 1000000;
#endif
}

