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