2  * Userland implementation of gettimeofday() for 64 bits processes in a
 
   3  * ppc64 kernel for use in the vDSO
 
   5  * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org),
 
   8  * This program is free software; you can redistribute it and/or
 
   9  * modify it under the terms of the GNU General Public License
 
  10  * as published by the Free Software Foundation; either version
 
  11  * 2 of the License, or (at your option) any later version.
 
  13 #include <asm/processor.h>
 
  14 #include <asm/ppc_asm.h>
 
  16 #include <asm/asm-offsets.h>
 
  17 #include <asm/unistd.h>
 
  21  * Exact prototype of gettimeofday
 
  23  * int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz);
 
  26 V_FUNCTION_BEGIN(__kernel_gettimeofday)
 
  31         mr      r11,r3                  /* r11 holds tv */
 
  32         mr      r10,r4                  /* r10 holds tz */
 
  33         bl      V_LOCAL_FUNC(__get_datapage)    /* get data page */
 
  34         cmpldi  r11,0                   /* check if tv is NULL */
 
  36         lis     r7,1000000@ha           /* load up USEC_PER_SEC */
 
  38         bl      V_LOCAL_FUNC(__do_get_tspec) /* get sec/us from tb & kernel */
 
  39         std     r4,TVAL64_TV_SEC(r11)   /* store sec in tv */
 
  40         std     r5,TVAL64_TV_USEC(r11)  /* store usec in tv */
 
  41 2:      cmpldi  r10,0                   /* check if tz is NULL */
 
  43         lwz     r4,CFG_TZ_MINUTEWEST(r3)/* fill tz */
 
  44         lwz     r5,CFG_TZ_DSTTIME(r3)
 
  45         stw     r4,TZONE_TZ_MINWEST(r10)
 
  46         stw     r5,TZONE_TZ_DSTTIME(r10)
 
  49         li      r3,0                    /* always success */
 
  52 V_FUNCTION_END(__kernel_gettimeofday)
 
  56  * Exact prototype of clock_gettime()
 
  58  * int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp);
 
  61 V_FUNCTION_BEGIN(__kernel_clock_gettime)
 
  63         /* Check for supported clock IDs */
 
  64         cmpwi   cr0,r3,CLOCK_REALTIME
 
  65         cmpwi   cr1,r3,CLOCK_MONOTONIC
 
  66         cror    cr0*4+eq,cr0*4+eq,cr1*4+eq
 
  68         cmpwi   cr5,r3,CLOCK_REALTIME_COARSE
 
  69         cmpwi   cr6,r3,CLOCK_MONOTONIC_COARSE
 
  70         cror    cr5*4+eq,cr5*4+eq,cr6*4+eq
 
  72         cror    cr0*4+eq,cr0*4+eq,cr5*4+eq
 
  75         mflr    r12                     /* r12 saves lr */
 
  77         mr      r11,r4                  /* r11 saves tp */
 
  78         bl      V_LOCAL_FUNC(__get_datapage)    /* get data page */
 
  79         lis     r7,NSEC_PER_SEC@h       /* want nanoseconds */
 
  80         ori     r7,r7,NSEC_PER_SEC@l
 
  82 50:     bl      V_LOCAL_FUNC(__do_get_tspec)    /* get time from tb & kernel */
 
  83         bne     cr1,80f                 /* if not monotonic, all done */
 
  89         /* now we must fixup using wall to monotonic. We need to snapshot
 
  90          * that value and do the counter trick again. Fortunately, we still
 
  91          * have the counter value in r8 that was returned by __do_get_tspec.
 
  92          * At this point, r4,r5 contain our sec/nsec values.
 
  95         lwa     r6,WTOM_CLOCK_SEC(r3)
 
  96         lwa     r9,WTOM_CLOCK_NSEC(r3)
 
  98         /* We now have our result in r6,r9. We create a fake dependency
 
  99          * on that result and re-check the counter
 
 104         ld      r0,CFG_TB_UPDATE_COUNT(r3)
 
 105         cmpld   cr0,r0,r8               /* check if updated */
 
 110          * For coarse clocks we get data directly from the vdso data page, so
 
 111          * we don't need to call __do_get_tspec, but we still need to do the
 
 114 70:     ld      r8,CFG_TB_UPDATE_COUNT(r3)
 
 115         andi.   r0,r8,1                 /* pending update ? loop */
 
 117         add     r3,r3,r0                /* r0 is already 0 */
 
 120          * CLOCK_REALTIME_COARSE, below values are needed for MONOTONIC_COARSE
 
 123         ld      r4,STAMP_XTIME+TSPC64_TV_SEC(r3)
 
 124         ld      r5,STAMP_XTIME+TSPC64_TV_NSEC(r3)
 
 127         /* CLOCK_MONOTONIC_COARSE */
 
 128         lwa     r6,WTOM_CLOCK_SEC(r3)
 
 129         lwa     r9,WTOM_CLOCK_NSEC(r3)
 
 131         /* check if counter has updated */
 
 137         ld      r0,CFG_TB_UPDATE_COUNT(r3)
 
 138         cmpld   cr0,r0,r8               /* check if updated */
 
 141         /* Counter has not updated, so continue calculating proper values for
 
 142          * sec and nsec if monotonic coarse, or just return with the proper
 
 143          * values for realtime.
 
 147         /* Add wall->monotonic offset and check for overflow or underflow */
 
 159 80:     std     r4,TSPC64_TV_SEC(r11)
 
 160         std     r5,TSPC64_TV_NSEC(r11)
 
 171         li      r0,__NR_clock_gettime
 
 175 V_FUNCTION_END(__kernel_clock_gettime)
 
 179  * Exact prototype of clock_getres()
 
 181  * int __kernel_clock_getres(clockid_t clock_id, struct timespec *res);
 
 184 V_FUNCTION_BEGIN(__kernel_clock_getres)
 
 186         /* Check for supported clock IDs */
 
 187         cmpwi   cr0,r3,CLOCK_REALTIME
 
 188         cmpwi   cr1,r3,CLOCK_MONOTONIC
 
 189         cror    cr0*4+eq,cr0*4+eq,cr1*4+eq
 
 196         lis     r5,CLOCK_REALTIME_RES@h
 
 197         ori     r5,r5,CLOCK_REALTIME_RES@l
 
 198         std     r3,TSPC64_TV_SEC(r4)
 
 199         std     r5,TSPC64_TV_NSEC(r4)
 
 206         li      r0,__NR_clock_getres
 
 210 V_FUNCTION_END(__kernel_clock_getres)
 
 213  * Exact prototype of time()
 
 215  * time_t time(time *t);
 
 218 V_FUNCTION_BEGIN(__kernel_time)
 
 223         mr      r11,r3                  /* r11 holds t */
 
 224         bl      V_LOCAL_FUNC(__get_datapage)
 
 226         ld      r4,STAMP_XTIME+TSPC64_TV_SEC(r3)
 
 228         cmpldi  r11,0                   /* check if t is NULL */
 
 230         std     r4,0(r11)               /* store result at *t */
 
 236 V_FUNCTION_END(__kernel_time)
 
 240  * This is the core of clock_gettime() and gettimeofday(),
 
 241  * it returns the current time in r4 (seconds) and r5.
 
 242  * On entry, r7 gives the resolution of r5, either USEC_PER_SEC
 
 243  * or NSEC_PER_SEC, giving r5 in microseconds or nanoseconds.
 
 244  * It expects the datapage ptr in r3 and doesn't clobber it.
 
 245  * It clobbers r0, r6 and r9.
 
 246  * On return, r8 contains the counter value that can be reused.
 
 247  * This clobbers cr0 but not any other cr field.
 
 249 V_FUNCTION_BEGIN(__do_get_tspec)
 
 251         /* check for update count & load values */
 
 252 1:      ld      r8,CFG_TB_UPDATE_COUNT(r3)
 
 253         andi.   r0,r8,1                 /* pending update ? loop */
 
 255         xor     r0,r8,r8                /* create dependency */
 
 258         /* Get TB & offset it. We use the MFTB macro which will generate
 
 259          * workaround code for Cell.
 
 262         ld      r9,CFG_TB_ORIG_STAMP(r3)
 
 266         ld      r5,CFG_TB_TO_XS(r3)
 
 267         sldi    r6,r6,12                /* compute time since stamp_xtime */
 
 268         mulhdu  r6,r6,r5                /* in units of 2^-32 seconds */
 
 270         /* Add stamp since epoch */
 
 271         ld      r4,STAMP_XTIME+TSPC64_TV_SEC(r3)
 
 272         lwz     r5,STAMP_SEC_FRAC(r3)
 
 277         ld      r0,CFG_TB_UPDATE_COUNT(r3)
 
 278         cmpld   r0,r8                   /* check if updated */
 
 279         bne-    1b                      /* reload if so */
 
 281         /* convert to seconds & nanoseconds and add to stamp */
 
 282         add     r6,r6,r5                /* add on fractional seconds of xtime */
 
 283         mulhwu  r5,r6,r7                /* compute micro or nanoseconds and */
 
 284         srdi    r6,r6,32                /* seconds since stamp_xtime */
 
 289 V_FUNCTION_END(__do_get_tspec)