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