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 52 static inline u32 pps_tio_read(u32 offset, struct pps_tio *tio) 53 { 54 return readl(tio->base + offset); 55 } 56 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 */ 66 static inline void pps_compv_write(u64 value, struct pps_tio *tio) 67 { 68 hi_lo_writeq(value, tio->base + TIOCOMPV); 69 } 70 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 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 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 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 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 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 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 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 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 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