kc.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * PPS kernel consumer API
  4. *
  5. * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su>
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/device.h>
  11. #include <linux/init.h>
  12. #include <linux/spinlock.h>
  13. #include <linux/pps_kernel.h>
  14. #include "kc.h"
  15. /*
  16. * Global variables
  17. */
  18. /* state variables to bind kernel consumer */
  19. static DEFINE_SPINLOCK(pps_kc_hardpps_lock);
  20. /* PPS API (RFC 2783): current source and mode for kernel consumer */
  21. static struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */
  22. static int pps_kc_hardpps_mode; /* mode bits for kernel consumer */
  23. /* pps_kc_bind - control PPS kernel consumer binding
  24. * @pps: the PPS source
  25. * @bind_args: kernel consumer bind parameters
  26. *
  27. * This function is used to bind or unbind PPS kernel consumer according to
  28. * supplied parameters. Should not be called in interrupt context.
  29. */
  30. int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
  31. {
  32. /* Check if another consumer is already bound */
  33. spin_lock_irq(&pps_kc_hardpps_lock);
  34. if (bind_args->edge == 0)
  35. if (pps_kc_hardpps_dev == pps) {
  36. pps_kc_hardpps_mode = 0;
  37. pps_kc_hardpps_dev = NULL;
  38. spin_unlock_irq(&pps_kc_hardpps_lock);
  39. dev_info(pps->dev, "unbound kernel"
  40. " consumer\n");
  41. } else {
  42. spin_unlock_irq(&pps_kc_hardpps_lock);
  43. dev_err(pps->dev, "selected kernel consumer"
  44. " is not bound\n");
  45. return -EINVAL;
  46. }
  47. else
  48. if (pps_kc_hardpps_dev == NULL ||
  49. pps_kc_hardpps_dev == pps) {
  50. pps_kc_hardpps_mode = bind_args->edge;
  51. pps_kc_hardpps_dev = pps;
  52. spin_unlock_irq(&pps_kc_hardpps_lock);
  53. dev_info(pps->dev, "bound kernel consumer: "
  54. "edge=0x%x\n", bind_args->edge);
  55. } else {
  56. spin_unlock_irq(&pps_kc_hardpps_lock);
  57. dev_err(pps->dev, "another kernel consumer"
  58. " is already bound\n");
  59. return -EINVAL;
  60. }
  61. return 0;
  62. }
  63. /* pps_kc_remove - unbind kernel consumer on PPS source removal
  64. * @pps: the PPS source
  65. *
  66. * This function is used to disable kernel consumer on PPS source removal
  67. * if this source was bound to PPS kernel consumer. Can be called on any
  68. * source safely. Should not be called in interrupt context.
  69. */
  70. void pps_kc_remove(struct pps_device *pps)
  71. {
  72. spin_lock_irq(&pps_kc_hardpps_lock);
  73. if (pps == pps_kc_hardpps_dev) {
  74. pps_kc_hardpps_mode = 0;
  75. pps_kc_hardpps_dev = NULL;
  76. spin_unlock_irq(&pps_kc_hardpps_lock);
  77. dev_info(pps->dev, "unbound kernel consumer"
  78. " on device removal\n");
  79. } else
  80. spin_unlock_irq(&pps_kc_hardpps_lock);
  81. }
  82. /* pps_kc_event - call hardpps() on PPS event
  83. * @pps: the PPS source
  84. * @ts: PPS event timestamp
  85. * @event: PPS event edge
  86. *
  87. * This function calls hardpps() when an event from bound PPS source occurs.
  88. */
  89. void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
  90. int event)
  91. {
  92. unsigned long flags;
  93. /* Pass some events to kernel consumer if activated */
  94. spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
  95. if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
  96. hardpps(&ts->ts_real, &ts->ts_raw);
  97. spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
  98. }