pps_gen_tio.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Intel PPS signal Generator Driver
  4. *
  5. * Copyright (C) 2024 Intel Corporation
  6. */
  7. #include <linux/bitfield.h>
  8. #include <linux/bits.h>
  9. #include <linux/cleanup.h>
  10. #include <linux/container_of.h>
  11. #include <linux/device.h>
  12. #include <linux/hrtimer.h>
  13. #include <linux/io-64-nonatomic-hi-lo.h>
  14. #include <linux/mod_devicetable.h>
  15. #include <linux/module.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/pps_gen_kernel.h>
  18. #include <linux/timekeeping.h>
  19. #include <linux/types.h>
  20. #include <asm/cpu_device_id.h>
  21. #define TIOCTL 0x00
  22. #define TIOCOMPV 0x10
  23. #define TIOEC 0x30
  24. /* Control Register */
  25. #define TIOCTL_EN BIT(0)
  26. #define TIOCTL_DIR BIT(1)
  27. #define TIOCTL_EP GENMASK(3, 2)
  28. #define TIOCTL_EP_RISING_EDGE FIELD_PREP(TIOCTL_EP, 0)
  29. #define TIOCTL_EP_FALLING_EDGE FIELD_PREP(TIOCTL_EP, 1)
  30. #define TIOCTL_EP_TOGGLE_EDGE FIELD_PREP(TIOCTL_EP, 2)
  31. /* Safety time to set hrtimer early */
  32. #define SAFE_TIME_NS (10 * NSEC_PER_MSEC)
  33. #define MAGIC_CONST (NSEC_PER_SEC - SAFE_TIME_NS)
  34. #define ART_HW_DELAY_CYCLES 2
  35. struct pps_tio {
  36. struct pps_gen_source_info gen_info;
  37. struct pps_gen_device *pps_gen;
  38. struct hrtimer timer;
  39. void __iomem *base;
  40. u32 prev_count;
  41. spinlock_t lock;
  42. struct device *dev;
  43. };
  44. static inline u32 pps_tio_read(u32 offset, struct pps_tio *tio)
  45. {
  46. return readl(tio->base + offset);
  47. }
  48. static inline void pps_ctl_write(u32 value, struct pps_tio *tio)
  49. {
  50. writel(value, tio->base + TIOCTL);
  51. }
  52. /*
  53. * For COMPV register, It's safer to write
  54. * higher 32-bit followed by lower 32-bit
  55. */
  56. static inline void pps_compv_write(u64 value, struct pps_tio *tio)
  57. {
  58. hi_lo_writeq(value, tio->base + TIOCOMPV);
  59. }
  60. static inline ktime_t first_event(struct pps_tio *tio)
  61. {
  62. return ktime_set(ktime_get_real_seconds() + 1, MAGIC_CONST);
  63. }
  64. static u32 pps_tio_disable(struct pps_tio *tio)
  65. {
  66. u32 ctrl;
  67. ctrl = pps_tio_read(TIOCTL, tio);
  68. pps_compv_write(0, tio);
  69. ctrl &= ~TIOCTL_EN;
  70. pps_ctl_write(ctrl, tio);
  71. tio->pps_gen->enabled = false;
  72. tio->prev_count = 0;
  73. return ctrl;
  74. }
  75. static void pps_tio_enable(struct pps_tio *tio)
  76. {
  77. u32 ctrl;
  78. ctrl = pps_tio_read(TIOCTL, tio);
  79. ctrl |= TIOCTL_EN;
  80. pps_ctl_write(ctrl, tio);
  81. tio->pps_gen->enabled = true;
  82. }
  83. static void pps_tio_direction_output(struct pps_tio *tio)
  84. {
  85. u32 ctrl;
  86. ctrl = pps_tio_disable(tio);
  87. /*
  88. * We enable the device, be sure that the
  89. * 'compare' value is invalid
  90. */
  91. pps_compv_write(0, tio);
  92. ctrl &= ~(TIOCTL_DIR | TIOCTL_EP);
  93. ctrl |= TIOCTL_EP_TOGGLE_EDGE;
  94. pps_ctl_write(ctrl, tio);
  95. pps_tio_enable(tio);
  96. }
  97. static bool pps_generate_next_pulse(ktime_t expires, struct pps_tio *tio)
  98. {
  99. u64 art;
  100. if (!ktime_real_to_base_clock(expires, CSID_X86_ART, &art)) {
  101. pps_tio_disable(tio);
  102. return false;
  103. }
  104. pps_compv_write(art - ART_HW_DELAY_CYCLES, tio);
  105. return true;
  106. }
  107. static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
  108. {
  109. ktime_t expires, now;
  110. u32 event_count;
  111. struct pps_tio *tio = container_of(timer, struct pps_tio, timer);
  112. guard(spinlock)(&tio->lock);
  113. /*
  114. * Check if any event is missed.
  115. * If an event is missed, TIO will be disabled.
  116. */
  117. event_count = pps_tio_read(TIOEC, tio);
  118. if (tio->prev_count && tio->prev_count == event_count)
  119. goto err;
  120. tio->prev_count = event_count;
  121. expires = hrtimer_get_expires(timer);
  122. now = ktime_get_real();
  123. if (now - expires >= SAFE_TIME_NS)
  124. goto err;
  125. tio->pps_gen->enabled = pps_generate_next_pulse(expires + SAFE_TIME_NS, tio);
  126. if (!tio->pps_gen->enabled)
  127. return HRTIMER_NORESTART;
  128. hrtimer_forward(timer, now, NSEC_PER_SEC / 2);
  129. return HRTIMER_RESTART;
  130. err:
  131. dev_err(tio->dev, "Event missed, Disabling Timed I/O");
  132. pps_tio_disable(tio);
  133. pps_gen_event(tio->pps_gen, PPS_GEN_EVENT_MISSEDPULSE, NULL);
  134. return HRTIMER_NORESTART;
  135. }
  136. static int pps_tio_gen_enable(struct pps_gen_device *pps_gen, bool enable)
  137. {
  138. struct pps_tio *tio = container_of(pps_gen->info, struct pps_tio, gen_info);
  139. if (!timekeeping_clocksource_has_base(CSID_X86_ART)) {
  140. dev_err_once(tio->dev, "PPS cannot be used as clock is not related to ART");
  141. return -ENODEV;
  142. }
  143. guard(spinlock_irqsave)(&tio->lock);
  144. if (enable && !pps_gen->enabled) {
  145. pps_tio_direction_output(tio);
  146. hrtimer_start(&tio->timer, first_event(tio), HRTIMER_MODE_ABS);
  147. } else if (!enable && pps_gen->enabled) {
  148. hrtimer_cancel(&tio->timer);
  149. pps_tio_disable(tio);
  150. }
  151. return 0;
  152. }
  153. static int pps_tio_get_time(struct pps_gen_device *pps_gen,
  154. struct timespec64 *time)
  155. {
  156. struct system_time_snapshot snap;
  157. ktime_get_snapshot(&snap);
  158. *time = ktime_to_timespec64(snap.real);
  159. return 0;
  160. }
  161. static int pps_gen_tio_probe(struct platform_device *pdev)
  162. {
  163. struct device *dev = &pdev->dev;
  164. struct pps_tio *tio;
  165. if (!(cpu_feature_enabled(X86_FEATURE_TSC_KNOWN_FREQ) &&
  166. cpu_feature_enabled(X86_FEATURE_ART))) {
  167. dev_warn(dev, "TSC/ART is not enabled");
  168. return -ENODEV;
  169. }
  170. tio = devm_kzalloc(dev, sizeof(*tio), GFP_KERNEL);
  171. if (!tio)
  172. return -ENOMEM;
  173. tio->gen_info.use_system_clock = true;
  174. tio->gen_info.enable = pps_tio_gen_enable;
  175. tio->gen_info.get_time = pps_tio_get_time;
  176. tio->gen_info.owner = THIS_MODULE;
  177. tio->pps_gen = pps_gen_register_source(&tio->gen_info);
  178. if (IS_ERR(tio->pps_gen))
  179. return PTR_ERR(tio->pps_gen);
  180. tio->dev = dev;
  181. tio->base = devm_platform_ioremap_resource(pdev, 0);
  182. if (IS_ERR(tio->base))
  183. return PTR_ERR(tio->base);
  184. pps_tio_disable(tio);
  185. hrtimer_setup(&tio->timer, hrtimer_callback, CLOCK_REALTIME,
  186. HRTIMER_MODE_ABS);
  187. spin_lock_init(&tio->lock);
  188. platform_set_drvdata(pdev, tio);
  189. return 0;
  190. }
  191. static void pps_gen_tio_remove(struct platform_device *pdev)
  192. {
  193. struct pps_tio *tio = platform_get_drvdata(pdev);
  194. hrtimer_cancel(&tio->timer);
  195. pps_tio_disable(tio);
  196. pps_gen_unregister_source(tio->pps_gen);
  197. }
  198. static const struct acpi_device_id intel_pmc_tio_acpi_match[] = {
  199. { "INTC1021" },
  200. { "INTC1022" },
  201. { "INTC1023" },
  202. { "INTC1024" },
  203. {}
  204. };
  205. MODULE_DEVICE_TABLE(acpi, intel_pmc_tio_acpi_match);
  206. static struct platform_driver pps_gen_tio_driver = {
  207. .probe = pps_gen_tio_probe,
  208. .remove = pps_gen_tio_remove,
  209. .driver = {
  210. .name = "intel-pps-gen-tio",
  211. .acpi_match_table = intel_pmc_tio_acpi_match,
  212. },
  213. };
  214. module_platform_driver(pps_gen_tio_driver);
  215. MODULE_AUTHOR("Christopher Hall <christopher.s.hall@intel.com>");
  216. MODULE_AUTHOR("Lakshmi Sowjanya D <lakshmi.sowjanya.d@intel.com>");
  217. MODULE_AUTHOR("Pandith N <pandith.n@intel.com>");
  218. MODULE_AUTHOR("Thejesh Reddy T R <thejesh.reddy.t.r@intel.com>");
  219. MODULE_AUTHOR("Subramanian Mohan <subramanian.mohan@intel.com>");
  220. MODULE_DESCRIPTION("Intel PMC Time-Aware IO Generator Driver");
  221. MODULE_LICENSE("GPL");