18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  S390 version
48c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 1999
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  Derived from "include/asm-i386/timex.h"
78c2ecf20Sopenharmony_ci *    Copyright (C) 1992, Linus Torvalds
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#ifndef _ASM_S390_TIMEX_H
118c2ecf20Sopenharmony_ci#define _ASM_S390_TIMEX_H
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/preempt.h>
148c2ecf20Sopenharmony_ci#include <linux/time64.h>
158c2ecf20Sopenharmony_ci#include <asm/lowcore.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* The value of the TOD clock for 1.1.1970. */
188c2ecf20Sopenharmony_ci#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciextern u64 clock_comparator_max;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* Inline functions for clock register access. */
238c2ecf20Sopenharmony_cistatic inline int set_tod_clock(__u64 time)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	int cc;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	asm volatile(
288c2ecf20Sopenharmony_ci		"   sck   %1\n"
298c2ecf20Sopenharmony_ci		"   ipm   %0\n"
308c2ecf20Sopenharmony_ci		"   srl   %0,28\n"
318c2ecf20Sopenharmony_ci		: "=d" (cc) : "Q" (time) : "cc");
328c2ecf20Sopenharmony_ci	return cc;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic inline int store_tod_clock(__u64 *time)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	int cc;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	asm volatile(
408c2ecf20Sopenharmony_ci		"   stck  %1\n"
418c2ecf20Sopenharmony_ci		"   ipm   %0\n"
428c2ecf20Sopenharmony_ci		"   srl   %0,28\n"
438c2ecf20Sopenharmony_ci		: "=d" (cc), "=Q" (*time) : : "cc");
448c2ecf20Sopenharmony_ci	return cc;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic inline void set_clock_comparator(__u64 time)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	asm volatile("sckc %0" : : "Q" (time));
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_civoid clock_comparator_work(void);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_civoid __init time_early_init(void);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciextern unsigned char ptff_function_mask[16];
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Function codes for the ptff instruction. */
598c2ecf20Sopenharmony_ci#define PTFF_QAF	0x00	/* query available functions */
608c2ecf20Sopenharmony_ci#define PTFF_QTO	0x01	/* query tod offset */
618c2ecf20Sopenharmony_ci#define PTFF_QSI	0x02	/* query steering information */
628c2ecf20Sopenharmony_ci#define PTFF_QUI	0x04	/* query UTC information */
638c2ecf20Sopenharmony_ci#define PTFF_ATO	0x40	/* adjust tod offset */
648c2ecf20Sopenharmony_ci#define PTFF_STO	0x41	/* set tod offset */
658c2ecf20Sopenharmony_ci#define PTFF_SFS	0x42	/* set fine steering rate */
668c2ecf20Sopenharmony_ci#define PTFF_SGS	0x43	/* set gross steering rate */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Query TOD offset result */
698c2ecf20Sopenharmony_cistruct ptff_qto {
708c2ecf20Sopenharmony_ci	unsigned long long physical_clock;
718c2ecf20Sopenharmony_ci	unsigned long long tod_offset;
728c2ecf20Sopenharmony_ci	unsigned long long logical_tod_offset;
738c2ecf20Sopenharmony_ci	unsigned long long tod_epoch_difference;
748c2ecf20Sopenharmony_ci} __packed;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic inline int ptff_query(unsigned int nr)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	unsigned char *ptr;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	ptr = ptff_function_mask + (nr >> 3);
818c2ecf20Sopenharmony_ci	return (*ptr & (0x80 >> (nr & 7))) != 0;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* Query UTC information result */
858c2ecf20Sopenharmony_cistruct ptff_qui {
868c2ecf20Sopenharmony_ci	unsigned int tm : 2;
878c2ecf20Sopenharmony_ci	unsigned int ts : 2;
888c2ecf20Sopenharmony_ci	unsigned int : 28;
898c2ecf20Sopenharmony_ci	unsigned int pad_0x04;
908c2ecf20Sopenharmony_ci	unsigned long leap_event;
918c2ecf20Sopenharmony_ci	short old_leap;
928c2ecf20Sopenharmony_ci	short new_leap;
938c2ecf20Sopenharmony_ci	unsigned int pad_0x14;
948c2ecf20Sopenharmony_ci	unsigned long prt[5];
958c2ecf20Sopenharmony_ci	unsigned long cst[3];
968c2ecf20Sopenharmony_ci	unsigned int skew;
978c2ecf20Sopenharmony_ci	unsigned int pad_0x5c[41];
988c2ecf20Sopenharmony_ci} __packed;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/*
1018c2ecf20Sopenharmony_ci * ptff - Perform timing facility function
1028c2ecf20Sopenharmony_ci * @ptff_block: Pointer to ptff parameter block
1038c2ecf20Sopenharmony_ci * @len: Length of parameter block
1048c2ecf20Sopenharmony_ci * @func: Function code
1058c2ecf20Sopenharmony_ci * Returns: Condition code (0 on success)
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_ci#define ptff(ptff_block, len, func)					\
1088c2ecf20Sopenharmony_ci({									\
1098c2ecf20Sopenharmony_ci	struct addrtype { char _[len]; };				\
1108c2ecf20Sopenharmony_ci	register unsigned int reg0 asm("0") = func;			\
1118c2ecf20Sopenharmony_ci	register unsigned long reg1 asm("1") = (unsigned long) (ptff_block);\
1128c2ecf20Sopenharmony_ci	int rc;								\
1138c2ecf20Sopenharmony_ci									\
1148c2ecf20Sopenharmony_ci	asm volatile(							\
1158c2ecf20Sopenharmony_ci		"	.word	0x0104\n"				\
1168c2ecf20Sopenharmony_ci		"	ipm	%0\n"					\
1178c2ecf20Sopenharmony_ci		"	srl	%0,28\n"				\
1188c2ecf20Sopenharmony_ci		: "=d" (rc), "+m" (*(struct addrtype *) reg1)		\
1198c2ecf20Sopenharmony_ci		: "d" (reg0), "d" (reg1) : "cc");			\
1208c2ecf20Sopenharmony_ci	rc;								\
1218c2ecf20Sopenharmony_ci})
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic inline unsigned long long local_tick_disable(void)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	unsigned long long old;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	old = S390_lowcore.clock_comparator;
1288c2ecf20Sopenharmony_ci	S390_lowcore.clock_comparator = clock_comparator_max;
1298c2ecf20Sopenharmony_ci	set_clock_comparator(S390_lowcore.clock_comparator);
1308c2ecf20Sopenharmony_ci	return old;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic inline void local_tick_enable(unsigned long long comp)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	S390_lowcore.clock_comparator = comp;
1368c2ecf20Sopenharmony_ci	set_clock_comparator(S390_lowcore.clock_comparator);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define CLOCK_TICK_RATE		1193180 /* Underlying HZ */
1408c2ecf20Sopenharmony_ci#define STORE_CLOCK_EXT_SIZE	16	/* stcke writes 16 bytes */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_citypedef unsigned long long cycles_t;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic inline void get_tod_clock_ext(char *clk)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	typedef struct { char _[STORE_CLOCK_EXT_SIZE]; } addrtype;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	asm volatile("stcke %0" : "=Q" (*(addrtype *) clk) : : "cc");
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic inline unsigned long long get_tod_clock(void)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	char clk[STORE_CLOCK_EXT_SIZE];
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	get_tod_clock_ext(clk);
1568c2ecf20Sopenharmony_ci	return *((unsigned long long *)&clk[1]);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic inline unsigned long long get_tod_clock_fast(void)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
1628c2ecf20Sopenharmony_ci	unsigned long long clk;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	asm volatile("stckf %0" : "=Q" (clk) : : "cc");
1658c2ecf20Sopenharmony_ci	return clk;
1668c2ecf20Sopenharmony_ci#else
1678c2ecf20Sopenharmony_ci	return get_tod_clock();
1688c2ecf20Sopenharmony_ci#endif
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic inline cycles_t get_cycles(void)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	return (cycles_t) get_tod_clock() >> 2;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci#define get_cycles get_cycles
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ciint get_phys_clock(unsigned long *clock);
1788c2ecf20Sopenharmony_civoid init_cpu_timer(void);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ciextern unsigned char tod_clock_base[16] __aligned(8);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * get_clock_monotonic - returns current time in clock rate units
1848c2ecf20Sopenharmony_ci *
1858c2ecf20Sopenharmony_ci * The clock and tod_clock_base get changed via stop_machine.
1868c2ecf20Sopenharmony_ci * Therefore preemption must be disabled, otherwise the returned
1878c2ecf20Sopenharmony_ci * value is not guaranteed to be monotonic.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cistatic inline unsigned long long get_tod_clock_monotonic(void)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	unsigned long long tod;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	preempt_disable_notrace();
1948c2ecf20Sopenharmony_ci	tod = get_tod_clock() - *(unsigned long long *) &tod_clock_base[1];
1958c2ecf20Sopenharmony_ci	preempt_enable_notrace();
1968c2ecf20Sopenharmony_ci	return tod;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci/**
2008c2ecf20Sopenharmony_ci * tod_to_ns - convert a TOD format value to nanoseconds
2018c2ecf20Sopenharmony_ci * @todval: to be converted TOD format value
2028c2ecf20Sopenharmony_ci * Returns: number of nanoseconds that correspond to the TOD format value
2038c2ecf20Sopenharmony_ci *
2048c2ecf20Sopenharmony_ci * Converting a 64 Bit TOD format value to nanoseconds means that the value
2058c2ecf20Sopenharmony_ci * must be divided by 4.096. In order to achieve that we multiply with 125
2068c2ecf20Sopenharmony_ci * and divide by 512:
2078c2ecf20Sopenharmony_ci *
2088c2ecf20Sopenharmony_ci *    ns = (todval * 125) >> 9;
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * In order to avoid an overflow with the multiplication we can rewrite this.
2118c2ecf20Sopenharmony_ci * With a split todval == 2^9 * th + tl (th upper 55 bits, tl lower 9 bits)
2128c2ecf20Sopenharmony_ci * we end up with
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci *    ns = ((2^9 * th + tl) * 125 ) >> 9;
2158c2ecf20Sopenharmony_ci * -> ns = (th * 125) + ((tl * 125) >> 9);
2168c2ecf20Sopenharmony_ci *
2178c2ecf20Sopenharmony_ci */
2188c2ecf20Sopenharmony_cistatic inline unsigned long long tod_to_ns(unsigned long long todval)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	return ((todval >> 9) * 125) + (((todval & 0x1ff) * 125) >> 9);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/**
2248c2ecf20Sopenharmony_ci * tod_after - compare two 64 bit TOD values
2258c2ecf20Sopenharmony_ci * @a: first 64 bit TOD timestamp
2268c2ecf20Sopenharmony_ci * @b: second 64 bit TOD timestamp
2278c2ecf20Sopenharmony_ci *
2288c2ecf20Sopenharmony_ci * Returns: true if a is later than b
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistatic inline int tod_after(unsigned long long a, unsigned long long b)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	if (MACHINE_HAS_SCC)
2338c2ecf20Sopenharmony_ci		return (long long) a > (long long) b;
2348c2ecf20Sopenharmony_ci	return a > b;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/**
2388c2ecf20Sopenharmony_ci * tod_after_eq - compare two 64 bit TOD values
2398c2ecf20Sopenharmony_ci * @a: first 64 bit TOD timestamp
2408c2ecf20Sopenharmony_ci * @b: second 64 bit TOD timestamp
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * Returns: true if a is later than b
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_cistatic inline int tod_after_eq(unsigned long long a, unsigned long long b)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	if (MACHINE_HAS_SCC)
2478c2ecf20Sopenharmony_ci		return (long long) a >= (long long) b;
2488c2ecf20Sopenharmony_ci	return a >= b;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci#endif
252