]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - arch/s390/kernel/time.c
s390/time: LPAR offset handling
[mirror_ubuntu-zesty-kernel.git] / arch / s390 / kernel / time.c
index 9409d32f285e903580a50aa9e5b90ccd37f6b1cb..d71623639997136e186e25e562b67e09dba95b99 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/gfp.h>
 #include <linux/kprobes.h>
 #include <asm/uaccess.h>
+#include <asm/facility.h>
 #include <asm/delay.h>
 #include <asm/div64.h>
 #include <asm/vdso.h>
@@ -61,6 +62,25 @@ static DEFINE_PER_CPU(struct clock_event_device, comparators);
 ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier);
 EXPORT_SYMBOL(s390_epoch_delta_notifier);
 
+unsigned char ptff_function_mask[16];
+unsigned long lpar_offset;
+
+/*
+ * Get time offsets with PTFF
+ */
+void __init ptff_init(void)
+{
+       struct ptff_qto qto;
+
+       if (!test_facility(28))
+               return;
+       ptff(&ptff_function_mask, sizeof(ptff_function_mask), PTFF_QAF);
+
+       /* get LPAR offset */
+       if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
+               lpar_offset = qto.tod_epoch_difference;
+}
+
 /*
  * Scheduler clock - returns current time in nanosec units.
  */
@@ -337,20 +357,20 @@ static unsigned long clock_sync_flags;
 #define CLOCK_SYNC_STP         3
 
 /*
- * The synchronous get_clock function. It will write the current clock
- * value to the clock pointer and return 0 if the clock is in sync with
- * the external time source. If the clock mode is local it will return
- * -EOPNOTSUPP and -EAGAIN if the clock is not in sync with the external
- * reference.
+ * The get_clock function for the physical clock. It will get the current
+ * TOD clock, subtract the LPAR offset and write the result to *clock.
+ * The function returns 0 if the clock is in sync with the external time
+ * source. If the clock mode is local it will return -EOPNOTSUPP and
+ * -EAGAIN if the clock is not in sync with the external reference.
  */
-int get_sync_clock(unsigned long long *clock)
+int get_phys_clock(unsigned long long *clock)
 {
        atomic_t *sw_ptr;
        unsigned int sw0, sw1;
 
        sw_ptr = &get_cpu_var(clock_sync_word);
        sw0 = atomic_read(sw_ptr);
-       *clock = get_tod_clock();
+       *clock = get_tod_clock() - lpar_offset;
        sw1 = atomic_read(sw_ptr);
        put_cpu_var(clock_sync_word);
        if (sw0 == sw1 && (sw0 & 0x80000000U))
@@ -364,7 +384,7 @@ int get_sync_clock(unsigned long long *clock)
                return -EACCES;
        return -EAGAIN;
 }
-EXPORT_SYMBOL(get_sync_clock);
+EXPORT_SYMBOL(get_phys_clock);
 
 /*
  * Make get_sync_clock return -EAGAIN.
@@ -758,6 +778,7 @@ static int etr_sync_clock(void *data)
        unsigned long long clock, old_clock, clock_delta, delay, delta;
        struct clock_sync_data *etr_sync;
        struct etr_aib *sync_port, *aib;
+       struct ptff_qto qto;
        int port;
        int rc;
 
@@ -804,6 +825,10 @@ static int etr_sync_clock(void *data)
                        etr_sync->in_sync = -EAGAIN;
                        rc = -EAGAIN;
                } else {
+                       if (ptff_query(PTFF_QTO) &&
+                           ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
+                               /* Update LPAR offset */
+                               lpar_offset = qto.tod_epoch_difference;
                        etr_sync->in_sync = 1;
                        rc = 0;
                }
@@ -1455,7 +1480,7 @@ static void __init stp_reset(void)
        int rc;
 
        stp_page = (void *) get_zeroed_page(GFP_ATOMIC);
-       rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+       rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000, NULL);
        if (rc == 0)
                set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags);
        else if (stp_online) {
@@ -1533,6 +1558,7 @@ static int stp_sync_clock(void *data)
        static int first;
        unsigned long long old_clock, delta, new_clock, clock_delta;
        struct clock_sync_data *stp_sync;
+       struct ptff_qto qto;
        int rc;
 
        stp_sync = data;
@@ -1554,11 +1580,14 @@ static int stp_sync_clock(void *data)
            stp_info.todoff[2] || stp_info.todoff[3] ||
            stp_info.tmd != 2) {
                old_clock = get_tod_clock();
-               rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0);
+               rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0, &clock_delta);
                if (rc == 0) {
-                       new_clock = get_tod_clock();
+                       new_clock = old_clock + clock_delta;
                        delta = adjust_time(old_clock, new_clock, 0);
-                       clock_delta = new_clock - old_clock;
+                       if (ptff_query(PTFF_QTO) &&
+                           ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
+                               /* Update LPAR offset */
+                               lpar_offset = qto.tod_epoch_difference;
                        atomic_notifier_call_chain(&s390_epoch_delta_notifier,
                                                   0, &clock_delta);
                        fixup_clock_comparator(delta);
@@ -1590,12 +1619,12 @@ static void stp_work_fn(struct work_struct *work)
        mutex_lock(&stp_work_mutex);
 
        if (!stp_online) {
-               chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+               chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000, NULL);
                del_timer_sync(&stp_timer);
                goto out_unlock;
        }
 
-       rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0);
+       rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0, NULL);
        if (rc)
                goto out_unlock;