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