brintos

brintos / linux-shallow public Read only

0
0
Text · 3.1 KiB · 50dc59a Raw
110 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * PPS kernel consumer API4 *5 * Copyright (C) 2009-2010   Alexander Gordeev <lasaine@lvk.cs.msu.su>6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/device.h>13#include <linux/init.h>14#include <linux/spinlock.h>15#include <linux/pps_kernel.h>16 17#include "kc.h"18 19/*20 * Global variables21 */22 23/* state variables to bind kernel consumer */24static DEFINE_SPINLOCK(pps_kc_hardpps_lock);25/* PPS API (RFC 2783): current source and mode for kernel consumer */26static struct pps_device *pps_kc_hardpps_dev;	/* unique pointer to device */27static int pps_kc_hardpps_mode;		/* mode bits for kernel consumer */28 29/* pps_kc_bind - control PPS kernel consumer binding30 * @pps: the PPS source31 * @bind_args: kernel consumer bind parameters32 *33 * This function is used to bind or unbind PPS kernel consumer according to34 * supplied parameters. Should not be called in interrupt context.35 */36int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)37{38	/* Check if another consumer is already bound */39	spin_lock_irq(&pps_kc_hardpps_lock);40 41	if (bind_args->edge == 0)42		if (pps_kc_hardpps_dev == pps) {43			pps_kc_hardpps_mode = 0;44			pps_kc_hardpps_dev = NULL;45			spin_unlock_irq(&pps_kc_hardpps_lock);46			dev_info(pps->dev, "unbound kernel"47					" consumer\n");48		} else {49			spin_unlock_irq(&pps_kc_hardpps_lock);50			dev_err(pps->dev, "selected kernel consumer"51					" is not bound\n");52			return -EINVAL;53		}54	else55		if (pps_kc_hardpps_dev == NULL ||56				pps_kc_hardpps_dev == pps) {57			pps_kc_hardpps_mode = bind_args->edge;58			pps_kc_hardpps_dev = pps;59			spin_unlock_irq(&pps_kc_hardpps_lock);60			dev_info(pps->dev, "bound kernel consumer: "61				"edge=0x%x\n", bind_args->edge);62		} else {63			spin_unlock_irq(&pps_kc_hardpps_lock);64			dev_err(pps->dev, "another kernel consumer"65					" is already bound\n");66			return -EINVAL;67		}68 69	return 0;70}71 72/* pps_kc_remove - unbind kernel consumer on PPS source removal73 * @pps: the PPS source74 *75 * This function is used to disable kernel consumer on PPS source removal76 * if this source was bound to PPS kernel consumer. Can be called on any77 * source safely. Should not be called in interrupt context.78 */79void pps_kc_remove(struct pps_device *pps)80{81	spin_lock_irq(&pps_kc_hardpps_lock);82	if (pps == pps_kc_hardpps_dev) {83		pps_kc_hardpps_mode = 0;84		pps_kc_hardpps_dev = NULL;85		spin_unlock_irq(&pps_kc_hardpps_lock);86		dev_info(pps->dev, "unbound kernel consumer"87				" on device removal\n");88	} else89		spin_unlock_irq(&pps_kc_hardpps_lock);90}91 92/* pps_kc_event - call hardpps() on PPS event93 * @pps: the PPS source94 * @ts: PPS event timestamp95 * @event: PPS event edge96 *97 * This function calls hardpps() when an event from bound PPS source occurs.98 */99void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,100		int event)101{102	unsigned long flags;103 104	/* Pass some events to kernel consumer if activated */105	spin_lock_irqsave(&pps_kc_hardpps_lock, flags);106	if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)107		hardpps(&ts->ts_real, &ts->ts_raw);108	spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);109}110