224 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * kernel API4 *5 * Copyright (C) 2005-2009 Rodolfo Giometti <giometti@linux.it>6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 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 functions25 */26 27static 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 41static 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 functions51 */52 53/* pps_register_source - add a PPS source in the system54 * @info: the PPS info struct55 * @default_params: the default PPS parameters of the new source56 *57 * This function is used to add a new PPS source in the system. The new58 * source is described by info's fields and it will have, as default PPS59 * parameters, the ones specified into default_params.60 *61 * The function returns, in case of success, the PPS device. Otherwise62 * ERR_PTR(errno).63 */64 65struct 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 kfree_pps;113 }114 115 dev_info(pps->dev, "new PPS source %s\n", info->name);116 117 return pps;118 119kfree_pps:120 kfree(pps);121 122pps_register_source_exit:123 pr_err("%s: unable to register source\n", info->name);124 125 return ERR_PTR(err);126}127EXPORT_SYMBOL(pps_register_source);128 129/* pps_unregister_source - remove a PPS source from the system130 * @pps: the PPS source131 *132 * This function is used to remove a previously registered PPS source from133 * the system.134 */135 136void pps_unregister_source(struct pps_device *pps)137{138 pps_kc_remove(pps);139 pps_unregister_cdev(pps);140 141 /* don't have to kfree(pps) here because it will be done on142 * device destruction */143}144EXPORT_SYMBOL(pps_unregister_source);145 146/* pps_event - register a PPS event into the system147 * @pps: the PPS device148 * @ts: the event timestamp149 * @event: the event type150 * @data: userdef pointer151 *152 * This function is used by each PPS client in order to register a new153 * PPS event into the system (it's usually called inside an IRQ handler).154 *155 * If an echo function is associated with the PPS device it will be called156 * as:157 * pps->info.echo(pps, event, data);158 */159void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event,160 void *data)161{162 unsigned long flags;163 int captured = 0;164 struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 };165 166 /* check event type */167 BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0);168 169 dev_dbg(pps->dev, "PPS event at %lld.%09ld\n",170 (s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec);171 172 timespec_to_pps_ktime(&ts_real, ts->ts_real);173 174 spin_lock_irqsave(&pps->lock, flags);175 176 /* Must call the echo function? */177 if ((pps->params.mode & (PPS_ECHOASSERT | PPS_ECHOCLEAR)))178 pps->info.echo(pps, event, data);179 180 /* Check the event */181 pps->current_mode = pps->params.mode;182 if (event & pps->params.mode & PPS_CAPTUREASSERT) {183 /* We have to add an offset? */184 if (pps->params.mode & PPS_OFFSETASSERT)185 pps_add_offset(&ts_real,186 &pps->params.assert_off_tu);187 188 /* Save the time stamp */189 pps->assert_tu = ts_real;190 pps->assert_sequence++;191 dev_dbg(pps->dev, "capture assert seq #%u\n",192 pps->assert_sequence);193 194 captured = ~0;195 }196 if (event & pps->params.mode & PPS_CAPTURECLEAR) {197 /* We have to add an offset? */198 if (pps->params.mode & PPS_OFFSETCLEAR)199 pps_add_offset(&ts_real,200 &pps->params.clear_off_tu);201 202 /* Save the time stamp */203 pps->clear_tu = ts_real;204 pps->clear_sequence++;205 dev_dbg(pps->dev, "capture clear seq #%u\n",206 pps->clear_sequence);207 208 captured = ~0;209 }210 211 pps_kc_event(pps, ts, event);212 213 /* Wake up if captured something */214 if (captured) {215 pps->last_ev++;216 wake_up_interruptible_all(&pps->queue);217 218 kill_fasync(&pps->async_queue, SIGIO, POLL_IN);219 }220 221 spin_unlock_irqrestore(&pps->lock, flags);222}223EXPORT_SYMBOL(pps_event);224