kc.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /*
  2. * PPS kernel consumer API
  3. *
  4. * Copyright (C) 2009-2010 Alexander Gordeev <lasaine@lvk.cs.msu.su>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. */
  20. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  21. #include <linux/kernel.h>
  22. #include <linux/module.h>
  23. #include <linux/device.h>
  24. #include <linux/init.h>
  25. #include <linux/spinlock.h>
  26. #include <linux/pps_kernel.h>
  27. #include "kc.h"
  28. /*
  29. * Global variables
  30. */
  31. /* state variables to bind kernel consumer */
  32. DEFINE_SPINLOCK(pps_kc_hardpps_lock);
  33. /* PPS API (RFC 2783): current source and mode for kernel consumer */
  34. struct pps_device *pps_kc_hardpps_dev; /* unique pointer to device */
  35. int pps_kc_hardpps_mode; /* mode bits for kernel consumer */
  36. /* pps_kc_bind - control PPS kernel consumer binding
  37. * @pps: the PPS source
  38. * @bind_args: kernel consumer bind parameters
  39. *
  40. * This function is used to bind or unbind PPS kernel consumer according to
  41. * supplied parameters. Should not be called in interrupt context.
  42. */
  43. int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args)
  44. {
  45. /* Check if another consumer is already bound */
  46. spin_lock_irq(&pps_kc_hardpps_lock);
  47. if (bind_args->edge == 0)
  48. if (pps_kc_hardpps_dev == pps) {
  49. pps_kc_hardpps_mode = 0;
  50. pps_kc_hardpps_dev = NULL;
  51. spin_unlock_irq(&pps_kc_hardpps_lock);
  52. dev_info(pps->dev, "unbound kernel"
  53. " consumer\n");
  54. } else {
  55. spin_unlock_irq(&pps_kc_hardpps_lock);
  56. dev_err(pps->dev, "selected kernel consumer"
  57. " is not bound\n");
  58. return -EINVAL;
  59. }
  60. else
  61. if (pps_kc_hardpps_dev == NULL ||
  62. pps_kc_hardpps_dev == pps) {
  63. pps_kc_hardpps_mode = bind_args->edge;
  64. pps_kc_hardpps_dev = pps;
  65. spin_unlock_irq(&pps_kc_hardpps_lock);
  66. dev_info(pps->dev, "bound kernel consumer: "
  67. "edge=0x%x\n", bind_args->edge);
  68. } else {
  69. spin_unlock_irq(&pps_kc_hardpps_lock);
  70. dev_err(pps->dev, "another kernel consumer"
  71. " is already bound\n");
  72. return -EINVAL;
  73. }
  74. return 0;
  75. }
  76. /* pps_kc_remove - unbind kernel consumer on PPS source removal
  77. * @pps: the PPS source
  78. *
  79. * This function is used to disable kernel consumer on PPS source removal
  80. * if this source was bound to PPS kernel consumer. Can be called on any
  81. * source safely. Should not be called in interrupt context.
  82. */
  83. void pps_kc_remove(struct pps_device *pps)
  84. {
  85. spin_lock_irq(&pps_kc_hardpps_lock);
  86. if (pps == pps_kc_hardpps_dev) {
  87. pps_kc_hardpps_mode = 0;
  88. pps_kc_hardpps_dev = NULL;
  89. spin_unlock_irq(&pps_kc_hardpps_lock);
  90. dev_info(pps->dev, "unbound kernel consumer"
  91. " on device removal\n");
  92. } else
  93. spin_unlock_irq(&pps_kc_hardpps_lock);
  94. }
  95. /* pps_kc_event - call hardpps() on PPS event
  96. * @pps: the PPS source
  97. * @ts: PPS event timestamp
  98. * @event: PPS event edge
  99. *
  100. * This function calls hardpps() when an event from bound PPS source occurs.
  101. */
  102. void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts,
  103. int event)
  104. {
  105. unsigned long flags;
  106. /* Pass some events to kernel consumer if activated */
  107. spin_lock_irqsave(&pps_kc_hardpps_lock, flags);
  108. if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode)
  109. hardpps(&ts->ts_real, &ts->ts_raw);
  110. spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags);
  111. }