diff options
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | arch/s390/include/asm/stp.h | 1 | ||||
-rw-r--r-- | arch/s390/include/asm/timex.h | 6 | ||||
-rw-r--r-- | arch/s390/kernel/time.c | 6 | ||||
-rw-r--r-- | drivers/ptp/Kconfig | 11 | ||||
-rw-r--r-- | drivers/ptp/Makefile | 1 | ||||
-rw-r--r-- | drivers/ptp/ptp_s390.c | 129 |
7 files changed, 160 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index f39ab140710f..c08624a073bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20204,6 +20204,12 @@ F: Documentation/arch/s390/pci.rst F: arch/s390/pci/ F: drivers/pci/hotplug/s390_pci_hpc.c +S390 PTP DRIVER +M: Sven Schnelle <[email protected]> +S: Supported +F: drivers/ptp/ptp_s390.c + S390 SCM DRIVER M: Vineeth Vijayan <[email protected]> diff --git a/arch/s390/include/asm/stp.h b/arch/s390/include/asm/stp.h index 4d74d7e33340..827cb208de86 100644 --- a/arch/s390/include/asm/stp.h +++ b/arch/s390/include/asm/stp.h @@ -94,5 +94,6 @@ struct stp_stzi { int stp_sync_check(void); int stp_island_check(void); void stp_queue_work(void); +bool stp_enabled(void); #endif /* __S390_STP_H */ diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h index 640901f2fbc3..642fd303ce01 100644 --- a/arch/s390/include/asm/timex.h +++ b/arch/s390/include/asm/timex.h @@ -93,6 +93,7 @@ extern unsigned char ptff_function_mask[16]; #define PTFF_QAF 0x00 /* query available functions */ #define PTFF_QTO 0x01 /* query tod offset */ #define PTFF_QSI 0x02 /* query steering information */ +#define PTFF_QPT 0x03 /* query physical clock */ #define PTFF_QUI 0x04 /* query UTC information */ #define PTFF_ATO 0x40 /* adjust tod offset */ #define PTFF_STO 0x41 /* set tod offset */ @@ -250,6 +251,11 @@ static __always_inline unsigned long tod_to_ns(unsigned long todval) return ((todval >> 9) * 125) + (((todval & 0x1ff) * 125) >> 9); } +static __always_inline u128 eitod_to_ns(u128 todval) +{ + return (todval * 125) >> 9; +} + /** * tod_after - compare two 64 bit TOD values * @a: first 64 bit TOD timestamp diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 4214901c3ab0..cc60b16a7dd0 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -469,6 +469,12 @@ static void __init stp_reset(void) } } +bool stp_enabled(void) +{ + return test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags) && stp_online; +} +EXPORT_SYMBOL(stp_enabled); + static void stp_timeout(struct timer_list *unused) { queue_work(time_sync_wq, &stp_work); diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig index e98c9767e0ef..3eac514195af 100644 --- a/drivers/ptp/Kconfig +++ b/drivers/ptp/Kconfig @@ -237,4 +237,15 @@ config PTP_DFL_TOD To compile this driver as a module, choose M here: the module will be called ptp_dfl_tod. +config PTP_S390 + tristate "S390 PTP driver" + depends on PTP_1588_CLOCK + depends on S390 + help + This driver adds support for S390 time steering via the PtP + interface. This works by adding a in-kernel clock delta value, + which is always added to time values used in the kernel. The PtP + driver provides the raw clock value without the delta to + userspace. That way userspace programs like chrony could steer + the kernel clock. endmenu diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile index 01b5cd91eb61..25f846fe48c9 100644 --- a/drivers/ptp/Makefile +++ b/drivers/ptp/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_PTP_1588_CLOCK_MOCK) += ptp_mock.o obj-$(CONFIG_PTP_1588_CLOCK_VMW) += ptp_vmw.o obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o obj-$(CONFIG_PTP_DFL_TOD) += ptp_dfl_tod.o +obj-$(CONFIG_PTP_S390) += ptp_s390.o diff --git a/drivers/ptp/ptp_s390.c b/drivers/ptp/ptp_s390.c new file mode 100644 index 000000000000..29618eb9bf44 --- /dev/null +++ b/drivers/ptp/ptp_s390.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * s390 PTP clock driver + * + */ + +#include "ptp_private.h" +#include <linux/time.h> +#include <asm/stp.h> + +static struct ptp_clock *ptp_stcke_clock, *ptp_qpt_clock; + +static int ptp_s390_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + return -EOPNOTSUPP; +} + +static int ptp_s390_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + return -EOPNOTSUPP; +} + +static struct timespec64 eitod_to_timespec64(union tod_clock *clk) +{ + return ns_to_timespec64(eitod_to_ns(clk->eitod - TOD_UNIX_EPOCH)); +} + +static struct timespec64 tod_to_timespec64(unsigned long tod) +{ + return ns_to_timespec64(tod_to_ns(tod - TOD_UNIX_EPOCH)); +} + +static int ptp_s390_stcke_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + union tod_clock tod; + + if (!stp_enabled()) + return -EOPNOTSUPP; + + store_tod_clock_ext(&tod); + *ts = eitod_to_timespec64(&tod); + return 0; +} + +static int ptp_s390_qpt_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + unsigned long tod; + + ptff(&tod, sizeof(tod), PTFF_QPT); + *ts = tod_to_timespec64(tod); + return 0; +} + +static int ptp_s390_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + return -EOPNOTSUPP; +} + +static int s390_arch_ptp_get_crosststamp(ktime_t *device_time, + struct system_counterval_t *system_counter, + void *ctx) +{ + union tod_clock clk; + + store_tod_clock_ext(&clk); + *device_time = ns_to_ktime(tod_to_ns(clk.tod - TOD_UNIX_EPOCH)); + system_counter->cycles = clk.tod; + system_counter->cs_id = CSID_S390_TOD; + return 0; +} + +static int ptp_s390_getcrosststamp(struct ptp_clock_info *ptp, + struct system_device_crosststamp *xtstamp) +{ + if (!stp_enabled()) + return -EOPNOTSUPP; + return get_device_system_crosststamp(s390_arch_ptp_get_crosststamp, NULL, NULL, xtstamp); +} + +static struct ptp_clock_info ptp_s390_stcke_info = { + .owner = THIS_MODULE, + .name = "s390 STCKE Clock", + .max_adj = 0, + .adjfine = ptp_s390_adjfine, + .adjtime = ptp_s390_adjtime, + .gettime64 = ptp_s390_stcke_gettime, + .settime64 = ptp_s390_settime, + .getcrosststamp = ptp_s390_getcrosststamp, +}; + +static struct ptp_clock_info ptp_s390_qpt_info = { + .owner = THIS_MODULE, + .name = "s390 Physical Clock", + .max_adj = 0, + .adjfine = ptp_s390_adjfine, + .adjtime = ptp_s390_adjtime, + .gettime64 = ptp_s390_qpt_gettime, + .settime64 = ptp_s390_settime, +}; + +static __init int ptp_s390_init(void) +{ + ptp_stcke_clock = ptp_clock_register(&ptp_s390_stcke_info, NULL); + if (IS_ERR(ptp_stcke_clock)) + return PTR_ERR(ptp_stcke_clock); + + ptp_qpt_clock = ptp_clock_register(&ptp_s390_qpt_info, NULL); + if (IS_ERR(ptp_qpt_clock)) { + ptp_clock_unregister(ptp_stcke_clock); + return PTR_ERR(ptp_qpt_clock); + } + return 0; +} + +static __exit void ptp_s390_exit(void) +{ + ptp_clock_unregister(ptp_qpt_clock); + ptp_clock_unregister(ptp_stcke_clock); +} + +module_init(ptp_s390_init); +module_exit(ptp_s390_exit); + +MODULE_AUTHOR("Sven Schnelle <[email protected]>"); +MODULE_DESCRIPTION("s390 Physical/STCKE Clock PtP Driver"); +MODULE_LICENSE("GPL"); |