162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SGI RTC clock/timer routines. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright 2020 Hewlett Packard Enterprise Development LP 662306a36Sopenharmony_ci * Copyright (c) 2009-2013 Silicon Graphics, Inc. All Rights Reserved. 762306a36Sopenharmony_ci * Copyright (c) Dimitri Sivanich 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/clockchips.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/uv/uv_mmrs.h> 1362306a36Sopenharmony_ci#include <asm/uv/uv_hub.h> 1462306a36Sopenharmony_ci#include <asm/uv/bios.h> 1562306a36Sopenharmony_ci#include <asm/uv/uv.h> 1662306a36Sopenharmony_ci#include <asm/apic.h> 1762306a36Sopenharmony_ci#include <asm/cpu.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define RTC_NAME "sgi_rtc" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic u64 uv_read_rtc(struct clocksource *cs); 2262306a36Sopenharmony_cistatic int uv_rtc_next_event(unsigned long, struct clock_event_device *); 2362306a36Sopenharmony_cistatic int uv_rtc_shutdown(struct clock_event_device *evt); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic struct clocksource clocksource_uv = { 2662306a36Sopenharmony_ci .name = RTC_NAME, 2762306a36Sopenharmony_ci .rating = 299, 2862306a36Sopenharmony_ci .read = uv_read_rtc, 2962306a36Sopenharmony_ci .mask = (u64)UVH_RTC_REAL_TIME_CLOCK_MASK, 3062306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct clock_event_device clock_event_device_uv = { 3462306a36Sopenharmony_ci .name = RTC_NAME, 3562306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT, 3662306a36Sopenharmony_ci .shift = 20, 3762306a36Sopenharmony_ci .rating = 400, 3862306a36Sopenharmony_ci .irq = -1, 3962306a36Sopenharmony_ci .set_next_event = uv_rtc_next_event, 4062306a36Sopenharmony_ci .set_state_shutdown = uv_rtc_shutdown, 4162306a36Sopenharmony_ci .event_handler = NULL, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct clock_event_device, cpu_ced); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* There is one of these allocated per node */ 4762306a36Sopenharmony_cistruct uv_rtc_timer_head { 4862306a36Sopenharmony_ci spinlock_t lock; 4962306a36Sopenharmony_ci /* next cpu waiting for timer, local node relative: */ 5062306a36Sopenharmony_ci int next_cpu; 5162306a36Sopenharmony_ci /* number of cpus on this node: */ 5262306a36Sopenharmony_ci int ncpus; 5362306a36Sopenharmony_ci struct { 5462306a36Sopenharmony_ci int lcpu; /* systemwide logical cpu number */ 5562306a36Sopenharmony_ci u64 expires; /* next timer expiration for this cpu */ 5662306a36Sopenharmony_ci } cpu[]; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * Access to uv_rtc_timer_head via blade id. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic struct uv_rtc_timer_head **blade_info __read_mostly; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int uv_rtc_evt_enable; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Hardware interface routines 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Send IPIs to another node */ 7162306a36Sopenharmony_cistatic void uv_rtc_send_IPI(int cpu) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned long apicid, val; 7462306a36Sopenharmony_ci int pnode; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci apicid = cpu_physical_id(cpu); 7762306a36Sopenharmony_ci pnode = uv_apicid_to_pnode(apicid); 7862306a36Sopenharmony_ci val = (1UL << UVH_IPI_INT_SEND_SHFT) | 7962306a36Sopenharmony_ci (apicid << UVH_IPI_INT_APIC_ID_SHFT) | 8062306a36Sopenharmony_ci (X86_PLATFORM_IPI_VECTOR << UVH_IPI_INT_VECTOR_SHFT); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci uv_write_global_mmr64(pnode, UVH_IPI_INT, val); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Check for an RTC interrupt pending */ 8662306a36Sopenharmony_cistatic int uv_intr_pending(int pnode) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return uv_read_global_mmr64(pnode, UVH_EVENT_OCCURRED2) & 8962306a36Sopenharmony_ci UVH_EVENT_OCCURRED2_RTC_1_MASK; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* Setup interrupt and return non-zero if early expiration occurred. */ 9362306a36Sopenharmony_cistatic int uv_setup_intr(int cpu, u64 expires) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci u64 val; 9662306a36Sopenharmony_ci unsigned long apicid = cpu_physical_id(cpu); 9762306a36Sopenharmony_ci int pnode = uv_cpu_to_pnode(cpu); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, 10062306a36Sopenharmony_ci UVH_RTC1_INT_CONFIG_M_MASK); 10162306a36Sopenharmony_ci uv_write_global_mmr64(pnode, UVH_INT_CMPB, -1L); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci uv_write_global_mmr64(pnode, UVH_EVENT_OCCURRED2_ALIAS, 10462306a36Sopenharmony_ci UVH_EVENT_OCCURRED2_RTC_1_MASK); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci val = (X86_PLATFORM_IPI_VECTOR << UVH_RTC1_INT_CONFIG_VECTOR_SHFT) | 10762306a36Sopenharmony_ci ((u64)apicid << UVH_RTC1_INT_CONFIG_APIC_ID_SHFT); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Set configuration */ 11062306a36Sopenharmony_ci uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, val); 11162306a36Sopenharmony_ci /* Initialize comparator value */ 11262306a36Sopenharmony_ci uv_write_global_mmr64(pnode, UVH_INT_CMPB, expires); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (uv_read_rtc(NULL) <= expires) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return !uv_intr_pending(pnode); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * Per-cpu timer tracking routines 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic __init void uv_rtc_deallocate_timers(void) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci int bid; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci for_each_possible_blade(bid) { 12962306a36Sopenharmony_ci kfree(blade_info[bid]); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci kfree(blade_info); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Allocate per-node list of cpu timer expiration times. */ 13562306a36Sopenharmony_cistatic __init int uv_rtc_allocate_timers(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int cpu; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci blade_info = kcalloc(uv_possible_blades, sizeof(void *), GFP_KERNEL); 14062306a36Sopenharmony_ci if (!blade_info) 14162306a36Sopenharmony_ci return -ENOMEM; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci for_each_present_cpu(cpu) { 14462306a36Sopenharmony_ci int nid = cpu_to_node(cpu); 14562306a36Sopenharmony_ci int bid = uv_cpu_to_blade_id(cpu); 14662306a36Sopenharmony_ci int bcpu = uv_cpu_blade_processor_id(cpu); 14762306a36Sopenharmony_ci struct uv_rtc_timer_head *head = blade_info[bid]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (!head) { 15062306a36Sopenharmony_ci head = kmalloc_node(struct_size(head, cpu, 15162306a36Sopenharmony_ci uv_blade_nr_possible_cpus(bid)), 15262306a36Sopenharmony_ci GFP_KERNEL, nid); 15362306a36Sopenharmony_ci if (!head) { 15462306a36Sopenharmony_ci uv_rtc_deallocate_timers(); 15562306a36Sopenharmony_ci return -ENOMEM; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci spin_lock_init(&head->lock); 15862306a36Sopenharmony_ci head->ncpus = uv_blade_nr_possible_cpus(bid); 15962306a36Sopenharmony_ci head->next_cpu = -1; 16062306a36Sopenharmony_ci blade_info[bid] = head; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci head->cpu[bcpu].lcpu = cpu; 16462306a36Sopenharmony_ci head->cpu[bcpu].expires = ULLONG_MAX; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* Find and set the next expiring timer. */ 17162306a36Sopenharmony_cistatic void uv_rtc_find_next_timer(struct uv_rtc_timer_head *head, int pnode) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u64 lowest = ULLONG_MAX; 17462306a36Sopenharmony_ci int c, bcpu = -1; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci head->next_cpu = -1; 17762306a36Sopenharmony_ci for (c = 0; c < head->ncpus; c++) { 17862306a36Sopenharmony_ci u64 exp = head->cpu[c].expires; 17962306a36Sopenharmony_ci if (exp < lowest) { 18062306a36Sopenharmony_ci bcpu = c; 18162306a36Sopenharmony_ci lowest = exp; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci if (bcpu >= 0) { 18562306a36Sopenharmony_ci head->next_cpu = bcpu; 18662306a36Sopenharmony_ci c = head->cpu[bcpu].lcpu; 18762306a36Sopenharmony_ci if (uv_setup_intr(c, lowest)) 18862306a36Sopenharmony_ci /* If we didn't set it up in time, trigger */ 18962306a36Sopenharmony_ci uv_rtc_send_IPI(c); 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, 19262306a36Sopenharmony_ci UVH_RTC1_INT_CONFIG_M_MASK); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * Set expiration time for current cpu. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Returns 1 if we missed the expiration time. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cistatic int uv_rtc_set_timer(int cpu, u64 expires) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int pnode = uv_cpu_to_pnode(cpu); 20462306a36Sopenharmony_ci int bid = uv_cpu_to_blade_id(cpu); 20562306a36Sopenharmony_ci struct uv_rtc_timer_head *head = blade_info[bid]; 20662306a36Sopenharmony_ci int bcpu = uv_cpu_blade_processor_id(cpu); 20762306a36Sopenharmony_ci u64 *t = &head->cpu[bcpu].expires; 20862306a36Sopenharmony_ci unsigned long flags; 20962306a36Sopenharmony_ci int next_cpu; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci spin_lock_irqsave(&head->lock, flags); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci next_cpu = head->next_cpu; 21462306a36Sopenharmony_ci *t = expires; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Will this one be next to go off? */ 21762306a36Sopenharmony_ci if (next_cpu < 0 || bcpu == next_cpu || 21862306a36Sopenharmony_ci expires < head->cpu[next_cpu].expires) { 21962306a36Sopenharmony_ci head->next_cpu = bcpu; 22062306a36Sopenharmony_ci if (uv_setup_intr(cpu, expires)) { 22162306a36Sopenharmony_ci *t = ULLONG_MAX; 22262306a36Sopenharmony_ci uv_rtc_find_next_timer(head, pnode); 22362306a36Sopenharmony_ci spin_unlock_irqrestore(&head->lock, flags); 22462306a36Sopenharmony_ci return -ETIME; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci spin_unlock_irqrestore(&head->lock, flags); 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * Unset expiration time for current cpu. 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * Returns 1 if this timer was pending. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cistatic int uv_rtc_unset_timer(int cpu, int force) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci int pnode = uv_cpu_to_pnode(cpu); 24062306a36Sopenharmony_ci int bid = uv_cpu_to_blade_id(cpu); 24162306a36Sopenharmony_ci struct uv_rtc_timer_head *head = blade_info[bid]; 24262306a36Sopenharmony_ci int bcpu = uv_cpu_blade_processor_id(cpu); 24362306a36Sopenharmony_ci u64 *t = &head->cpu[bcpu].expires; 24462306a36Sopenharmony_ci unsigned long flags; 24562306a36Sopenharmony_ci int rc = 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci spin_lock_irqsave(&head->lock, flags); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if ((head->next_cpu == bcpu && uv_read_rtc(NULL) >= *t) || force) 25062306a36Sopenharmony_ci rc = 1; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (rc) { 25362306a36Sopenharmony_ci *t = ULLONG_MAX; 25462306a36Sopenharmony_ci /* Was the hardware setup for this timer? */ 25562306a36Sopenharmony_ci if (head->next_cpu == bcpu) 25662306a36Sopenharmony_ci uv_rtc_find_next_timer(head, pnode); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_unlock_irqrestore(&head->lock, flags); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return rc; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci/* 26662306a36Sopenharmony_ci * Kernel interface routines. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* 27062306a36Sopenharmony_ci * Read the RTC. 27162306a36Sopenharmony_ci * 27262306a36Sopenharmony_ci * Starting with HUB rev 2.0, the UV RTC register is replicated across all 27362306a36Sopenharmony_ci * cachelines of it's own page. This allows faster simultaneous reads 27462306a36Sopenharmony_ci * from a given socket. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic u64 uv_read_rtc(struct clocksource *cs) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci unsigned long offset; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (uv_get_min_hub_revision_id() == 1) 28162306a36Sopenharmony_ci offset = 0; 28262306a36Sopenharmony_ci else 28362306a36Sopenharmony_ci offset = (uv_blade_processor_id() * L1_CACHE_BYTES) % PAGE_SIZE; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return (u64)uv_read_local_mmr(UVH_RTC | offset); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * Program the next event, relative to now 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic int uv_rtc_next_event(unsigned long delta, 29262306a36Sopenharmony_ci struct clock_event_device *ced) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int ced_cpu = cpumask_first(ced->cpumask); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return uv_rtc_set_timer(ced_cpu, delta + uv_read_rtc(NULL)); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * Shutdown the RTC timer 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic int uv_rtc_shutdown(struct clock_event_device *evt) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int ced_cpu = cpumask_first(evt->cpumask); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci uv_rtc_unset_timer(ced_cpu, 1); 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void uv_rtc_interrupt(void) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci int cpu = smp_processor_id(); 31362306a36Sopenharmony_ci struct clock_event_device *ced = &per_cpu(cpu_ced, cpu); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!ced || !ced->event_handler) 31662306a36Sopenharmony_ci return; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (uv_rtc_unset_timer(cpu, 0) != 1) 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ced->event_handler(ced); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int __init uv_enable_evt_rtc(char *str) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci uv_rtc_evt_enable = 1; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return 1; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci__setup("uvrtcevt", uv_enable_evt_rtc); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic __init void uv_rtc_register_clockevents(struct work_struct *dummy) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct clock_event_device *ced = this_cpu_ptr(&cpu_ced); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci *ced = clock_event_device_uv; 33762306a36Sopenharmony_ci ced->cpumask = cpumask_of(smp_processor_id()); 33862306a36Sopenharmony_ci clockevents_register_device(ced); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic __init int uv_rtc_setup_clock(void) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci int rc; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!is_uv_system()) 34662306a36Sopenharmony_ci return -ENODEV; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci rc = clocksource_register_hz(&clocksource_uv, sn_rtc_cycles_per_second); 34962306a36Sopenharmony_ci if (rc) 35062306a36Sopenharmony_ci printk(KERN_INFO "UV RTC clocksource failed rc %d\n", rc); 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci printk(KERN_INFO "UV RTC clocksource registered freq %lu MHz\n", 35362306a36Sopenharmony_ci sn_rtc_cycles_per_second/(unsigned long)1E6); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (rc || !uv_rtc_evt_enable || x86_platform_ipi_callback) 35662306a36Sopenharmony_ci return rc; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Setup and register clockevents */ 35962306a36Sopenharmony_ci rc = uv_rtc_allocate_timers(); 36062306a36Sopenharmony_ci if (rc) 36162306a36Sopenharmony_ci goto error; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci x86_platform_ipi_callback = uv_rtc_interrupt; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci clock_event_device_uv.mult = div_sc(sn_rtc_cycles_per_second, 36662306a36Sopenharmony_ci NSEC_PER_SEC, clock_event_device_uv.shift); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci clock_event_device_uv.min_delta_ns = NSEC_PER_SEC / 36962306a36Sopenharmony_ci sn_rtc_cycles_per_second; 37062306a36Sopenharmony_ci clock_event_device_uv.min_delta_ticks = 1; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci clock_event_device_uv.max_delta_ns = clocksource_uv.mask * 37362306a36Sopenharmony_ci (NSEC_PER_SEC / sn_rtc_cycles_per_second); 37462306a36Sopenharmony_ci clock_event_device_uv.max_delta_ticks = clocksource_uv.mask; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci rc = schedule_on_each_cpu(uv_rtc_register_clockevents); 37762306a36Sopenharmony_ci if (rc) { 37862306a36Sopenharmony_ci x86_platform_ipi_callback = NULL; 37962306a36Sopenharmony_ci uv_rtc_deallocate_timers(); 38062306a36Sopenharmony_ci goto error; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci printk(KERN_INFO "UV RTC clockevents registered\n"); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cierror: 38862306a36Sopenharmony_ci clocksource_unregister(&clocksource_uv); 38962306a36Sopenharmony_ci printk(KERN_INFO "UV RTC clockevents failed rc %d\n", rc); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return rc; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ciarch_initcall(uv_rtc_setup_clock); 394