| 1 | |
| 2 | |
| 3 | * Time of day based timer functions.
|
| 4 | *
|
| 5 | * S390 version
|
| 6 | * Copyright IBM Corp. 1999, 2008
|
| 7 | * Author(s): Hartmut Penner ([email protected]),
|
| 8 | * Martin Schwidefsky ([email protected]),
|
| 9 | * Denis Joseph Barrow ([email protected],[email protected])
|
| 10 | *
|
| 11 | * Derived from "arch/i386/kernel/time.c"
|
| 12 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds
|
| 13 |
|
| 14 |
|
| 15 | #define pr_fmt(fmt) "time: " fmt
|
| 16 |
|
| 17 | #include <linux/kernel_stat.h>
|
| 18 | #include <linux/errno.h>
|
| 19 | #include <linux/export.h>
|
| 20 | #include <linux/sched.h>
|
| 21 | #include <linux/sched/clock.h>
|
| 22 | #include <linux/kernel.h>
|
| 23 | #include <linux/param.h>
|
| 24 | #include <linux/string.h>
|
| 25 | #include <linux/mm.h>
|
| 26 | #include <linux/interrupt.h>
|
| 27 | #include <linux/cpu.h>
|
| 28 | #include <linux/stop_machine.h>
|
| 29 | #include <linux/time.h>
|
| 30 | #include <linux/device.h>
|
| 31 | #include <linux/delay.h>
|
| 32 | #include <linux/init.h>
|
| 33 | #include <linux/smp.h>
|
| 34 | #include <linux/types.h>
|
| 35 | #include <linux/profile.h>
|
| 36 | #include <linux/timex.h>
|
| 37 | #include <linux/notifier.h>
|
| 38 | #include <linux/clockchips.h>
|
| 39 | #include <linux/gfp.h>
|
| 40 | #include <linux/kprobes.h>
|
| 41 | #include <linux/uaccess.h>
|
| 42 | #include <vdso/vsyscall.h>
|
| 43 | #include <vdso/clocksource.h>
|
| 44 | #include <vdso/helpers.h>
|
| 45 | #include <asm/facility.h>
|
| 46 | #include <asm/delay.h>
|
| 47 | #include <asm/div64.h>
|
| 48 | #include <asm/vdso.h>
|
| 49 | #include <asm/irq.h>
|
| 50 | #include <asm/irq_regs.h>
|
| 51 | #include <asm/stp.h>
|
| 52 | #include <asm/cio.h>
|
| 53 | #include <asm/vtime.h>
|
| 54 | #include "entry.h"
|
| 55 |
|
| 56 | union tod_clock __bootdata_preserved(tod_clock_base);
|
| 57 | EXPORT_SYMBOL_GPL(tod_clock_base);
|
| 58 |
|
| 59 | u64 __bootdata_preserved(clock_comparator_max);
|
| 60 | EXPORT_SYMBOL_GPL(clock_comparator_max);
|
| 61 |
|
| 62 | static DEFINE_PER_CPU(struct clock_event_device, comparators);
|
| 63 |
|
| 64 | ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier);
|
| 65 | EXPORT_SYMBOL(s390_epoch_delta_notifier);
|
| 66 |
|
| 67 | unsigned char ptff_function_mask[16];
|
| 68 | EXPORT_SYMBOL(ptff_function_mask);
|
| 69 |
|
| 70 | static unsigned long lpar_offset;
|
| 71 | static unsigned long initial_leap_seconds;
|
| 72 |
|
| 73 | |
| 74 | * Get time offsets with PTFF
|
| 75 |
|
| 76 | void __init time_early_init(void)
|
| 77 | {
|
| 78 | struct ptff_qto qto;
|
| 79 | struct ptff_qui qui;
|
| 80 |
|
| 81 | vdso_k_time_data->arch_data.tod_delta = tod_clock_base.tod;
|
| 82 |
|
| 83 | if (!test_facility(28))
|
| 84 | return;
|
| 85 |
|
| 86 | ptff(&ptff_function_mask, sizeof(ptff_function_mask), PTFF_QAF);
|
| 87 |
|
| 88 |
|
| 89 | if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
|
| 90 | lpar_offset = qto.tod_epoch_difference;
|
| 91 |
|
| 92 |
|
| 93 | if (ptff_query(PTFF_QUI) && ptff(&qui, sizeof(qui), PTFF_QUI) == 0)
|
| 94 | initial_leap_seconds = (unsigned long)
|
| 95 | ((long) qui.old_leap * 4096000000L);
|
| 96 | }
|
| 97 |
|
| 98 | unsigned long long noinstr sched_clock_noinstr(void)
|
| 99 | {
|
| 100 | return tod_to_ns(__get_tod_clock_monotonic());
|
| 101 | }
|
| 102 |
|
| 103 | |
| 104 | * Scheduler clock - returns current time in nanosec units.
|
| 105 |
|
| 106 | unsigned long long notrace sched_clock(void)
|
| 107 | {
|
| 108 | return tod_to_ns(get_tod_clock_monotonic());
|
| 109 | }
|
| 110 | NOKPROBE_SYMBOL(sched_clock);
|
| 111 |
|
| 112 | static void ext_to_timespec64(union tod_clock *clk, struct timespec64 *xt)
|
| 113 | {
|
| 114 | unsigned long rem, sec, nsec;
|
| 115 |
|
| 116 | sec = clk->us;
|
| 117 | rem = do_div(sec, 1000000);
|
| 118 | nsec = ((clk->sus + (rem << 12)) * 125) >> 9;
|
| 119 | xt->tv_sec = sec;
|
| 120 | xt->tv_nsec = nsec;
|
| 121 | }
|
| 122 |
|
| 123 | void clock_comparator_work(void)
|
| 124 | {
|
| 125 | struct clock_event_device *cd;
|
| 126 |
|
| 127 | get_lowcore()->clock_comparator = clock_comparator_max;
|
| 128 | cd = this_cpu_ptr(&comparators);
|
| 129 | cd->event_handler(cd);
|
| 130 | }
|
| 131 |
|
| 132 | static int s390_next_event(unsigned long delta,
|
| 133 | struct clock_event_device *evt)
|
| 134 | {
|
| 135 | get_lowcore()->clock_comparator = get_tod_clock() + delta;
|
| 136 | set_clock_comparator(get_lowcore()->clock_comparator);
|
| 137 | return 0;
|
| 138 | }
|
| 139 |
|
| 140 | |
| 141 | * Set up lowcore and control register of the current cpu to
|
| 142 | * enable TOD clock and clock comparator interrupts.
|
| 143 |
|
| 144 | void init_cpu_timer(void)
|
| 145 | {
|
| 146 | struct clock_event_device *cd;
|
| 147 | int cpu;
|
| 148 |
|
| 149 | get_lowcore()->clock_comparator = clock_comparator_max;
|
| 150 | set_clock_comparator(get_lowcore()->clock_comparator);
|
| 151 |
|
| 152 | cpu = smp_processor_id();
|
| 153 | cd = &per_cpu(comparators, cpu);
|
| 154 | cd->name = "comparator";
|
| 155 | cd->features = CLOCK_EVT_FEAT_ONESHOT;
|
| 156 | cd->mult = 16777;
|
| 157 | cd->shift = 12;
|
| 158 | cd->min_delta_ns = 1;
|
| 159 | cd->min_delta_ticks = 1;
|
| 160 | cd->max_delta_ns = LONG_MAX;
|
| 161 | cd->max_delta_ticks = ULONG_MAX;
|
| 162 | cd->rating = 400;
|
| 163 | cd->cpumask = cpumask_of(cpu);
|
| 164 | cd->set_next_event = s390_next_event;
|
| 165 |
|
| 166 | clockevents_register_device(cd);
|
| 167 |
|
| 168 |
|
| 169 | local_ctl_set_bit(0, CR0_CLOCK_COMPARATOR_SUBMASK_BIT);
|
| 170 |
|
| 171 |
|
| 172 | local_ctl_set_bit(0, CR0_ETR_SUBMASK_BIT);
|
| 173 | }
|
| 174 |
|
| 175 | static void clock_comparator_interrupt(struct ext_code ext_code,
|
| 176 | unsigned int param32,
|
| 177 | unsigned long param64)
|
| 178 | {
|
| 179 | inc_irq_stat(IRQEXT_CLK);
|
| 180 | if (get_lowcore()->clock_comparator == clock_comparator_max)
|
| 181 | set_clock_comparator(get_lowcore()->clock_comparator);
|
| 182 | }
|
| 183 |
|
| 184 | static void stp_timing_alert(struct stp_irq_parm *);
|
| 185 |
|
| 186 | static void timing_alert_interrupt(struct ext_code ext_code,
|
| 187 | unsigned int param32, unsigned long param64)
|
| 188 | {
|
| 189 | inc_irq_stat(IRQEXT_TLA);
|
| 190 | if (param32 & 0x00038000)
|
| 191 | stp_timing_alert((struct stp_irq_parm *) ¶m32);
|
| 192 | }
|
| 193 |
|
| 194 | static void stp_reset(void);
|
| 195 |
|
| 196 | void read_persistent_clock64(struct timespec64 *ts)
|
| 197 | {
|
| 198 | union tod_clock clk;
|
| 199 | u64 delta;
|
| 200 |
|
| 201 | delta = initial_leap_seconds + TOD_UNIX_EPOCH;
|
| 202 | store_tod_clock_ext(&clk);
|
| 203 | clk.eitod -= delta;
|
| 204 | ext_to_timespec64(&clk, ts);
|
| 205 | }
|
| 206 |
|
| 207 | void __init read_persistent_wall_and_boot_offset(struct timespec64 *wall_time,
|
| 208 | struct timespec64 *boot_offset)
|
| 209 | {
|
| 210 | struct timespec64 boot_time;
|
| 211 | union tod_clock clk;
|
| 212 | u64 delta;
|
| 213 |
|
| 214 | delta = initial_leap_seconds + TOD_UNIX_EPOCH;
|
| 215 | clk = tod_clock_base;
|
| 216 | clk.eitod -= delta;
|
| 217 | ext_to_timespec64(&clk, &boot_time);
|
| 218 |
|
| 219 | read_persistent_clock64(wall_time);
|
| 220 | *boot_offset = timespec64_sub(*wall_time, boot_time);
|
| 221 | }
|
| 222 |
|
| 223 | static u64 read_tod_clock(struct clocksource *cs)
|
| 224 | {
|
| 225 | return get_tod_clock_monotonic();
|
| 226 | }
|
| 227 |
|
| 228 | static struct clocksource clocksource_tod = {
|
| 229 | .name = "tod",
|
| 230 | .rating = 400,
|
| 231 | .read = read_tod_clock,
|
| 232 | .mask = CLOCKSOURCE_MASK(64),
|
| 233 | .mult = 4096000,
|
| 234 | .shift = 24,
|
| 235 | .flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
| 236 | .vdso_clock_mode = VDSO_CLOCKMODE_TOD,
|
| 237 | .id = CSID_S390_TOD,
|
| 238 | };
|
| 239 |
|
| 240 | struct clocksource * __init clocksource_default_clock(void)
|
| 241 | {
|
| 242 | return &clocksource_tod;
|
| 243 | }
|
| 244 |
|
| 245 | |
| 246 | * Initialize the TOD clock and the CPU timer of
|
| 247 | * the boot cpu.
|
| 248 |
|
| 249 | void __init time_init(void)
|
| 250 | {
|
| 251 |
|
| 252 | stp_reset();
|
| 253 |
|
| 254 |
|
| 255 | if (register_external_irq(EXT_IRQ_CLK_COMP, clock_comparator_interrupt))
|
| 256 | panic("Couldn't request external interrupt 0x1004");
|
| 257 |
|
| 258 |
|
| 259 | if (register_external_irq(EXT_IRQ_TIMING_ALERT, timing_alert_interrupt))
|
| 260 | panic("Couldn't request external interrupt 0x1406");
|
| 261 |
|
| 262 | if (__clocksource_register(&clocksource_tod) != 0)
|
| 263 | panic("Could not register TOD clock source");
|
| 264 |
|
| 265 |
|
| 266 | init_cpu_timer();
|
| 267 |
|
| 268 |
|
| 269 | vtime_init();
|
| 270 | }
|
| 271 |
|
| 272 | static DEFINE_PER_CPU(atomic_t, clock_sync_word);
|
| 273 | static DEFINE_MUTEX(stp_mutex);
|
| 274 | static unsigned long clock_sync_flags;
|
| 275 |
|
| 276 | #define CLOCK_SYNC_HAS_STP 0
|
| 277 | #define CLOCK_SYNC_STP 1
|
| 278 | #define CLOCK_SYNC_STPINFO_VALID 2
|
| 279 |
|
| 280 | |
| 281 | * The get_clock function for the physical clock. It will get the current
|
| 282 | * TOD clock, subtract the LPAR offset and write the result to *clock.
|
| 283 | * The function returns 0 if the clock is in sync with the external time
|
| 284 | * source. If the clock mode is local it will return -EOPNOTSUPP and
|
| 285 | * -EAGAIN if the clock is not in sync with the external reference.
|
| 286 |
|
| 287 | int get_phys_clock(unsigned long *clock)
|
| 288 | {
|
| 289 | atomic_t *sw_ptr;
|
| 290 | unsigned int sw0, sw1;
|
| 291 |
|
| 292 | sw_ptr = &get_cpu_var(clock_sync_word);
|
| 293 | sw0 = atomic_read(sw_ptr);
|
| 294 | *clock = get_tod_clock() - lpar_offset;
|
| 295 | sw1 = atomic_read(sw_ptr);
|
| 296 | put_cpu_var(clock_sync_word);
|
| 297 | if (sw0 == sw1 && (sw0 & 0x80000000U))
|
| 298 |
|
| 299 | return 0;
|
| 300 | if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
|
| 301 | return -EOPNOTSUPP;
|
| 302 | if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
|
| 303 | return -EACCES;
|
| 304 | return -EAGAIN;
|
| 305 | }
|
| 306 | EXPORT_SYMBOL(get_phys_clock);
|
| 307 |
|
| 308 | |
| 309 | * Make get_phys_clock() return -EAGAIN.
|
| 310 |
|
| 311 | static void disable_sync_clock(void *dummy)
|
| 312 | {
|
| 313 | atomic_t *sw_ptr = this_cpu_ptr(&clock_sync_word);
|
| 314 | |
| 315 | * Clear the in-sync bit 2^31. All get_phys_clock calls will
|
| 316 | * fail until the sync bit is turned back on. In addition
|
| 317 | * increase the "sequence" counter to avoid the race of an
|
| 318 | * stp event and the complete recovery against get_phys_clock.
|
| 319 |
|
| 320 | atomic_andnot(0x80000000, sw_ptr);
|
| 321 | atomic_inc(sw_ptr);
|
| 322 | }
|
| 323 |
|
| 324 | |
| 325 | * Make get_phys_clock() return 0 again.
|
| 326 | * Needs to be called from a context disabled for preemption.
|
| 327 |
|
| 328 | static void enable_sync_clock(void)
|
| 329 | {
|
| 330 | atomic_t *sw_ptr = this_cpu_ptr(&clock_sync_word);
|
| 331 | atomic_or(0x80000000, sw_ptr);
|
| 332 | }
|
| 333 |
|
| 334 | |
| 335 | * Function to check if the clock is in sync.
|
| 336 |
|
| 337 | static inline int check_sync_clock(void)
|
| 338 | {
|
| 339 | atomic_t *sw_ptr;
|
| 340 | int rc;
|
| 341 |
|
| 342 | sw_ptr = &get_cpu_var(clock_sync_word);
|
| 343 | rc = (atomic_read(sw_ptr) & 0x80000000U) != 0;
|
| 344 | put_cpu_var(clock_sync_word);
|
| 345 | return rc;
|
| 346 | }
|
| 347 |
|
| 348 | |
| 349 | * Apply clock delta to the global data structures.
|
| 350 | * This is called once on the CPU that performed the clock sync.
|
| 351 |
|
| 352 | static void clock_sync_global(long delta)
|
| 353 | {
|
| 354 | struct ptff_qto qto;
|
| 355 |
|
| 356 |
|
| 357 | tod_clock_base.eitod += delta;
|
| 358 | vdso_k_time_data->arch_data.tod_delta = tod_clock_base.tod;
|
| 359 |
|
| 360 | if (ptff_query(PTFF_QTO) && ptff(&qto, sizeof(qto), PTFF_QTO) == 0)
|
| 361 | lpar_offset = qto.tod_epoch_difference;
|
| 362 |
|
| 363 | atomic_notifier_call_chain(&s390_epoch_delta_notifier, 0, &delta);
|
| 364 | }
|
| 365 |
|
| 366 | |
| 367 | * Apply clock delta to the per-CPU data structures of this CPU.
|
| 368 | * This is called for each online CPU after the call to clock_sync_global.
|
| 369 |
|
| 370 | static void clock_sync_local(long delta)
|
| 371 | {
|
| 372 |
|
| 373 | if (get_lowcore()->clock_comparator != clock_comparator_max) {
|
| 374 | get_lowcore()->clock_comparator += delta;
|
| 375 | set_clock_comparator(get_lowcore()->clock_comparator);
|
| 376 | }
|
| 377 |
|
| 378 | get_lowcore()->last_update_clock += delta;
|
| 379 | }
|
| 380 |
|
| 381 |
|
| 382 | static struct workqueue_struct *time_sync_wq;
|
| 383 |
|
| 384 | static void __init time_init_wq(void)
|
| 385 | {
|
| 386 | if (time_sync_wq)
|
| 387 | return;
|
| 388 | time_sync_wq = create_singlethread_workqueue("timesync");
|
| 389 | }
|
| 390 |
|
| 391 | struct clock_sync_data {
|
| 392 | atomic_t cpus;
|
| 393 | int in_sync;
|
| 394 | long clock_delta;
|
| 395 | };
|
| 396 |
|
| 397 | |
| 398 | * Server Time Protocol (STP) code.
|
| 399 |
|
| 400 | static bool stp_online = true;
|
| 401 | static struct stp_sstpi stp_info;
|
| 402 | static void *stp_page;
|
| 403 |
|
| 404 | static void stp_work_fn(struct work_struct *work);
|
| 405 | static DECLARE_WORK(stp_work, stp_work_fn);
|
| 406 | static struct timer_list stp_timer;
|
| 407 |
|
| 408 | static int __init early_parse_stp(char *p)
|
| 409 | {
|
| 410 | return kstrtobool(p, &stp_online);
|
| 411 | }
|
| 412 | early_param("stp", early_parse_stp);
|
| 413 |
|
| 414 | |
| 415 | * Reset STP attachment.
|
| 416 |
|
| 417 | static void __init stp_reset(void)
|
| 418 | {
|
| 419 | int rc;
|
| 420 |
|
| 421 | stp_page = (void *) get_zeroed_page(GFP_ATOMIC);
|
| 422 | rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000, NULL);
|
| 423 | if (rc == 0)
|
| 424 | set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags);
|
| 425 | else if (stp_online) {
|
| 426 | free_page((unsigned long) stp_page);
|
| 427 | stp_page = NULL;
|
| 428 | stp_online = false;
|
| 429 | }
|
| 430 | }
|
| 431 |
|
| 432 | bool stp_enabled(void)
|
| 433 | {
|
| 434 | return test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags) && stp_online;
|
| 435 | }
|
| 436 | EXPORT_SYMBOL(stp_enabled);
|
| 437 |
|
| 438 | static void stp_timeout(struct timer_list *unused)
|
| 439 | {
|
| 440 | queue_work(time_sync_wq, &stp_work);
|
| 441 | }
|
| 442 |
|
| 443 | static int __init stp_init(void)
|
| 444 | {
|
| 445 | if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
|
| 446 | return 0;
|
| 447 | timer_setup(&stp_timer, stp_timeout, 0);
|
| 448 | time_init_wq();
|
| 449 | if (!stp_online)
|
| 450 | return 0;
|
| 451 | queue_work(time_sync_wq, &stp_work);
|
| 452 | return 0;
|
| 453 | }
|
| 454 |
|
| 455 | arch_initcall(stp_init);
|
| 456 |
|
| 457 | |
| 458 | * STP timing alert. There are three causes:
|
| 459 | * 1) timing status change
|
| 460 | * 2) link availability change
|
| 461 | * 3) time control parameter change
|
| 462 | * In all three cases we are only interested in the clock source state.
|
| 463 | * If a STP clock source is now available use it.
|
| 464 |
|
| 465 | static void stp_timing_alert(struct stp_irq_parm *intparm)
|
| 466 | {
|
| 467 | if (intparm->tsc || intparm->lac || intparm->tcpc)
|
| 468 | queue_work(time_sync_wq, &stp_work);
|
| 469 | }
|
| 470 |
|
| 471 | |
| 472 | * STP sync check machine check. This is called when the timing state
|
| 473 | * changes from the synchronized state to the unsynchronized state.
|
| 474 | * After a STP sync check the clock is not in sync. The machine check
|
| 475 | * is broadcasted to all cpus at the same time.
|
| 476 |
|
| 477 | int stp_sync_check(void)
|
| 478 | {
|
| 479 | disable_sync_clock(NULL);
|
| 480 | return 1;
|
| 481 | }
|
| 482 |
|
| 483 | |
| 484 | * STP island condition machine check. This is called when an attached
|
| 485 | * server attempts to communicate over an STP link and the servers
|
| 486 | * have matching CTN ids and have a valid stratum-1 configuration
|
| 487 | * but the configurations do not match.
|
| 488 |
|
| 489 | int stp_island_check(void)
|
| 490 | {
|
| 491 | disable_sync_clock(NULL);
|
| 492 | return 1;
|
| 493 | }
|
| 494 |
|
| 495 | void stp_queue_work(void)
|
| 496 | {
|
| 497 | queue_work(time_sync_wq, &stp_work);
|
| 498 | }
|
| 499 |
|
| 500 | static int __store_stpinfo(void)
|
| 501 | {
|
| 502 | int rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
|
| 503 |
|
| 504 | if (rc)
|
| 505 | clear_bit(CLOCK_SYNC_STPINFO_VALID, &clock_sync_flags);
|
| 506 | else
|
| 507 | set_bit(CLOCK_SYNC_STPINFO_VALID, &clock_sync_flags);
|
| 508 | return rc;
|
| 509 | }
|
| 510 |
|
| 511 | static int stpinfo_valid(void)
|
| 512 | {
|
| 513 | return stp_online && test_bit(CLOCK_SYNC_STPINFO_VALID, &clock_sync_flags);
|
| 514 | }
|
| 515 |
|
| 516 | static int stp_sync_clock(void *data)
|
| 517 | {
|
| 518 | struct clock_sync_data *sync = data;
|
| 519 | long clock_delta, flags;
|
| 520 | static int first;
|
| 521 | int rc;
|
| 522 |
|
| 523 | enable_sync_clock();
|
| 524 | if (xchg(&first, 1) == 0) {
|
| 525 |
|
| 526 | while (atomic_read(&sync->cpus) != 0)
|
| 527 | cpu_relax();
|
| 528 | rc = 0;
|
| 529 | if (stp_info.todoff || stp_info.tmd != 2) {
|
| 530 | flags = vdso_update_begin();
|
| 531 | rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0,
|
| 532 | &clock_delta);
|
| 533 | if (rc == 0) {
|
| 534 | sync->clock_delta = clock_delta;
|
| 535 | clock_sync_global(clock_delta);
|
| 536 | rc = __store_stpinfo();
|
| 537 | if (rc == 0 && stp_info.tmd != 2)
|
| 538 | rc = -EAGAIN;
|
| 539 | }
|
| 540 | vdso_update_end(flags);
|
| 541 | }
|
| 542 | sync->in_sync = rc ? -EAGAIN : 1;
|
| 543 | xchg(&first, 0);
|
| 544 | } else {
|
| 545 |
|
| 546 | atomic_dec(&sync->cpus);
|
| 547 |
|
| 548 | while (READ_ONCE(sync->in_sync) == 0)
|
| 549 | ;
|
| 550 | }
|
| 551 | if (sync->in_sync != 1)
|
| 552 |
|
| 553 | disable_sync_clock(NULL);
|
| 554 |
|
| 555 | clock_sync_local(sync->clock_delta);
|
| 556 |
|
| 557 | return 0;
|
| 558 | }
|
| 559 |
|
| 560 | |
| 561 | * STP work. Check for the STP state and take over the clock
|
| 562 | * synchronization if the STP clock source is usable.
|
| 563 |
|
| 564 | static void stp_work_fn(struct work_struct *work)
|
| 565 | {
|
| 566 | struct clock_sync_data stp_sync;
|
| 567 | int rc;
|
| 568 |
|
| 569 |
|
| 570 | mutex_lock(&stp_mutex);
|
| 571 |
|
| 572 | if (!stp_online) {
|
| 573 | chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000, NULL);
|
| 574 | timer_delete_sync(&stp_timer);
|
| 575 | goto out_unlock;
|
| 576 | }
|
| 577 |
|
| 578 | rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xf0e0, NULL);
|
| 579 | if (rc)
|
| 580 | goto out_unlock;
|
| 581 |
|
| 582 | rc = __store_stpinfo();
|
| 583 | if (rc || stp_info.c == 0)
|
| 584 | goto out_unlock;
|
| 585 |
|
| 586 |
|
| 587 | if (!check_sync_clock()) {
|
| 588 | memset(&stp_sync, 0, sizeof(stp_sync));
|
| 589 | cpus_read_lock();
|
| 590 | atomic_set(&stp_sync.cpus, num_online_cpus() - 1);
|
| 591 | stop_machine_cpuslocked(stp_sync_clock, &stp_sync, cpu_online_mask);
|
| 592 | cpus_read_unlock();
|
| 593 | }
|
| 594 |
|
| 595 | if (!check_sync_clock())
|
| 596 | |
| 597 | * There is a usable clock but the synchronization failed.
|
| 598 | * Retry after a second.
|
| 599 |
|
| 600 | mod_timer(&stp_timer, jiffies + msecs_to_jiffies(MSEC_PER_SEC));
|
| 601 |
|
| 602 | out_unlock:
|
| 603 | mutex_unlock(&stp_mutex);
|
| 604 | }
|
| 605 |
|
| 606 | |
| 607 | * STP subsys sysfs interface functions
|
| 608 |
|
| 609 | static const struct bus_type stp_subsys = {
|
| 610 | .name = "stp",
|
| 611 | .dev_name = "stp",
|
| 612 | };
|
| 613 |
|
| 614 | static ssize_t ctn_id_show(struct device *dev,
|
| 615 | struct device_attribute *attr,
|
| 616 | char *buf)
|
| 617 | {
|
| 618 | ssize_t ret = -ENODATA;
|
| 619 |
|
| 620 | mutex_lock(&stp_mutex);
|
| 621 | if (stpinfo_valid())
|
| 622 | ret = sysfs_emit(buf, "%016lx\n",
|
| 623 | *(unsigned long *)stp_info.ctnid);
|
| 624 | mutex_unlock(&stp_mutex);
|
| 625 | return ret;
|
| 626 | }
|
| 627 |
|
| 628 | static DEVICE_ATTR_RO(ctn_id);
|
| 629 |
|
| 630 | static ssize_t ctn_type_show(struct device *dev,
|
| 631 | struct device_attribute *attr,
|
| 632 | char *buf)
|
| 633 | {
|
| 634 | ssize_t ret = -ENODATA;
|
| 635 |
|
| 636 | mutex_lock(&stp_mutex);
|
| 637 | if (stpinfo_valid())
|
| 638 | ret = sysfs_emit(buf, "%i\n", stp_info.ctn);
|
| 639 | mutex_unlock(&stp_mutex);
|
| 640 | return ret;
|
| 641 | }
|
| 642 |
|
| 643 | static DEVICE_ATTR_RO(ctn_type);
|
| 644 |
|
| 645 | static ssize_t dst_offset_show(struct device *dev,
|
| 646 | struct device_attribute *attr,
|
| 647 | char *buf)
|
| 648 | {
|
| 649 | ssize_t ret = -ENODATA;
|
| 650 |
|
| 651 | mutex_lock(&stp_mutex);
|
| 652 | if (stpinfo_valid() && (stp_info.vbits & 0x2000))
|
| 653 | ret = sysfs_emit(buf, "%i\n", (int)(s16)stp_info.dsto);
|
| 654 | mutex_unlock(&stp_mutex);
|
| 655 | return ret;
|
| 656 | }
|
| 657 |
|
| 658 | static DEVICE_ATTR_RO(dst_offset);
|
| 659 |
|
| 660 | static ssize_t leap_seconds_show(struct device *dev,
|
| 661 | struct device_attribute *attr,
|
| 662 | char *buf)
|
| 663 | {
|
| 664 | ssize_t ret = -ENODATA;
|
| 665 |
|
| 666 | mutex_lock(&stp_mutex);
|
| 667 | if (stpinfo_valid() && (stp_info.vbits & 0x8000))
|
| 668 | ret = sysfs_emit(buf, "%i\n", (int)(s16)stp_info.leaps);
|
| 669 | mutex_unlock(&stp_mutex);
|
| 670 | return ret;
|
| 671 | }
|
| 672 |
|
| 673 | static DEVICE_ATTR_RO(leap_seconds);
|
| 674 |
|
| 675 | static ssize_t leap_seconds_scheduled_show(struct device *dev,
|
| 676 | struct device_attribute *attr,
|
| 677 | char *buf)
|
| 678 | {
|
| 679 | struct stp_stzi stzi;
|
| 680 | ssize_t ret;
|
| 681 |
|
| 682 | mutex_lock(&stp_mutex);
|
| 683 | if (!stpinfo_valid() || !(stp_info.vbits & 0x8000) || !stp_info.lu) {
|
| 684 | mutex_unlock(&stp_mutex);
|
| 685 | return -ENODATA;
|
| 686 | }
|
| 687 |
|
| 688 | ret = chsc_stzi(stp_page, &stzi, sizeof(stzi));
|
| 689 | mutex_unlock(&stp_mutex);
|
| 690 | if (ret < 0)
|
| 691 | return ret;
|
| 692 |
|
| 693 | if (!stzi.lsoib.p)
|
| 694 | return sysfs_emit(buf, "0,0\n");
|
| 695 |
|
| 696 | return sysfs_emit(buf, "%lu,%d\n",
|
| 697 | tod_to_ns(stzi.lsoib.nlsout - TOD_UNIX_EPOCH) / NSEC_PER_SEC,
|
| 698 | stzi.lsoib.nlso - stzi.lsoib.also);
|
| 699 | }
|
| 700 |
|
| 701 | static DEVICE_ATTR_RO(leap_seconds_scheduled);
|
| 702 |
|
| 703 | static ssize_t stratum_show(struct device *dev,
|
| 704 | struct device_attribute *attr,
|
| 705 | char *buf)
|
| 706 | {
|
| 707 | ssize_t ret = -ENODATA;
|
| 708 |
|
| 709 | mutex_lock(&stp_mutex);
|
| 710 | if (stpinfo_valid())
|
| 711 | ret = sysfs_emit(buf, "%i\n", (int)(s16)stp_info.stratum);
|
| 712 | mutex_unlock(&stp_mutex);
|
| 713 | return ret;
|
| 714 | }
|
| 715 |
|
| 716 | static DEVICE_ATTR_RO(stratum);
|
| 717 |
|
| 718 | static ssize_t time_offset_show(struct device *dev,
|
| 719 | struct device_attribute *attr,
|
| 720 | char *buf)
|
| 721 | {
|
| 722 | ssize_t ret = -ENODATA;
|
| 723 |
|
| 724 | mutex_lock(&stp_mutex);
|
| 725 | if (stpinfo_valid() && (stp_info.vbits & 0x0800))
|
| 726 | ret = sysfs_emit(buf, "%i\n", (int)stp_info.tto);
|
| 727 | mutex_unlock(&stp_mutex);
|
| 728 | return ret;
|
| 729 | }
|
| 730 |
|
| 731 | static DEVICE_ATTR_RO(time_offset);
|
| 732 |
|
| 733 | static ssize_t time_zone_offset_show(struct device *dev,
|
| 734 | struct device_attribute *attr,
|
| 735 | char *buf)
|
| 736 | {
|
| 737 | ssize_t ret = -ENODATA;
|
| 738 |
|
| 739 | mutex_lock(&stp_mutex);
|
| 740 | if (stpinfo_valid() && (stp_info.vbits & 0x4000))
|
| 741 | ret = sysfs_emit(buf, "%i\n", (int)(s16)stp_info.tzo);
|
| 742 | mutex_unlock(&stp_mutex);
|
| 743 | return ret;
|
| 744 | }
|
| 745 |
|
| 746 | static DEVICE_ATTR_RO(time_zone_offset);
|
| 747 |
|
| 748 | static ssize_t timing_mode_show(struct device *dev,
|
| 749 | struct device_attribute *attr,
|
| 750 | char *buf)
|
| 751 | {
|
| 752 | ssize_t ret = -ENODATA;
|
| 753 |
|
| 754 | mutex_lock(&stp_mutex);
|
| 755 | if (stpinfo_valid())
|
| 756 | ret = sysfs_emit(buf, "%i\n", stp_info.tmd);
|
| 757 | mutex_unlock(&stp_mutex);
|
| 758 | return ret;
|
| 759 | }
|
| 760 |
|
| 761 | static DEVICE_ATTR_RO(timing_mode);
|
| 762 |
|
| 763 | static ssize_t timing_state_show(struct device *dev,
|
| 764 | struct device_attribute *attr,
|
| 765 | char *buf)
|
| 766 | {
|
| 767 | ssize_t ret = -ENODATA;
|
| 768 |
|
| 769 | mutex_lock(&stp_mutex);
|
| 770 | if (stpinfo_valid())
|
| 771 | ret = sysfs_emit(buf, "%i\n", stp_info.tst);
|
| 772 | mutex_unlock(&stp_mutex);
|
| 773 | return ret;
|
| 774 | }
|
| 775 |
|
| 776 | static DEVICE_ATTR_RO(timing_state);
|
| 777 |
|
| 778 | static ssize_t online_show(struct device *dev,
|
| 779 | struct device_attribute *attr,
|
| 780 | char *buf)
|
| 781 | {
|
| 782 | return sysfs_emit(buf, "%i\n", stp_online);
|
| 783 | }
|
| 784 |
|
| 785 | static ssize_t online_store(struct device *dev,
|
| 786 | struct device_attribute *attr,
|
| 787 | const char *buf, size_t count)
|
| 788 | {
|
| 789 | unsigned int value;
|
| 790 |
|
| 791 | value = simple_strtoul(buf, NULL, 0);
|
| 792 | if (value != 0 && value != 1)
|
| 793 | return -EINVAL;
|
| 794 | if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
|
| 795 | return -EOPNOTSUPP;
|
| 796 | mutex_lock(&stp_mutex);
|
| 797 | stp_online = value;
|
| 798 | if (stp_online)
|
| 799 | set_bit(CLOCK_SYNC_STP, &clock_sync_flags);
|
| 800 | else
|
| 801 | clear_bit(CLOCK_SYNC_STP, &clock_sync_flags);
|
| 802 | queue_work(time_sync_wq, &stp_work);
|
| 803 | mutex_unlock(&stp_mutex);
|
| 804 | return count;
|
| 805 | }
|
| 806 |
|
| 807 | |
| 808 | * Can't use DEVICE_ATTR because the attribute should be named
|
| 809 | * stp/online but dev_attr_online already exists in this file ..
|
| 810 |
|
| 811 | static DEVICE_ATTR_RW(online);
|
| 812 |
|
| 813 | static struct attribute *stp_dev_attrs[] = {
|
| 814 | &dev_attr_ctn_id.attr,
|
| 815 | &dev_attr_ctn_type.attr,
|
| 816 | &dev_attr_dst_offset.attr,
|
| 817 | &dev_attr_leap_seconds.attr,
|
| 818 | &dev_attr_online.attr,
|
| 819 | &dev_attr_leap_seconds_scheduled.attr,
|
| 820 | &dev_attr_stratum.attr,
|
| 821 | &dev_attr_time_offset.attr,
|
| 822 | &dev_attr_time_zone_offset.attr,
|
| 823 | &dev_attr_timing_mode.attr,
|
| 824 | &dev_attr_timing_state.attr,
|
| 825 | NULL
|
| 826 | };
|
| 827 | ATTRIBUTE_GROUPS(stp_dev);
|
| 828 |
|
| 829 | static int __init stp_init_sysfs(void)
|
| 830 | {
|
| 831 | return subsys_system_register(&stp_subsys, stp_dev_groups);
|
| 832 | }
|
| 833 |
|
| 834 | device_initcall(stp_init_sysfs);
|