| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 2 | /* | 
|---|
| 3 | * kernel API | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (C) 2005-2009   Rodolfo Giometti <giometti@linux.it> | 
|---|
| 6 | */ | 
|---|
| 7 |  | 
|---|
| 8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|---|
| 9 |  | 
|---|
| 10 | #include <linux/kernel.h> | 
|---|
| 11 | #include <linux/module.h> | 
|---|
| 12 | #include <linux/init.h> | 
|---|
| 13 | #include <linux/sched.h> | 
|---|
| 14 | #include <linux/time.h> | 
|---|
| 15 | #include <linux/timex.h> | 
|---|
| 16 | #include <linux/spinlock.h> | 
|---|
| 17 | #include <linux/fs.h> | 
|---|
| 18 | #include <linux/pps_kernel.h> | 
|---|
| 19 | #include <linux/slab.h> | 
|---|
| 20 |  | 
|---|
| 21 | #include "kc.h" | 
|---|
| 22 |  | 
|---|
| 23 | /* | 
|---|
| 24 | * Local functions | 
|---|
| 25 | */ | 
|---|
| 26 |  | 
|---|
| 27 | static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset) | 
|---|
| 28 | { | 
|---|
| 29 | ts->nsec += offset->nsec; | 
|---|
| 30 | while (ts->nsec >= NSEC_PER_SEC) { | 
|---|
| 31 | ts->nsec -= NSEC_PER_SEC; | 
|---|
| 32 | ts->sec++; | 
|---|
| 33 | } | 
|---|
| 34 | while (ts->nsec < 0) { | 
|---|
| 35 | ts->nsec += NSEC_PER_SEC; | 
|---|
| 36 | ts->sec--; | 
|---|
| 37 | } | 
|---|
| 38 | ts->sec += offset->sec; | 
|---|
| 39 | } | 
|---|
| 40 |  | 
|---|
| 41 | static void pps_echo_client_default(struct pps_device *pps, int event, | 
|---|
| 42 | void *data) | 
|---|
| 43 | { | 
|---|
| 44 | dev_info(&pps->dev, "echo %s %s\n", | 
|---|
| 45 | event & PPS_CAPTUREASSERT ? "assert": "", | 
|---|
| 46 | event & PPS_CAPTURECLEAR ? "clear": ""); | 
|---|
| 47 | } | 
|---|
| 48 |  | 
|---|
| 49 | /* | 
|---|
| 50 | * Exported functions | 
|---|
| 51 | */ | 
|---|
| 52 |  | 
|---|
| 53 | /* pps_register_source - add a PPS source in the system | 
|---|
| 54 | * @info: the PPS info struct | 
|---|
| 55 | * @default_params: the default PPS parameters of the new source | 
|---|
| 56 | * | 
|---|
| 57 | * This function is used to add a new PPS source in the system. The new | 
|---|
| 58 | * source is described by info's fields and it will have, as default PPS | 
|---|
| 59 | * parameters, the ones specified into default_params. | 
|---|
| 60 | * | 
|---|
| 61 | * The function returns, in case of success, the PPS device. Otherwise | 
|---|
| 62 | * ERR_PTR(errno). | 
|---|
| 63 | */ | 
|---|
| 64 |  | 
|---|
| 65 | struct pps_device *pps_register_source(struct pps_source_info *info, | 
|---|
| 66 | int default_params) | 
|---|
| 67 | { | 
|---|
| 68 | struct pps_device *pps; | 
|---|
| 69 | int err; | 
|---|
| 70 |  | 
|---|
| 71 | /* Sanity checks */ | 
|---|
| 72 | if ((info->mode & default_params) != default_params) { | 
|---|
| 73 | pr_err( "%s: unsupported default parameters\n", | 
|---|
| 74 | info->name); | 
|---|
| 75 | err = -EINVAL; | 
|---|
| 76 | goto pps_register_source_exit; | 
|---|
| 77 | } | 
|---|
| 78 | if ((info->mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { | 
|---|
| 79 | pr_err( "%s: unspecified time format\n", | 
|---|
| 80 | info->name); | 
|---|
| 81 | err = -EINVAL; | 
|---|
| 82 | goto pps_register_source_exit; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | /* Allocate memory for the new PPS source struct */ | 
|---|
| 86 | pps = kzalloc(sizeof(struct pps_device), GFP_KERNEL); | 
|---|
| 87 | if (pps == NULL) { | 
|---|
| 88 | err = -ENOMEM; | 
|---|
| 89 | goto pps_register_source_exit; | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 | /* These initializations must be done before calling idr_alloc() | 
|---|
| 93 | * in order to avoid reces into pps_event(). | 
|---|
| 94 | */ | 
|---|
| 95 | pps->params.api_version = PPS_API_VERS; | 
|---|
| 96 | pps->params.mode = default_params; | 
|---|
| 97 | pps->info = *info; | 
|---|
| 98 |  | 
|---|
| 99 | /* check for default echo function */ | 
|---|
| 100 | if ((pps->info.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)) && | 
|---|
| 101 | pps->info.echo == NULL) | 
|---|
| 102 | pps->info.echo = pps_echo_client_default; | 
|---|
| 103 |  | 
|---|
| 104 | init_waitqueue_head(&pps->queue); | 
|---|
| 105 | spin_lock_init(&pps->lock); | 
|---|
| 106 |  | 
|---|
| 107 | /* Create the char device */ | 
|---|
| 108 | err = pps_register_cdev(pps); | 
|---|
| 109 | if (err < 0) { | 
|---|
| 110 | pr_err( "%s: unable to create char device\n", | 
|---|
| 111 | info->name); | 
|---|
| 112 | goto pps_register_source_exit; | 
|---|
| 113 | } | 
|---|
| 114 |  | 
|---|
| 115 | dev_dbg(&pps->dev, "new PPS source %s\n", info->name); | 
|---|
| 116 |  | 
|---|
| 117 | return pps; | 
|---|
| 118 |  | 
|---|
| 119 | pps_register_source_exit: | 
|---|
| 120 | pr_err( "%s: unable to register source\n", info->name); | 
|---|
| 121 |  | 
|---|
| 122 | return ERR_PTR(error: err); | 
|---|
| 123 | } | 
|---|
| 124 | EXPORT_SYMBOL(pps_register_source); | 
|---|
| 125 |  | 
|---|
| 126 | /* pps_unregister_source - remove a PPS source from the system | 
|---|
| 127 | * @pps: the PPS source | 
|---|
| 128 | * | 
|---|
| 129 | * This function is used to remove a previously registered PPS source from | 
|---|
| 130 | * the system. | 
|---|
| 131 | */ | 
|---|
| 132 |  | 
|---|
| 133 | void pps_unregister_source(struct pps_device *pps) | 
|---|
| 134 | { | 
|---|
| 135 | pps_kc_remove(pps); | 
|---|
| 136 | pps_unregister_cdev(pps); | 
|---|
| 137 |  | 
|---|
| 138 | /* don't have to kfree(pps) here because it will be done on | 
|---|
| 139 | * device destruction */ | 
|---|
| 140 | } | 
|---|
| 141 | EXPORT_SYMBOL(pps_unregister_source); | 
|---|
| 142 |  | 
|---|
| 143 | /* pps_event - register a PPS event into the system | 
|---|
| 144 | * @pps: the PPS device | 
|---|
| 145 | * @ts: the event timestamp | 
|---|
| 146 | * @event: the event type | 
|---|
| 147 | * @data: userdef pointer | 
|---|
| 148 | * | 
|---|
| 149 | * This function is used by each PPS client in order to register a new | 
|---|
| 150 | * PPS event into the system (it's usually called inside an IRQ handler). | 
|---|
| 151 | * | 
|---|
| 152 | * If an echo function is associated with the PPS device it will be called | 
|---|
| 153 | * as: | 
|---|
| 154 | *	pps->info.echo(pps, event, data); | 
|---|
| 155 | */ | 
|---|
| 156 | void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, | 
|---|
| 157 | void *data) | 
|---|
| 158 | { | 
|---|
| 159 | unsigned long flags; | 
|---|
| 160 | int captured = 0; | 
|---|
| 161 | struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 }; | 
|---|
| 162 |  | 
|---|
| 163 | /* check event type */ | 
|---|
| 164 | BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0); | 
|---|
| 165 |  | 
|---|
| 166 | dev_dbg(&pps->dev, "PPS event at %lld.%09ld\n", | 
|---|
| 167 | (s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec); | 
|---|
| 168 |  | 
|---|
| 169 | timespec_to_pps_ktime(kt: &ts_real, ts: ts->ts_real); | 
|---|
| 170 |  | 
|---|
| 171 | spin_lock_irqsave(&pps->lock, flags); | 
|---|
| 172 |  | 
|---|
| 173 | /* Must call the echo function? */ | 
|---|
| 174 | if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR))) | 
|---|
| 175 | pps->info.echo(pps, event, data); | 
|---|
| 176 |  | 
|---|
| 177 | /* Check the event */ | 
|---|
| 178 | pps->current_mode = pps->params.mode; | 
|---|
| 179 | if (event & pps->params.mode & PPS_CAPTUREASSERT) { | 
|---|
| 180 | /* We have to add an offset? */ | 
|---|
| 181 | if (pps->params.mode & PPS_OFFSETASSERT) | 
|---|
| 182 | pps_add_offset(ts: &ts_real, | 
|---|
| 183 | offset: &pps->params.assert_off_tu); | 
|---|
| 184 |  | 
|---|
| 185 | /* Save the time stamp */ | 
|---|
| 186 | pps->assert_tu = ts_real; | 
|---|
| 187 | pps->assert_sequence++; | 
|---|
| 188 | dev_dbg(&pps->dev, "capture assert seq #%u\n", | 
|---|
| 189 | pps->assert_sequence); | 
|---|
| 190 |  | 
|---|
| 191 | captured = ~0; | 
|---|
| 192 | } | 
|---|
| 193 | if (event & pps->params.mode & PPS_CAPTURECLEAR) { | 
|---|
| 194 | /* We have to add an offset? */ | 
|---|
| 195 | if (pps->params.mode & PPS_OFFSETCLEAR) | 
|---|
| 196 | pps_add_offset(ts: &ts_real, | 
|---|
| 197 | offset: &pps->params.clear_off_tu); | 
|---|
| 198 |  | 
|---|
| 199 | /* Save the time stamp */ | 
|---|
| 200 | pps->clear_tu = ts_real; | 
|---|
| 201 | pps->clear_sequence++; | 
|---|
| 202 | dev_dbg(&pps->dev, "capture clear seq #%u\n", | 
|---|
| 203 | pps->clear_sequence); | 
|---|
| 204 |  | 
|---|
| 205 | captured = ~0; | 
|---|
| 206 | } | 
|---|
| 207 |  | 
|---|
| 208 | pps_kc_event(pps, ts, event); | 
|---|
| 209 |  | 
|---|
| 210 | /* Wake up if captured something */ | 
|---|
| 211 | if (captured) { | 
|---|
| 212 | pps->last_ev++; | 
|---|
| 213 | wake_up_interruptible_all(&pps->queue); | 
|---|
| 214 |  | 
|---|
| 215 | kill_fasync(&pps->async_queue, SIGIO, POLL_IN); | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | spin_unlock_irqrestore(lock: &pps->lock, flags); | 
|---|
| 219 | } | 
|---|
| 220 | EXPORT_SYMBOL(pps_event); | 
|---|
| 221 |  | 
|---|