Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.17 272 lines 6.7 kB view raw
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 42struct 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 52static inline u32 pps_tio_read(u32 offset, struct pps_tio *tio) 53{ 54 return readl(tio->base + offset); 55} 56 57static 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 */ 66static inline void pps_compv_write(u64 value, struct pps_tio *tio) 67{ 68 hi_lo_writeq(value, tio->base + TIOCOMPV); 69} 70 71static inline ktime_t first_event(struct pps_tio *tio) 72{ 73 return ktime_set(ktime_get_real_seconds() + 1, MAGIC_CONST); 74} 75 76static 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 90static 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 100static 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 118static 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 131static 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 161err: 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 168static 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 189static 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 200static 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 238static 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 247static const struct acpi_device_id intel_pmc_tio_acpi_match[] = { 248 { "INTC1021" }, 249 { "INTC1022" }, 250 { "INTC1023" }, 251 { "INTC1024" }, 252 {} 253}; 254MODULE_DEVICE_TABLE(acpi, intel_pmc_tio_acpi_match); 255 256static 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}; 264module_platform_driver(pps_gen_tio_driver); 265 266MODULE_AUTHOR("Christopher Hall <christopher.s.hall@intel.com>"); 267MODULE_AUTHOR("Lakshmi Sowjanya D <lakshmi.sowjanya.d@intel.com>"); 268MODULE_AUTHOR("Pandith N <pandith.n@intel.com>"); 269MODULE_AUTHOR("Thejesh Reddy T R <thejesh.reddy.t.r@intel.com>"); 270MODULE_AUTHOR("Subramanian Mohan <subramanian.mohan@intel.com>"); 271MODULE_DESCRIPTION("Intel PMC Time-Aware IO Generator Driver"); 272MODULE_LICENSE("GPL");