1*1802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23bd69ad1SAndreas Eversberg /* 33bd69ad1SAndreas Eversberg * Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu> 43bd69ad1SAndreas Eversberg * 53bd69ad1SAndreas Eversberg * Quick API description: 63bd69ad1SAndreas Eversberg * 73bd69ad1SAndreas Eversberg * A clock source registers using mISDN_register_clock: 83bd69ad1SAndreas Eversberg * name = text string to name clock source 93bd69ad1SAndreas Eversberg * priority = value to priorize clock sources (0 = default) 103bd69ad1SAndreas Eversberg * ctl = callback function to enable/disable clock source 113bd69ad1SAndreas Eversberg * priv = private pointer of clock source 123bd69ad1SAndreas Eversberg * return = pointer to clock source structure; 133bd69ad1SAndreas Eversberg * 143bd69ad1SAndreas Eversberg * Note: Callback 'ctl' can be called before mISDN_register_clock returns! 153bd69ad1SAndreas Eversberg * Also it can be called during mISDN_unregister_clock. 163bd69ad1SAndreas Eversberg * 173bd69ad1SAndreas Eversberg * A clock source calls mISDN_clock_update with given samples elapsed, if 183bd69ad1SAndreas Eversberg * enabled. If function call is delayed, tv must be set with the timestamp 193bd69ad1SAndreas Eversberg * of the actual event. 203bd69ad1SAndreas Eversberg * 213bd69ad1SAndreas Eversberg * A clock source unregisters using mISDN_unregister_clock. 223bd69ad1SAndreas Eversberg * 233bd69ad1SAndreas Eversberg * To get current clock, call mISDN_clock_get. The signed short value 243bd69ad1SAndreas Eversberg * counts the number of samples since. Time since last clock event is added. 253bd69ad1SAndreas Eversberg */ 263bd69ad1SAndreas Eversberg 275a0e3ad6STejun Heo #include <linux/slab.h> 283bd69ad1SAndreas Eversberg #include <linux/types.h> 293bd69ad1SAndreas Eversberg #include <linux/stddef.h> 303bd69ad1SAndreas Eversberg #include <linux/spinlock.h> 31ebf918cfSTina Ruchandani #include <linux/ktime.h> 323bd69ad1SAndreas Eversberg #include <linux/mISDNif.h> 335d76fc21SPaul Gortmaker #include <linux/export.h> 343bd69ad1SAndreas Eversberg #include "core.h" 353bd69ad1SAndreas Eversberg 363bd69ad1SAndreas Eversberg static u_int *debug; 373bd69ad1SAndreas Eversberg static LIST_HEAD(iclock_list); 38f8532fdeSHannes Eder static DEFINE_RWLOCK(iclock_lock); 39f8532fdeSHannes Eder static u16 iclock_count; /* counter of last clock */ 40ebf918cfSTina Ruchandani static ktime_t iclock_timestamp; /* time stamp of last clock */ 41ebf918cfSTina Ruchandani static int iclock_timestamp_valid; /* already received one timestamp */ 42f8532fdeSHannes Eder static struct mISDNclock *iclock_current; 433bd69ad1SAndreas Eversberg 443bd69ad1SAndreas Eversberg void 453bd69ad1SAndreas Eversberg mISDN_init_clock(u_int *dp) 463bd69ad1SAndreas Eversberg { 473bd69ad1SAndreas Eversberg debug = dp; 48ebf918cfSTina Ruchandani iclock_timestamp = ktime_get(); 493bd69ad1SAndreas Eversberg } 503bd69ad1SAndreas Eversberg 513bd69ad1SAndreas Eversberg static void 523bd69ad1SAndreas Eversberg select_iclock(void) 533bd69ad1SAndreas Eversberg { 543bd69ad1SAndreas Eversberg struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; 553bd69ad1SAndreas Eversberg int pri = -128; 563bd69ad1SAndreas Eversberg 573bd69ad1SAndreas Eversberg list_for_each_entry(iclock, &iclock_list, list) { 583bd69ad1SAndreas Eversberg if (iclock->pri > pri) { 593bd69ad1SAndreas Eversberg pri = iclock->pri; 603bd69ad1SAndreas Eversberg bestclock = iclock; 613bd69ad1SAndreas Eversberg } 623bd69ad1SAndreas Eversberg if (iclock_current == iclock) 633bd69ad1SAndreas Eversberg lastclock = iclock; 643bd69ad1SAndreas Eversberg } 653bd69ad1SAndreas Eversberg if (lastclock && bestclock != lastclock) { 663bd69ad1SAndreas Eversberg /* last used clock source still exists but changes, disable */ 673bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 683bd69ad1SAndreas Eversberg printk(KERN_DEBUG "Old clock source '%s' disable.\n", 693bd69ad1SAndreas Eversberg lastclock->name); 703bd69ad1SAndreas Eversberg lastclock->ctl(lastclock->priv, 0); 713bd69ad1SAndreas Eversberg } 723bd69ad1SAndreas Eversberg if (bestclock && bestclock != iclock_current) { 733bd69ad1SAndreas Eversberg /* new clock source selected, enable */ 743bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 753bd69ad1SAndreas Eversberg printk(KERN_DEBUG "New clock source '%s' enable.\n", 763bd69ad1SAndreas Eversberg bestclock->name); 773bd69ad1SAndreas Eversberg bestclock->ctl(bestclock->priv, 1); 783bd69ad1SAndreas Eversberg } 793bd69ad1SAndreas Eversberg if (bestclock != iclock_current) { 803bd69ad1SAndreas Eversberg /* no clock received yet */ 81ebf918cfSTina Ruchandani iclock_timestamp_valid = 0; 823bd69ad1SAndreas Eversberg } 833bd69ad1SAndreas Eversberg iclock_current = bestclock; 843bd69ad1SAndreas Eversberg } 853bd69ad1SAndreas Eversberg 863bd69ad1SAndreas Eversberg struct mISDNclock 873bd69ad1SAndreas Eversberg *mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) 883bd69ad1SAndreas Eversberg { 893bd69ad1SAndreas Eversberg u_long flags; 903bd69ad1SAndreas Eversberg struct mISDNclock *iclock; 913bd69ad1SAndreas Eversberg 923bd69ad1SAndreas Eversberg if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 933bd69ad1SAndreas Eversberg printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); 943bd69ad1SAndreas Eversberg iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC); 953bd69ad1SAndreas Eversberg if (!iclock) { 963bd69ad1SAndreas Eversberg printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); 973bd69ad1SAndreas Eversberg return NULL; 983bd69ad1SAndreas Eversberg } 993bd69ad1SAndreas Eversberg strncpy(iclock->name, name, sizeof(iclock->name) - 1); 1003bd69ad1SAndreas Eversberg iclock->pri = pri; 1013bd69ad1SAndreas Eversberg iclock->priv = priv; 1023bd69ad1SAndreas Eversberg iclock->ctl = ctl; 1033bd69ad1SAndreas Eversberg write_lock_irqsave(&iclock_lock, flags); 1043bd69ad1SAndreas Eversberg list_add_tail(&iclock->list, &iclock_list); 1053bd69ad1SAndreas Eversberg select_iclock(); 1063bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1073bd69ad1SAndreas Eversberg return iclock; 1083bd69ad1SAndreas Eversberg } 1093bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_register_clock); 1103bd69ad1SAndreas Eversberg 1113bd69ad1SAndreas Eversberg void 1123bd69ad1SAndreas Eversberg mISDN_unregister_clock(struct mISDNclock *iclock) 1133bd69ad1SAndreas Eversberg { 1143bd69ad1SAndreas Eversberg u_long flags; 1153bd69ad1SAndreas Eversberg 1163bd69ad1SAndreas Eversberg if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) 1173bd69ad1SAndreas Eversberg printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, 1183bd69ad1SAndreas Eversberg iclock->pri); 1193bd69ad1SAndreas Eversberg write_lock_irqsave(&iclock_lock, flags); 1203bd69ad1SAndreas Eversberg if (iclock_current == iclock) { 1213bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 1223bd69ad1SAndreas Eversberg printk(KERN_DEBUG 1233bd69ad1SAndreas Eversberg "Current clock source '%s' unregisters.\n", 1243bd69ad1SAndreas Eversberg iclock->name); 1253bd69ad1SAndreas Eversberg iclock->ctl(iclock->priv, 0); 1263bd69ad1SAndreas Eversberg } 1273bd69ad1SAndreas Eversberg list_del(&iclock->list); 1283bd69ad1SAndreas Eversberg select_iclock(); 1293bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1303bd69ad1SAndreas Eversberg } 1313bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_unregister_clock); 1323bd69ad1SAndreas Eversberg 1333bd69ad1SAndreas Eversberg void 134ebf918cfSTina Ruchandani mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp) 1353bd69ad1SAndreas Eversberg { 1363bd69ad1SAndreas Eversberg u_long flags; 137ebf918cfSTina Ruchandani ktime_t timestamp_now; 138ebf918cfSTina Ruchandani u16 delta; 1393bd69ad1SAndreas Eversberg 1403bd69ad1SAndreas Eversberg write_lock_irqsave(&iclock_lock, flags); 1413bd69ad1SAndreas Eversberg if (iclock_current != iclock) { 1423bd69ad1SAndreas Eversberg printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " 1433bd69ad1SAndreas Eversberg "listen to '%s'. This is a bug!\n", __func__, 1443bd69ad1SAndreas Eversberg iclock->name, 1453bd69ad1SAndreas Eversberg iclock_current ? iclock_current->name : "nothing"); 1463bd69ad1SAndreas Eversberg iclock->ctl(iclock->priv, 0); 1473bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1483bd69ad1SAndreas Eversberg return; 1493bd69ad1SAndreas Eversberg } 150ebf918cfSTina Ruchandani if (iclock_timestamp_valid) { 1513bd69ad1SAndreas Eversberg /* increment sample counter by given samples */ 1523bd69ad1SAndreas Eversberg iclock_count += samples; 153ebf918cfSTina Ruchandani if (timestamp) { /* timestamp must be set, if function call is delayed */ 154ebf918cfSTina Ruchandani iclock_timestamp = *timestamp; 155ebf918cfSTina Ruchandani } else { 156ebf918cfSTina Ruchandani iclock_timestamp = ktime_get(); 157ebf918cfSTina Ruchandani } 1583bd69ad1SAndreas Eversberg } else { 1593bd69ad1SAndreas Eversberg /* calc elapsed time by system clock */ 160ebf918cfSTina Ruchandani if (timestamp) { /* timestamp must be set, if function call is delayed */ 161ebf918cfSTina Ruchandani timestamp_now = *timestamp; 162ebf918cfSTina Ruchandani } else { 163ebf918cfSTina Ruchandani timestamp_now = ktime_get(); 1643bd69ad1SAndreas Eversberg } 165ebf918cfSTina Ruchandani delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), 166ebf918cfSTina Ruchandani (NSEC_PER_SEC / 8000)); 1673bd69ad1SAndreas Eversberg /* add elapsed time to counter and set new timestamp */ 168ebf918cfSTina Ruchandani iclock_count += delta; 169ebf918cfSTina Ruchandani iclock_timestamp = timestamp_now; 170ebf918cfSTina Ruchandani iclock_timestamp_valid = 1; 1713bd69ad1SAndreas Eversberg if (*debug & DEBUG_CLOCK) 1723bd69ad1SAndreas Eversberg printk("Received first clock from source '%s'.\n", 1733bd69ad1SAndreas Eversberg iclock_current ? iclock_current->name : "nothing"); 1743bd69ad1SAndreas Eversberg } 1753bd69ad1SAndreas Eversberg write_unlock_irqrestore(&iclock_lock, flags); 1763bd69ad1SAndreas Eversberg } 1773bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_clock_update); 1783bd69ad1SAndreas Eversberg 1793bd69ad1SAndreas Eversberg unsigned short 1803bd69ad1SAndreas Eversberg mISDN_clock_get(void) 1813bd69ad1SAndreas Eversberg { 1823bd69ad1SAndreas Eversberg u_long flags; 183ebf918cfSTina Ruchandani ktime_t timestamp_now; 184ebf918cfSTina Ruchandani u16 delta; 1853bd69ad1SAndreas Eversberg u16 count; 1863bd69ad1SAndreas Eversberg 1873bd69ad1SAndreas Eversberg read_lock_irqsave(&iclock_lock, flags); 1883bd69ad1SAndreas Eversberg /* calc elapsed time by system clock */ 189ebf918cfSTina Ruchandani timestamp_now = ktime_get(); 190ebf918cfSTina Ruchandani delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp), 191ebf918cfSTina Ruchandani (NSEC_PER_SEC / 8000)); 1923bd69ad1SAndreas Eversberg /* add elapsed time to counter */ 193ebf918cfSTina Ruchandani count = iclock_count + delta; 1943bd69ad1SAndreas Eversberg read_unlock_irqrestore(&iclock_lock, flags); 1953bd69ad1SAndreas Eversberg return count; 1963bd69ad1SAndreas Eversberg } 1973bd69ad1SAndreas Eversberg EXPORT_SYMBOL(mISDN_clock_get); 198