| .. | .. |
|---|
| 1 | | -#include <linux/clocksource.h> |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 2 | 2 | #include <linux/clockchips.h> |
|---|
| 3 | 3 | #include <linux/interrupt.h> |
|---|
| 4 | | -#include <linux/irq.h> |
|---|
| 5 | 4 | #include <linux/export.h> |
|---|
| 6 | 5 | #include <linux/delay.h> |
|---|
| 7 | | -#include <linux/errno.h> |
|---|
| 8 | | -#include <linux/i8253.h> |
|---|
| 9 | | -#include <linux/slab.h> |
|---|
| 10 | 6 | #include <linux/hpet.h> |
|---|
| 11 | | -#include <linux/init.h> |
|---|
| 12 | 7 | #include <linux/cpu.h> |
|---|
| 13 | | -#include <linux/pm.h> |
|---|
| 14 | | -#include <linux/io.h> |
|---|
| 8 | +#include <linux/irq.h> |
|---|
| 15 | 9 | |
|---|
| 16 | | -#include <asm/cpufeature.h> |
|---|
| 17 | | -#include <asm/irqdomain.h> |
|---|
| 18 | | -#include <asm/fixmap.h> |
|---|
| 19 | 10 | #include <asm/hpet.h> |
|---|
| 20 | 11 | #include <asm/time.h> |
|---|
| 12 | +#include <asm/mwait.h> |
|---|
| 13 | + |
|---|
| 14 | +#undef pr_fmt |
|---|
| 15 | +#define pr_fmt(fmt) "hpet: " fmt |
|---|
| 16 | + |
|---|
| 17 | +enum hpet_mode { |
|---|
| 18 | + HPET_MODE_UNUSED, |
|---|
| 19 | + HPET_MODE_LEGACY, |
|---|
| 20 | + HPET_MODE_CLOCKEVT, |
|---|
| 21 | + HPET_MODE_DEVICE, |
|---|
| 22 | +}; |
|---|
| 23 | + |
|---|
| 24 | +struct hpet_channel { |
|---|
| 25 | + struct clock_event_device evt; |
|---|
| 26 | + unsigned int num; |
|---|
| 27 | + unsigned int cpu; |
|---|
| 28 | + unsigned int irq; |
|---|
| 29 | + unsigned int in_use; |
|---|
| 30 | + enum hpet_mode mode; |
|---|
| 31 | + unsigned int boot_cfg; |
|---|
| 32 | + char name[10]; |
|---|
| 33 | +}; |
|---|
| 34 | + |
|---|
| 35 | +struct hpet_base { |
|---|
| 36 | + unsigned int nr_channels; |
|---|
| 37 | + unsigned int nr_clockevents; |
|---|
| 38 | + unsigned int boot_cfg; |
|---|
| 39 | + struct hpet_channel *channels; |
|---|
| 40 | +}; |
|---|
| 21 | 41 | |
|---|
| 22 | 42 | #define HPET_MASK CLOCKSOURCE_MASK(32) |
|---|
| 23 | | - |
|---|
| 24 | | -/* FSEC = 10^-15 |
|---|
| 25 | | - NSEC = 10^-9 */ |
|---|
| 26 | | -#define FSEC_PER_NSEC 1000000L |
|---|
| 27 | | - |
|---|
| 28 | | -#define HPET_DEV_USED_BIT 2 |
|---|
| 29 | | -#define HPET_DEV_USED (1 << HPET_DEV_USED_BIT) |
|---|
| 30 | | -#define HPET_DEV_VALID 0x8 |
|---|
| 31 | | -#define HPET_DEV_FSB_CAP 0x1000 |
|---|
| 32 | | -#define HPET_DEV_PERI_CAP 0x2000 |
|---|
| 33 | 43 | |
|---|
| 34 | 44 | #define HPET_MIN_CYCLES 128 |
|---|
| 35 | 45 | #define HPET_MIN_PROG_DELTA (HPET_MIN_CYCLES + (HPET_MIN_CYCLES >> 1)) |
|---|
| .. | .. |
|---|
| 42 | 52 | bool hpet_msi_disable; |
|---|
| 43 | 53 | |
|---|
| 44 | 54 | #ifdef CONFIG_PCI_MSI |
|---|
| 45 | | -static unsigned int hpet_num_timers; |
|---|
| 55 | +static DEFINE_PER_CPU(struct hpet_channel *, cpu_hpet_channel); |
|---|
| 56 | +static struct irq_domain *hpet_domain; |
|---|
| 46 | 57 | #endif |
|---|
| 58 | + |
|---|
| 47 | 59 | static void __iomem *hpet_virt_address; |
|---|
| 48 | 60 | |
|---|
| 49 | | -struct hpet_dev { |
|---|
| 50 | | - struct clock_event_device evt; |
|---|
| 51 | | - unsigned int num; |
|---|
| 52 | | - int cpu; |
|---|
| 53 | | - unsigned int irq; |
|---|
| 54 | | - unsigned int flags; |
|---|
| 55 | | - char name[10]; |
|---|
| 56 | | -}; |
|---|
| 61 | +static struct hpet_base hpet_base; |
|---|
| 57 | 62 | |
|---|
| 58 | | -static inline struct hpet_dev *EVT_TO_HPET_DEV(struct clock_event_device *evtdev) |
|---|
| 63 | +static bool hpet_legacy_int_enabled; |
|---|
| 64 | +static unsigned long hpet_freq; |
|---|
| 65 | + |
|---|
| 66 | +bool boot_hpet_disable; |
|---|
| 67 | +bool hpet_force_user; |
|---|
| 68 | +static bool hpet_verbose; |
|---|
| 69 | + |
|---|
| 70 | +static inline |
|---|
| 71 | +struct hpet_channel *clockevent_to_channel(struct clock_event_device *evt) |
|---|
| 59 | 72 | { |
|---|
| 60 | | - return container_of(evtdev, struct hpet_dev, evt); |
|---|
| 73 | + return container_of(evt, struct hpet_channel, evt); |
|---|
| 61 | 74 | } |
|---|
| 62 | 75 | |
|---|
| 63 | 76 | inline unsigned int hpet_readl(unsigned int a) |
|---|
| .. | .. |
|---|
| 70 | 83 | writel(d, hpet_virt_address + a); |
|---|
| 71 | 84 | } |
|---|
| 72 | 85 | |
|---|
| 73 | | -#ifdef CONFIG_X86_64 |
|---|
| 74 | | -#include <asm/pgtable.h> |
|---|
| 75 | | -#endif |
|---|
| 76 | | - |
|---|
| 77 | 86 | static inline void hpet_set_mapping(void) |
|---|
| 78 | 87 | { |
|---|
| 79 | | - hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE); |
|---|
| 88 | + hpet_virt_address = ioremap(hpet_address, HPET_MMAP_SIZE); |
|---|
| 80 | 89 | } |
|---|
| 81 | 90 | |
|---|
| 82 | 91 | static inline void hpet_clear_mapping(void) |
|---|
| .. | .. |
|---|
| 88 | 97 | /* |
|---|
| 89 | 98 | * HPET command line enable / disable |
|---|
| 90 | 99 | */ |
|---|
| 91 | | -bool boot_hpet_disable; |
|---|
| 92 | | -bool hpet_force_user; |
|---|
| 93 | | -static bool hpet_verbose; |
|---|
| 94 | | - |
|---|
| 95 | 100 | static int __init hpet_setup(char *str) |
|---|
| 96 | 101 | { |
|---|
| 97 | 102 | while (str) { |
|---|
| .. | .. |
|---|
| 123 | 128 | return !boot_hpet_disable && hpet_address; |
|---|
| 124 | 129 | } |
|---|
| 125 | 130 | |
|---|
| 126 | | -/* |
|---|
| 127 | | - * HPET timer interrupt enable / disable |
|---|
| 128 | | - */ |
|---|
| 129 | | -static bool hpet_legacy_int_enabled; |
|---|
| 130 | | - |
|---|
| 131 | 131 | /** |
|---|
| 132 | | - * is_hpet_enabled - check whether the hpet timer interrupt is enabled |
|---|
| 132 | + * is_hpet_enabled - Check whether the legacy HPET timer interrupt is enabled |
|---|
| 133 | 133 | */ |
|---|
| 134 | 134 | int is_hpet_enabled(void) |
|---|
| 135 | 135 | { |
|---|
| .. | .. |
|---|
| 139 | 139 | |
|---|
| 140 | 140 | static void _hpet_print_config(const char *function, int line) |
|---|
| 141 | 141 | { |
|---|
| 142 | | - u32 i, timers, l, h; |
|---|
| 143 | | - printk(KERN_INFO "hpet: %s(%d):\n", function, line); |
|---|
| 144 | | - l = hpet_readl(HPET_ID); |
|---|
| 145 | | - h = hpet_readl(HPET_PERIOD); |
|---|
| 146 | | - timers = ((l & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; |
|---|
| 147 | | - printk(KERN_INFO "hpet: ID: 0x%x, PERIOD: 0x%x\n", l, h); |
|---|
| 148 | | - l = hpet_readl(HPET_CFG); |
|---|
| 149 | | - h = hpet_readl(HPET_STATUS); |
|---|
| 150 | | - printk(KERN_INFO "hpet: CFG: 0x%x, STATUS: 0x%x\n", l, h); |
|---|
| 142 | + u32 i, id, period, cfg, status, channels, l, h; |
|---|
| 143 | + |
|---|
| 144 | + pr_info("%s(%d):\n", function, line); |
|---|
| 145 | + |
|---|
| 146 | + id = hpet_readl(HPET_ID); |
|---|
| 147 | + period = hpet_readl(HPET_PERIOD); |
|---|
| 148 | + pr_info("ID: 0x%x, PERIOD: 0x%x\n", id, period); |
|---|
| 149 | + |
|---|
| 150 | + cfg = hpet_readl(HPET_CFG); |
|---|
| 151 | + status = hpet_readl(HPET_STATUS); |
|---|
| 152 | + pr_info("CFG: 0x%x, STATUS: 0x%x\n", cfg, status); |
|---|
| 153 | + |
|---|
| 151 | 154 | l = hpet_readl(HPET_COUNTER); |
|---|
| 152 | 155 | h = hpet_readl(HPET_COUNTER+4); |
|---|
| 153 | | - printk(KERN_INFO "hpet: COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); |
|---|
| 156 | + pr_info("COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); |
|---|
| 154 | 157 | |
|---|
| 155 | | - for (i = 0; i < timers; i++) { |
|---|
| 158 | + channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; |
|---|
| 159 | + |
|---|
| 160 | + for (i = 0; i < channels; i++) { |
|---|
| 156 | 161 | l = hpet_readl(HPET_Tn_CFG(i)); |
|---|
| 157 | 162 | h = hpet_readl(HPET_Tn_CFG(i)+4); |
|---|
| 158 | | - printk(KERN_INFO "hpet: T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", |
|---|
| 159 | | - i, l, h); |
|---|
| 163 | + pr_info("T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", i, l, h); |
|---|
| 164 | + |
|---|
| 160 | 165 | l = hpet_readl(HPET_Tn_CMP(i)); |
|---|
| 161 | 166 | h = hpet_readl(HPET_Tn_CMP(i)+4); |
|---|
| 162 | | - printk(KERN_INFO "hpet: T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", |
|---|
| 163 | | - i, l, h); |
|---|
| 167 | + pr_info("T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", i, l, h); |
|---|
| 168 | + |
|---|
| 164 | 169 | l = hpet_readl(HPET_Tn_ROUTE(i)); |
|---|
| 165 | 170 | h = hpet_readl(HPET_Tn_ROUTE(i)+4); |
|---|
| 166 | | - printk(KERN_INFO "hpet: T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", |
|---|
| 167 | | - i, l, h); |
|---|
| 171 | + pr_info("T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", i, l, h); |
|---|
| 168 | 172 | } |
|---|
| 169 | 173 | } |
|---|
| 170 | 174 | |
|---|
| .. | .. |
|---|
| 175 | 179 | } while (0) |
|---|
| 176 | 180 | |
|---|
| 177 | 181 | /* |
|---|
| 178 | | - * When the hpet driver (/dev/hpet) is enabled, we need to reserve |
|---|
| 182 | + * When the HPET driver (/dev/hpet) is enabled, we need to reserve |
|---|
| 179 | 183 | * timer 0 and timer 1 in case of RTC emulation. |
|---|
| 180 | 184 | */ |
|---|
| 181 | 185 | #ifdef CONFIG_HPET |
|---|
| 182 | 186 | |
|---|
| 183 | | -static void hpet_reserve_msi_timers(struct hpet_data *hd); |
|---|
| 184 | | - |
|---|
| 185 | | -static void hpet_reserve_platform_timers(unsigned int id) |
|---|
| 187 | +static void __init hpet_reserve_platform_timers(void) |
|---|
| 186 | 188 | { |
|---|
| 187 | | - struct hpet __iomem *hpet = hpet_virt_address; |
|---|
| 188 | | - struct hpet_timer __iomem *timer = &hpet->hpet_timers[2]; |
|---|
| 189 | | - unsigned int nrtimers, i; |
|---|
| 190 | 189 | struct hpet_data hd; |
|---|
| 191 | | - |
|---|
| 192 | | - nrtimers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; |
|---|
| 190 | + unsigned int i; |
|---|
| 193 | 191 | |
|---|
| 194 | 192 | memset(&hd, 0, sizeof(hd)); |
|---|
| 195 | 193 | hd.hd_phys_address = hpet_address; |
|---|
| 196 | | - hd.hd_address = hpet; |
|---|
| 197 | | - hd.hd_nirqs = nrtimers; |
|---|
| 198 | | - hpet_reserve_timer(&hd, 0); |
|---|
| 199 | | - |
|---|
| 200 | | -#ifdef CONFIG_HPET_EMULATE_RTC |
|---|
| 201 | | - hpet_reserve_timer(&hd, 1); |
|---|
| 202 | | -#endif |
|---|
| 194 | + hd.hd_address = hpet_virt_address; |
|---|
| 195 | + hd.hd_nirqs = hpet_base.nr_channels; |
|---|
| 203 | 196 | |
|---|
| 204 | 197 | /* |
|---|
| 205 | 198 | * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254 |
|---|
| .. | .. |
|---|
| 209 | 202 | hd.hd_irq[0] = HPET_LEGACY_8254; |
|---|
| 210 | 203 | hd.hd_irq[1] = HPET_LEGACY_RTC; |
|---|
| 211 | 204 | |
|---|
| 212 | | - for (i = 2; i < nrtimers; timer++, i++) { |
|---|
| 213 | | - hd.hd_irq[i] = (readl(&timer->hpet_config) & |
|---|
| 214 | | - Tn_INT_ROUTE_CNF_MASK) >> Tn_INT_ROUTE_CNF_SHIFT; |
|---|
| 205 | + for (i = 0; i < hpet_base.nr_channels; i++) { |
|---|
| 206 | + struct hpet_channel *hc = hpet_base.channels + i; |
|---|
| 207 | + |
|---|
| 208 | + if (i >= 2) |
|---|
| 209 | + hd.hd_irq[i] = hc->irq; |
|---|
| 210 | + |
|---|
| 211 | + switch (hc->mode) { |
|---|
| 212 | + case HPET_MODE_UNUSED: |
|---|
| 213 | + case HPET_MODE_DEVICE: |
|---|
| 214 | + hc->mode = HPET_MODE_DEVICE; |
|---|
| 215 | + break; |
|---|
| 216 | + case HPET_MODE_CLOCKEVT: |
|---|
| 217 | + case HPET_MODE_LEGACY: |
|---|
| 218 | + hpet_reserve_timer(&hd, hc->num); |
|---|
| 219 | + break; |
|---|
| 220 | + } |
|---|
| 215 | 221 | } |
|---|
| 216 | 222 | |
|---|
| 217 | | - hpet_reserve_msi_timers(&hd); |
|---|
| 218 | | - |
|---|
| 219 | 223 | hpet_alloc(&hd); |
|---|
| 220 | | - |
|---|
| 221 | 224 | } |
|---|
| 225 | + |
|---|
| 226 | +static void __init hpet_select_device_channel(void) |
|---|
| 227 | +{ |
|---|
| 228 | + int i; |
|---|
| 229 | + |
|---|
| 230 | + for (i = 0; i < hpet_base.nr_channels; i++) { |
|---|
| 231 | + struct hpet_channel *hc = hpet_base.channels + i; |
|---|
| 232 | + |
|---|
| 233 | + /* Associate the first unused channel to /dev/hpet */ |
|---|
| 234 | + if (hc->mode == HPET_MODE_UNUSED) { |
|---|
| 235 | + hc->mode = HPET_MODE_DEVICE; |
|---|
| 236 | + return; |
|---|
| 237 | + } |
|---|
| 238 | + } |
|---|
| 239 | +} |
|---|
| 240 | + |
|---|
| 222 | 241 | #else |
|---|
| 223 | | -static void hpet_reserve_platform_timers(unsigned int id) { } |
|---|
| 242 | +static inline void hpet_reserve_platform_timers(void) { } |
|---|
| 243 | +static inline void hpet_select_device_channel(void) {} |
|---|
| 224 | 244 | #endif |
|---|
| 225 | 245 | |
|---|
| 226 | | -/* |
|---|
| 227 | | - * Common hpet info |
|---|
| 228 | | - */ |
|---|
| 229 | | -static unsigned long hpet_freq; |
|---|
| 230 | | - |
|---|
| 231 | | -static struct clock_event_device hpet_clockevent; |
|---|
| 232 | | - |
|---|
| 246 | +/* Common HPET functions */ |
|---|
| 233 | 247 | static void hpet_stop_counter(void) |
|---|
| 234 | 248 | { |
|---|
| 235 | 249 | u32 cfg = hpet_readl(HPET_CFG); |
|---|
| 250 | + |
|---|
| 236 | 251 | cfg &= ~HPET_CFG_ENABLE; |
|---|
| 237 | 252 | hpet_writel(cfg, HPET_CFG); |
|---|
| 238 | 253 | } |
|---|
| .. | .. |
|---|
| 246 | 261 | static void hpet_start_counter(void) |
|---|
| 247 | 262 | { |
|---|
| 248 | 263 | unsigned int cfg = hpet_readl(HPET_CFG); |
|---|
| 264 | + |
|---|
| 249 | 265 | cfg |= HPET_CFG_ENABLE; |
|---|
| 250 | 266 | hpet_writel(cfg, HPET_CFG); |
|---|
| 251 | 267 | } |
|---|
| .. | .. |
|---|
| 277 | 293 | hpet_legacy_int_enabled = true; |
|---|
| 278 | 294 | } |
|---|
| 279 | 295 | |
|---|
| 280 | | -static void hpet_legacy_clockevent_register(void) |
|---|
| 296 | +static int hpet_clkevt_set_state_periodic(struct clock_event_device *evt) |
|---|
| 281 | 297 | { |
|---|
| 282 | | - /* Start HPET legacy interrupts */ |
|---|
| 283 | | - hpet_enable_legacy_int(); |
|---|
| 284 | | - |
|---|
| 285 | | - /* |
|---|
| 286 | | - * Start hpet with the boot cpu mask and make it |
|---|
| 287 | | - * global after the IO_APIC has been initialized. |
|---|
| 288 | | - */ |
|---|
| 289 | | - hpet_clockevent.cpumask = cpumask_of(boot_cpu_data.cpu_index); |
|---|
| 290 | | - clockevents_config_and_register(&hpet_clockevent, hpet_freq, |
|---|
| 291 | | - HPET_MIN_PROG_DELTA, 0x7FFFFFFF); |
|---|
| 292 | | - global_clock_event = &hpet_clockevent; |
|---|
| 293 | | - printk(KERN_DEBUG "hpet clockevent registered\n"); |
|---|
| 294 | | -} |
|---|
| 295 | | - |
|---|
| 296 | | -static int hpet_set_periodic(struct clock_event_device *evt, int timer) |
|---|
| 297 | | -{ |
|---|
| 298 | + unsigned int channel = clockevent_to_channel(evt)->num; |
|---|
| 298 | 299 | unsigned int cfg, cmp, now; |
|---|
| 299 | 300 | uint64_t delta; |
|---|
| 300 | 301 | |
|---|
| .. | .. |
|---|
| 303 | 304 | delta >>= evt->shift; |
|---|
| 304 | 305 | now = hpet_readl(HPET_COUNTER); |
|---|
| 305 | 306 | cmp = now + (unsigned int)delta; |
|---|
| 306 | | - cfg = hpet_readl(HPET_Tn_CFG(timer)); |
|---|
| 307 | + cfg = hpet_readl(HPET_Tn_CFG(channel)); |
|---|
| 307 | 308 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | |
|---|
| 308 | 309 | HPET_TN_32BIT; |
|---|
| 309 | | - hpet_writel(cfg, HPET_Tn_CFG(timer)); |
|---|
| 310 | | - hpet_writel(cmp, HPET_Tn_CMP(timer)); |
|---|
| 310 | + hpet_writel(cfg, HPET_Tn_CFG(channel)); |
|---|
| 311 | + hpet_writel(cmp, HPET_Tn_CMP(channel)); |
|---|
| 311 | 312 | udelay(1); |
|---|
| 312 | 313 | /* |
|---|
| 313 | 314 | * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL |
|---|
| .. | .. |
|---|
| 316 | 317 | * (See AMD-8111 HyperTransport I/O Hub Data Sheet, |
|---|
| 317 | 318 | * Publication # 24674) |
|---|
| 318 | 319 | */ |
|---|
| 319 | | - hpet_writel((unsigned int)delta, HPET_Tn_CMP(timer)); |
|---|
| 320 | + hpet_writel((unsigned int)delta, HPET_Tn_CMP(channel)); |
|---|
| 320 | 321 | hpet_start_counter(); |
|---|
| 321 | 322 | hpet_print_config(); |
|---|
| 322 | 323 | |
|---|
| 323 | 324 | return 0; |
|---|
| 324 | 325 | } |
|---|
| 325 | 326 | |
|---|
| 326 | | -static int hpet_set_oneshot(struct clock_event_device *evt, int timer) |
|---|
| 327 | +static int hpet_clkevt_set_state_oneshot(struct clock_event_device *evt) |
|---|
| 327 | 328 | { |
|---|
| 329 | + unsigned int channel = clockevent_to_channel(evt)->num; |
|---|
| 328 | 330 | unsigned int cfg; |
|---|
| 329 | 331 | |
|---|
| 330 | | - cfg = hpet_readl(HPET_Tn_CFG(timer)); |
|---|
| 332 | + cfg = hpet_readl(HPET_Tn_CFG(channel)); |
|---|
| 331 | 333 | cfg &= ~HPET_TN_PERIODIC; |
|---|
| 332 | 334 | cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; |
|---|
| 333 | | - hpet_writel(cfg, HPET_Tn_CFG(timer)); |
|---|
| 335 | + hpet_writel(cfg, HPET_Tn_CFG(channel)); |
|---|
| 334 | 336 | |
|---|
| 335 | 337 | return 0; |
|---|
| 336 | 338 | } |
|---|
| 337 | 339 | |
|---|
| 338 | | -static int hpet_shutdown(struct clock_event_device *evt, int timer) |
|---|
| 340 | +static int hpet_clkevt_set_state_shutdown(struct clock_event_device *evt) |
|---|
| 339 | 341 | { |
|---|
| 342 | + unsigned int channel = clockevent_to_channel(evt)->num; |
|---|
| 340 | 343 | unsigned int cfg; |
|---|
| 341 | 344 | |
|---|
| 342 | | - cfg = hpet_readl(HPET_Tn_CFG(timer)); |
|---|
| 345 | + cfg = hpet_readl(HPET_Tn_CFG(channel)); |
|---|
| 343 | 346 | cfg &= ~HPET_TN_ENABLE; |
|---|
| 344 | | - hpet_writel(cfg, HPET_Tn_CFG(timer)); |
|---|
| 347 | + hpet_writel(cfg, HPET_Tn_CFG(channel)); |
|---|
| 345 | 348 | |
|---|
| 346 | 349 | return 0; |
|---|
| 347 | 350 | } |
|---|
| 348 | 351 | |
|---|
| 349 | | -static int hpet_resume(struct clock_event_device *evt) |
|---|
| 352 | +static int hpet_clkevt_legacy_resume(struct clock_event_device *evt) |
|---|
| 350 | 353 | { |
|---|
| 351 | 354 | hpet_enable_legacy_int(); |
|---|
| 352 | 355 | hpet_print_config(); |
|---|
| 353 | 356 | return 0; |
|---|
| 354 | 357 | } |
|---|
| 355 | 358 | |
|---|
| 356 | | -static int hpet_next_event(unsigned long delta, |
|---|
| 357 | | - struct clock_event_device *evt, int timer) |
|---|
| 359 | +static int |
|---|
| 360 | +hpet_clkevt_set_next_event(unsigned long delta, struct clock_event_device *evt) |
|---|
| 358 | 361 | { |
|---|
| 362 | + unsigned int channel = clockevent_to_channel(evt)->num; |
|---|
| 359 | 363 | u32 cnt; |
|---|
| 360 | 364 | s32 res; |
|---|
| 361 | 365 | |
|---|
| 362 | 366 | cnt = hpet_readl(HPET_COUNTER); |
|---|
| 363 | 367 | cnt += (u32) delta; |
|---|
| 364 | | - hpet_writel(cnt, HPET_Tn_CMP(timer)); |
|---|
| 368 | + hpet_writel(cnt, HPET_Tn_CMP(channel)); |
|---|
| 365 | 369 | |
|---|
| 366 | 370 | /* |
|---|
| 367 | 371 | * HPETs are a complete disaster. The compare register is |
|---|
| .. | .. |
|---|
| 390 | 394 | return res < HPET_MIN_CYCLES ? -ETIME : 0; |
|---|
| 391 | 395 | } |
|---|
| 392 | 396 | |
|---|
| 393 | | -static int hpet_legacy_shutdown(struct clock_event_device *evt) |
|---|
| 397 | +static void hpet_init_clockevent(struct hpet_channel *hc, unsigned int rating) |
|---|
| 394 | 398 | { |
|---|
| 395 | | - return hpet_shutdown(evt, 0); |
|---|
| 399 | + struct clock_event_device *evt = &hc->evt; |
|---|
| 400 | + |
|---|
| 401 | + evt->rating = rating; |
|---|
| 402 | + evt->irq = hc->irq; |
|---|
| 403 | + evt->name = hc->name; |
|---|
| 404 | + evt->cpumask = cpumask_of(hc->cpu); |
|---|
| 405 | + evt->set_state_oneshot = hpet_clkevt_set_state_oneshot; |
|---|
| 406 | + evt->set_next_event = hpet_clkevt_set_next_event; |
|---|
| 407 | + evt->set_state_shutdown = hpet_clkevt_set_state_shutdown; |
|---|
| 408 | + |
|---|
| 409 | + evt->features = CLOCK_EVT_FEAT_ONESHOT; |
|---|
| 410 | + if (hc->boot_cfg & HPET_TN_PERIODIC) { |
|---|
| 411 | + evt->features |= CLOCK_EVT_FEAT_PERIODIC; |
|---|
| 412 | + evt->set_state_periodic = hpet_clkevt_set_state_periodic; |
|---|
| 413 | + } |
|---|
| 396 | 414 | } |
|---|
| 397 | 415 | |
|---|
| 398 | | -static int hpet_legacy_set_oneshot(struct clock_event_device *evt) |
|---|
| 416 | +static void __init hpet_legacy_clockevent_register(struct hpet_channel *hc) |
|---|
| 399 | 417 | { |
|---|
| 400 | | - return hpet_set_oneshot(evt, 0); |
|---|
| 401 | | -} |
|---|
| 418 | + /* |
|---|
| 419 | + * Start HPET with the boot CPU's cpumask and make it global after |
|---|
| 420 | + * the IO_APIC has been initialized. |
|---|
| 421 | + */ |
|---|
| 422 | + hc->cpu = boot_cpu_data.cpu_index; |
|---|
| 423 | + strncpy(hc->name, "hpet", sizeof(hc->name)); |
|---|
| 424 | + hpet_init_clockevent(hc, 50); |
|---|
| 402 | 425 | |
|---|
| 403 | | -static int hpet_legacy_set_periodic(struct clock_event_device *evt) |
|---|
| 404 | | -{ |
|---|
| 405 | | - return hpet_set_periodic(evt, 0); |
|---|
| 406 | | -} |
|---|
| 426 | + hc->evt.tick_resume = hpet_clkevt_legacy_resume; |
|---|
| 407 | 427 | |
|---|
| 408 | | -static int hpet_legacy_resume(struct clock_event_device *evt) |
|---|
| 409 | | -{ |
|---|
| 410 | | - return hpet_resume(evt); |
|---|
| 411 | | -} |
|---|
| 428 | + /* |
|---|
| 429 | + * Legacy horrors and sins from the past. HPET used periodic mode |
|---|
| 430 | + * unconditionally forever on the legacy channel 0. Removing the |
|---|
| 431 | + * below hack and using the conditional in hpet_init_clockevent() |
|---|
| 432 | + * makes at least Qemu and one hardware machine fail to boot. |
|---|
| 433 | + * There are two issues which cause the boot failure: |
|---|
| 434 | + * |
|---|
| 435 | + * #1 After the timer delivery test in IOAPIC and the IOAPIC setup |
|---|
| 436 | + * the next interrupt is not delivered despite the HPET channel |
|---|
| 437 | + * being programmed correctly. Reprogramming the HPET after |
|---|
| 438 | + * switching to IOAPIC makes it work again. After fixing this, |
|---|
| 439 | + * the next issue surfaces: |
|---|
| 440 | + * |
|---|
| 441 | + * #2 Due to the unconditional periodic mode availability the Local |
|---|
| 442 | + * APIC timer calibration can hijack the global clockevents |
|---|
| 443 | + * event handler without causing damage. Using oneshot at this |
|---|
| 444 | + * stage makes if hang because the HPET does not get |
|---|
| 445 | + * reprogrammed due to the handler hijacking. Duh, stupid me! |
|---|
| 446 | + * |
|---|
| 447 | + * Both issues require major surgery and especially the kick HPET |
|---|
| 448 | + * again after enabling IOAPIC results in really nasty hackery. |
|---|
| 449 | + * This 'assume periodic works' magic has survived since HPET |
|---|
| 450 | + * support got added, so it's questionable whether this should be |
|---|
| 451 | + * fixed. Both Qemu and the failing hardware machine support |
|---|
| 452 | + * periodic mode despite the fact that both don't advertise it in |
|---|
| 453 | + * the configuration register and both need that extra kick after |
|---|
| 454 | + * switching to IOAPIC. Seems to be a feature... |
|---|
| 455 | + */ |
|---|
| 456 | + hc->evt.features |= CLOCK_EVT_FEAT_PERIODIC; |
|---|
| 457 | + hc->evt.set_state_periodic = hpet_clkevt_set_state_periodic; |
|---|
| 412 | 458 | |
|---|
| 413 | | -static int hpet_legacy_next_event(unsigned long delta, |
|---|
| 414 | | - struct clock_event_device *evt) |
|---|
| 415 | | -{ |
|---|
| 416 | | - return hpet_next_event(delta, evt, 0); |
|---|
| 417 | | -} |
|---|
| 459 | + /* Start HPET legacy interrupts */ |
|---|
| 460 | + hpet_enable_legacy_int(); |
|---|
| 418 | 461 | |
|---|
| 419 | | -/* |
|---|
| 420 | | - * The hpet clock event device |
|---|
| 421 | | - */ |
|---|
| 422 | | -static struct clock_event_device hpet_clockevent = { |
|---|
| 423 | | - .name = "hpet", |
|---|
| 424 | | - .features = CLOCK_EVT_FEAT_PERIODIC | |
|---|
| 425 | | - CLOCK_EVT_FEAT_ONESHOT, |
|---|
| 426 | | - .set_state_periodic = hpet_legacy_set_periodic, |
|---|
| 427 | | - .set_state_oneshot = hpet_legacy_set_oneshot, |
|---|
| 428 | | - .set_state_shutdown = hpet_legacy_shutdown, |
|---|
| 429 | | - .tick_resume = hpet_legacy_resume, |
|---|
| 430 | | - .set_next_event = hpet_legacy_next_event, |
|---|
| 431 | | - .irq = 0, |
|---|
| 432 | | - .rating = 50, |
|---|
| 433 | | -}; |
|---|
| 462 | + clockevents_config_and_register(&hc->evt, hpet_freq, |
|---|
| 463 | + HPET_MIN_PROG_DELTA, 0x7FFFFFFF); |
|---|
| 464 | + global_clock_event = &hc->evt; |
|---|
| 465 | + pr_debug("Clockevent registered\n"); |
|---|
| 466 | +} |
|---|
| 434 | 467 | |
|---|
| 435 | 468 | /* |
|---|
| 436 | 469 | * HPET MSI Support |
|---|
| 437 | 470 | */ |
|---|
| 438 | 471 | #ifdef CONFIG_PCI_MSI |
|---|
| 439 | 472 | |
|---|
| 440 | | -static DEFINE_PER_CPU(struct hpet_dev *, cpu_hpet_dev); |
|---|
| 441 | | -static struct hpet_dev *hpet_devs; |
|---|
| 442 | | -static struct irq_domain *hpet_domain; |
|---|
| 443 | | - |
|---|
| 444 | 473 | void hpet_msi_unmask(struct irq_data *data) |
|---|
| 445 | 474 | { |
|---|
| 446 | | - struct hpet_dev *hdev = irq_data_get_irq_handler_data(data); |
|---|
| 475 | + struct hpet_channel *hc = irq_data_get_irq_handler_data(data); |
|---|
| 447 | 476 | unsigned int cfg; |
|---|
| 448 | 477 | |
|---|
| 449 | | - /* unmask it */ |
|---|
| 450 | | - cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); |
|---|
| 478 | + cfg = hpet_readl(HPET_Tn_CFG(hc->num)); |
|---|
| 451 | 479 | cfg |= HPET_TN_ENABLE | HPET_TN_FSB; |
|---|
| 452 | | - hpet_writel(cfg, HPET_Tn_CFG(hdev->num)); |
|---|
| 480 | + hpet_writel(cfg, HPET_Tn_CFG(hc->num)); |
|---|
| 453 | 481 | } |
|---|
| 454 | 482 | |
|---|
| 455 | 483 | void hpet_msi_mask(struct irq_data *data) |
|---|
| 456 | 484 | { |
|---|
| 457 | | - struct hpet_dev *hdev = irq_data_get_irq_handler_data(data); |
|---|
| 485 | + struct hpet_channel *hc = irq_data_get_irq_handler_data(data); |
|---|
| 458 | 486 | unsigned int cfg; |
|---|
| 459 | 487 | |
|---|
| 460 | | - /* mask it */ |
|---|
| 461 | | - cfg = hpet_readl(HPET_Tn_CFG(hdev->num)); |
|---|
| 488 | + cfg = hpet_readl(HPET_Tn_CFG(hc->num)); |
|---|
| 462 | 489 | cfg &= ~(HPET_TN_ENABLE | HPET_TN_FSB); |
|---|
| 463 | | - hpet_writel(cfg, HPET_Tn_CFG(hdev->num)); |
|---|
| 490 | + hpet_writel(cfg, HPET_Tn_CFG(hc->num)); |
|---|
| 464 | 491 | } |
|---|
| 465 | 492 | |
|---|
| 466 | | -void hpet_msi_write(struct hpet_dev *hdev, struct msi_msg *msg) |
|---|
| 493 | +void hpet_msi_write(struct hpet_channel *hc, struct msi_msg *msg) |
|---|
| 467 | 494 | { |
|---|
| 468 | | - hpet_writel(msg->data, HPET_Tn_ROUTE(hdev->num)); |
|---|
| 469 | | - hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hdev->num) + 4); |
|---|
| 495 | + hpet_writel(msg->data, HPET_Tn_ROUTE(hc->num)); |
|---|
| 496 | + hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hc->num) + 4); |
|---|
| 470 | 497 | } |
|---|
| 471 | 498 | |
|---|
| 472 | | -void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg) |
|---|
| 499 | +static int hpet_clkevt_msi_resume(struct clock_event_device *evt) |
|---|
| 473 | 500 | { |
|---|
| 474 | | - msg->data = hpet_readl(HPET_Tn_ROUTE(hdev->num)); |
|---|
| 475 | | - msg->address_lo = hpet_readl(HPET_Tn_ROUTE(hdev->num) + 4); |
|---|
| 476 | | - msg->address_hi = 0; |
|---|
| 477 | | -} |
|---|
| 478 | | - |
|---|
| 479 | | -static int hpet_msi_shutdown(struct clock_event_device *evt) |
|---|
| 480 | | -{ |
|---|
| 481 | | - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); |
|---|
| 482 | | - |
|---|
| 483 | | - return hpet_shutdown(evt, hdev->num); |
|---|
| 484 | | -} |
|---|
| 485 | | - |
|---|
| 486 | | -static int hpet_msi_set_oneshot(struct clock_event_device *evt) |
|---|
| 487 | | -{ |
|---|
| 488 | | - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); |
|---|
| 489 | | - |
|---|
| 490 | | - return hpet_set_oneshot(evt, hdev->num); |
|---|
| 491 | | -} |
|---|
| 492 | | - |
|---|
| 493 | | -static int hpet_msi_set_periodic(struct clock_event_device *evt) |
|---|
| 494 | | -{ |
|---|
| 495 | | - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); |
|---|
| 496 | | - |
|---|
| 497 | | - return hpet_set_periodic(evt, hdev->num); |
|---|
| 498 | | -} |
|---|
| 499 | | - |
|---|
| 500 | | -static int hpet_msi_resume(struct clock_event_device *evt) |
|---|
| 501 | | -{ |
|---|
| 502 | | - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); |
|---|
| 503 | | - struct irq_data *data = irq_get_irq_data(hdev->irq); |
|---|
| 501 | + struct hpet_channel *hc = clockevent_to_channel(evt); |
|---|
| 502 | + struct irq_data *data = irq_get_irq_data(hc->irq); |
|---|
| 504 | 503 | struct msi_msg msg; |
|---|
| 505 | 504 | |
|---|
| 506 | 505 | /* Restore the MSI msg and unmask the interrupt */ |
|---|
| 507 | 506 | irq_chip_compose_msi_msg(data, &msg); |
|---|
| 508 | | - hpet_msi_write(hdev, &msg); |
|---|
| 507 | + hpet_msi_write(hc, &msg); |
|---|
| 509 | 508 | hpet_msi_unmask(data); |
|---|
| 510 | 509 | return 0; |
|---|
| 511 | 510 | } |
|---|
| 512 | 511 | |
|---|
| 513 | | -static int hpet_msi_next_event(unsigned long delta, |
|---|
| 514 | | - struct clock_event_device *evt) |
|---|
| 512 | +static irqreturn_t hpet_msi_interrupt_handler(int irq, void *data) |
|---|
| 515 | 513 | { |
|---|
| 516 | | - struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); |
|---|
| 517 | | - return hpet_next_event(delta, evt, hdev->num); |
|---|
| 518 | | -} |
|---|
| 514 | + struct hpet_channel *hc = data; |
|---|
| 515 | + struct clock_event_device *evt = &hc->evt; |
|---|
| 519 | 516 | |
|---|
| 520 | | -static irqreturn_t hpet_interrupt_handler(int irq, void *data) |
|---|
| 521 | | -{ |
|---|
| 522 | | - struct hpet_dev *dev = (struct hpet_dev *)data; |
|---|
| 523 | | - struct clock_event_device *hevt = &dev->evt; |
|---|
| 524 | | - |
|---|
| 525 | | - if (!hevt->event_handler) { |
|---|
| 526 | | - printk(KERN_INFO "Spurious HPET timer interrupt on HPET timer %d\n", |
|---|
| 527 | | - dev->num); |
|---|
| 517 | + if (!evt->event_handler) { |
|---|
| 518 | + pr_info("Spurious interrupt HPET channel %d\n", hc->num); |
|---|
| 528 | 519 | return IRQ_HANDLED; |
|---|
| 529 | 520 | } |
|---|
| 530 | 521 | |
|---|
| 531 | | - hevt->event_handler(hevt); |
|---|
| 522 | + evt->event_handler(evt); |
|---|
| 532 | 523 | return IRQ_HANDLED; |
|---|
| 533 | 524 | } |
|---|
| 534 | 525 | |
|---|
| 535 | | -static int hpet_setup_irq(struct hpet_dev *dev) |
|---|
| 526 | +static int hpet_setup_msi_irq(struct hpet_channel *hc) |
|---|
| 536 | 527 | { |
|---|
| 537 | | - |
|---|
| 538 | | - if (request_irq(dev->irq, hpet_interrupt_handler, |
|---|
| 528 | + if (request_irq(hc->irq, hpet_msi_interrupt_handler, |
|---|
| 539 | 529 | IRQF_TIMER | IRQF_NOBALANCING, |
|---|
| 540 | | - dev->name, dev)) |
|---|
| 530 | + hc->name, hc)) |
|---|
| 541 | 531 | return -1; |
|---|
| 542 | 532 | |
|---|
| 543 | | - disable_irq(dev->irq); |
|---|
| 544 | | - irq_set_affinity(dev->irq, cpumask_of(dev->cpu)); |
|---|
| 545 | | - enable_irq(dev->irq); |
|---|
| 533 | + disable_irq(hc->irq); |
|---|
| 534 | + irq_set_affinity(hc->irq, cpumask_of(hc->cpu)); |
|---|
| 535 | + enable_irq(hc->irq); |
|---|
| 546 | 536 | |
|---|
| 547 | | - printk(KERN_DEBUG "hpet: %s irq %d for MSI\n", |
|---|
| 548 | | - dev->name, dev->irq); |
|---|
| 537 | + pr_debug("%s irq %u for MSI\n", hc->name, hc->irq); |
|---|
| 549 | 538 | |
|---|
| 550 | 539 | return 0; |
|---|
| 551 | 540 | } |
|---|
| 552 | 541 | |
|---|
| 553 | | -/* This should be called in specific @cpu */ |
|---|
| 554 | | -static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) |
|---|
| 542 | +/* Invoked from the hotplug callback on @cpu */ |
|---|
| 543 | +static void init_one_hpet_msi_clockevent(struct hpet_channel *hc, int cpu) |
|---|
| 555 | 544 | { |
|---|
| 556 | | - struct clock_event_device *evt = &hdev->evt; |
|---|
| 545 | + struct clock_event_device *evt = &hc->evt; |
|---|
| 557 | 546 | |
|---|
| 558 | | - WARN_ON(cpu != smp_processor_id()); |
|---|
| 559 | | - if (!(hdev->flags & HPET_DEV_VALID)) |
|---|
| 560 | | - return; |
|---|
| 547 | + hc->cpu = cpu; |
|---|
| 548 | + per_cpu(cpu_hpet_channel, cpu) = hc; |
|---|
| 549 | + hpet_setup_msi_irq(hc); |
|---|
| 561 | 550 | |
|---|
| 562 | | - hdev->cpu = cpu; |
|---|
| 563 | | - per_cpu(cpu_hpet_dev, cpu) = hdev; |
|---|
| 564 | | - evt->name = hdev->name; |
|---|
| 565 | | - hpet_setup_irq(hdev); |
|---|
| 566 | | - evt->irq = hdev->irq; |
|---|
| 567 | | - |
|---|
| 568 | | - evt->rating = 110; |
|---|
| 569 | | - evt->features = CLOCK_EVT_FEAT_ONESHOT; |
|---|
| 570 | | - if (hdev->flags & HPET_DEV_PERI_CAP) { |
|---|
| 571 | | - evt->features |= CLOCK_EVT_FEAT_PERIODIC; |
|---|
| 572 | | - evt->set_state_periodic = hpet_msi_set_periodic; |
|---|
| 573 | | - } |
|---|
| 574 | | - |
|---|
| 575 | | - evt->set_state_shutdown = hpet_msi_shutdown; |
|---|
| 576 | | - evt->set_state_oneshot = hpet_msi_set_oneshot; |
|---|
| 577 | | - evt->tick_resume = hpet_msi_resume; |
|---|
| 578 | | - evt->set_next_event = hpet_msi_next_event; |
|---|
| 579 | | - evt->cpumask = cpumask_of(hdev->cpu); |
|---|
| 551 | + hpet_init_clockevent(hc, 110); |
|---|
| 552 | + evt->tick_resume = hpet_clkevt_msi_resume; |
|---|
| 580 | 553 | |
|---|
| 581 | 554 | clockevents_config_and_register(evt, hpet_freq, HPET_MIN_PROG_DELTA, |
|---|
| 582 | 555 | 0x7FFFFFFF); |
|---|
| 583 | 556 | } |
|---|
| 584 | 557 | |
|---|
| 585 | | -#ifdef CONFIG_HPET |
|---|
| 586 | | -/* Reserve at least one timer for userspace (/dev/hpet) */ |
|---|
| 587 | | -#define RESERVE_TIMERS 1 |
|---|
| 588 | | -#else |
|---|
| 589 | | -#define RESERVE_TIMERS 0 |
|---|
| 590 | | -#endif |
|---|
| 591 | | - |
|---|
| 592 | | -static void hpet_msi_capability_lookup(unsigned int start_timer) |
|---|
| 558 | +static struct hpet_channel *hpet_get_unused_clockevent(void) |
|---|
| 593 | 559 | { |
|---|
| 594 | | - unsigned int id; |
|---|
| 595 | | - unsigned int num_timers; |
|---|
| 596 | | - unsigned int num_timers_used = 0; |
|---|
| 597 | | - int i, irq; |
|---|
| 560 | + int i; |
|---|
| 598 | 561 | |
|---|
| 599 | | - if (hpet_msi_disable) |
|---|
| 562 | + for (i = 0; i < hpet_base.nr_channels; i++) { |
|---|
| 563 | + struct hpet_channel *hc = hpet_base.channels + i; |
|---|
| 564 | + |
|---|
| 565 | + if (hc->mode != HPET_MODE_CLOCKEVT || hc->in_use) |
|---|
| 566 | + continue; |
|---|
| 567 | + hc->in_use = 1; |
|---|
| 568 | + return hc; |
|---|
| 569 | + } |
|---|
| 570 | + return NULL; |
|---|
| 571 | +} |
|---|
| 572 | + |
|---|
| 573 | +static int hpet_cpuhp_online(unsigned int cpu) |
|---|
| 574 | +{ |
|---|
| 575 | + struct hpet_channel *hc = hpet_get_unused_clockevent(); |
|---|
| 576 | + |
|---|
| 577 | + if (hc) |
|---|
| 578 | + init_one_hpet_msi_clockevent(hc, cpu); |
|---|
| 579 | + return 0; |
|---|
| 580 | +} |
|---|
| 581 | + |
|---|
| 582 | +static int hpet_cpuhp_dead(unsigned int cpu) |
|---|
| 583 | +{ |
|---|
| 584 | + struct hpet_channel *hc = per_cpu(cpu_hpet_channel, cpu); |
|---|
| 585 | + |
|---|
| 586 | + if (!hc) |
|---|
| 587 | + return 0; |
|---|
| 588 | + free_irq(hc->irq, hc); |
|---|
| 589 | + hc->in_use = 0; |
|---|
| 590 | + per_cpu(cpu_hpet_channel, cpu) = NULL; |
|---|
| 591 | + return 0; |
|---|
| 592 | +} |
|---|
| 593 | + |
|---|
| 594 | +static void __init hpet_select_clockevents(void) |
|---|
| 595 | +{ |
|---|
| 596 | + unsigned int i; |
|---|
| 597 | + |
|---|
| 598 | + hpet_base.nr_clockevents = 0; |
|---|
| 599 | + |
|---|
| 600 | + /* No point if MSI is disabled or CPU has an Always Runing APIC Timer */ |
|---|
| 601 | + if (hpet_msi_disable || boot_cpu_has(X86_FEATURE_ARAT)) |
|---|
| 600 | 602 | return; |
|---|
| 601 | 603 | |
|---|
| 602 | | - if (boot_cpu_has(X86_FEATURE_ARAT)) |
|---|
| 603 | | - return; |
|---|
| 604 | | - id = hpet_readl(HPET_ID); |
|---|
| 605 | | - |
|---|
| 606 | | - num_timers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); |
|---|
| 607 | | - num_timers++; /* Value read out starts from 0 */ |
|---|
| 608 | 604 | hpet_print_config(); |
|---|
| 609 | 605 | |
|---|
| 610 | 606 | hpet_domain = hpet_create_irq_domain(hpet_blockid); |
|---|
| 611 | 607 | if (!hpet_domain) |
|---|
| 612 | 608 | return; |
|---|
| 613 | 609 | |
|---|
| 614 | | - hpet_devs = kcalloc(num_timers, sizeof(struct hpet_dev), GFP_KERNEL); |
|---|
| 615 | | - if (!hpet_devs) |
|---|
| 616 | | - return; |
|---|
| 610 | + for (i = 0; i < hpet_base.nr_channels; i++) { |
|---|
| 611 | + struct hpet_channel *hc = hpet_base.channels + i; |
|---|
| 612 | + int irq; |
|---|
| 617 | 613 | |
|---|
| 618 | | - hpet_num_timers = num_timers; |
|---|
| 619 | | - |
|---|
| 620 | | - for (i = start_timer; i < num_timers - RESERVE_TIMERS; i++) { |
|---|
| 621 | | - struct hpet_dev *hdev = &hpet_devs[num_timers_used]; |
|---|
| 622 | | - unsigned int cfg = hpet_readl(HPET_Tn_CFG(i)); |
|---|
| 623 | | - |
|---|
| 624 | | - /* Only consider HPET timer with MSI support */ |
|---|
| 625 | | - if (!(cfg & HPET_TN_FSB_CAP)) |
|---|
| 614 | + if (hc->mode != HPET_MODE_UNUSED) |
|---|
| 626 | 615 | continue; |
|---|
| 627 | 616 | |
|---|
| 628 | | - hdev->flags = 0; |
|---|
| 629 | | - if (cfg & HPET_TN_PERIODIC_CAP) |
|---|
| 630 | | - hdev->flags |= HPET_DEV_PERI_CAP; |
|---|
| 631 | | - sprintf(hdev->name, "hpet%d", i); |
|---|
| 632 | | - hdev->num = i; |
|---|
| 617 | + /* Only consider HPET channel with MSI support */ |
|---|
| 618 | + if (!(hc->boot_cfg & HPET_TN_FSB_CAP)) |
|---|
| 619 | + continue; |
|---|
| 633 | 620 | |
|---|
| 634 | | - irq = hpet_assign_irq(hpet_domain, hdev, hdev->num); |
|---|
| 621 | + sprintf(hc->name, "hpet%d", i); |
|---|
| 622 | + |
|---|
| 623 | + irq = hpet_assign_irq(hpet_domain, hc, hc->num); |
|---|
| 635 | 624 | if (irq <= 0) |
|---|
| 636 | 625 | continue; |
|---|
| 637 | 626 | |
|---|
| 638 | | - hdev->irq = irq; |
|---|
| 639 | | - hdev->flags |= HPET_DEV_FSB_CAP; |
|---|
| 640 | | - hdev->flags |= HPET_DEV_VALID; |
|---|
| 641 | | - num_timers_used++; |
|---|
| 642 | | - if (num_timers_used == num_possible_cpus()) |
|---|
| 627 | + hc->irq = irq; |
|---|
| 628 | + hc->mode = HPET_MODE_CLOCKEVT; |
|---|
| 629 | + |
|---|
| 630 | + if (++hpet_base.nr_clockevents == num_possible_cpus()) |
|---|
| 643 | 631 | break; |
|---|
| 644 | 632 | } |
|---|
| 645 | 633 | |
|---|
| 646 | | - printk(KERN_INFO "HPET: %d timers in total, %d timers will be used for per-cpu timer\n", |
|---|
| 647 | | - num_timers, num_timers_used); |
|---|
| 634 | + pr_info("%d channels of %d reserved for per-cpu timers\n", |
|---|
| 635 | + hpet_base.nr_channels, hpet_base.nr_clockevents); |
|---|
| 648 | 636 | } |
|---|
| 649 | 637 | |
|---|
| 650 | | -#ifdef CONFIG_HPET |
|---|
| 651 | | -static void hpet_reserve_msi_timers(struct hpet_data *hd) |
|---|
| 652 | | -{ |
|---|
| 653 | | - int i; |
|---|
| 654 | | - |
|---|
| 655 | | - if (!hpet_devs) |
|---|
| 656 | | - return; |
|---|
| 657 | | - |
|---|
| 658 | | - for (i = 0; i < hpet_num_timers; i++) { |
|---|
| 659 | | - struct hpet_dev *hdev = &hpet_devs[i]; |
|---|
| 660 | | - |
|---|
| 661 | | - if (!(hdev->flags & HPET_DEV_VALID)) |
|---|
| 662 | | - continue; |
|---|
| 663 | | - |
|---|
| 664 | | - hd->hd_irq[hdev->num] = hdev->irq; |
|---|
| 665 | | - hpet_reserve_timer(hd, hdev->num); |
|---|
| 666 | | - } |
|---|
| 667 | | -} |
|---|
| 668 | | -#endif |
|---|
| 669 | | - |
|---|
| 670 | | -static struct hpet_dev *hpet_get_unused_timer(void) |
|---|
| 671 | | -{ |
|---|
| 672 | | - int i; |
|---|
| 673 | | - |
|---|
| 674 | | - if (!hpet_devs) |
|---|
| 675 | | - return NULL; |
|---|
| 676 | | - |
|---|
| 677 | | - for (i = 0; i < hpet_num_timers; i++) { |
|---|
| 678 | | - struct hpet_dev *hdev = &hpet_devs[i]; |
|---|
| 679 | | - |
|---|
| 680 | | - if (!(hdev->flags & HPET_DEV_VALID)) |
|---|
| 681 | | - continue; |
|---|
| 682 | | - if (test_and_set_bit(HPET_DEV_USED_BIT, |
|---|
| 683 | | - (unsigned long *)&hdev->flags)) |
|---|
| 684 | | - continue; |
|---|
| 685 | | - return hdev; |
|---|
| 686 | | - } |
|---|
| 687 | | - return NULL; |
|---|
| 688 | | -} |
|---|
| 689 | | - |
|---|
| 690 | | -struct hpet_work_struct { |
|---|
| 691 | | - struct delayed_work work; |
|---|
| 692 | | - struct completion complete; |
|---|
| 693 | | -}; |
|---|
| 694 | | - |
|---|
| 695 | | -static void hpet_work(struct work_struct *w) |
|---|
| 696 | | -{ |
|---|
| 697 | | - struct hpet_dev *hdev; |
|---|
| 698 | | - int cpu = smp_processor_id(); |
|---|
| 699 | | - struct hpet_work_struct *hpet_work; |
|---|
| 700 | | - |
|---|
| 701 | | - hpet_work = container_of(w, struct hpet_work_struct, work.work); |
|---|
| 702 | | - |
|---|
| 703 | | - hdev = hpet_get_unused_timer(); |
|---|
| 704 | | - if (hdev) |
|---|
| 705 | | - init_one_hpet_msi_clockevent(hdev, cpu); |
|---|
| 706 | | - |
|---|
| 707 | | - complete(&hpet_work->complete); |
|---|
| 708 | | -} |
|---|
| 709 | | - |
|---|
| 710 | | -static int hpet_cpuhp_online(unsigned int cpu) |
|---|
| 711 | | -{ |
|---|
| 712 | | - struct hpet_work_struct work; |
|---|
| 713 | | - |
|---|
| 714 | | - INIT_DELAYED_WORK_ONSTACK(&work.work, hpet_work); |
|---|
| 715 | | - init_completion(&work.complete); |
|---|
| 716 | | - /* FIXME: add schedule_work_on() */ |
|---|
| 717 | | - schedule_delayed_work_on(cpu, &work.work, 0); |
|---|
| 718 | | - wait_for_completion(&work.complete); |
|---|
| 719 | | - destroy_delayed_work_on_stack(&work.work); |
|---|
| 720 | | - return 0; |
|---|
| 721 | | -} |
|---|
| 722 | | - |
|---|
| 723 | | -static int hpet_cpuhp_dead(unsigned int cpu) |
|---|
| 724 | | -{ |
|---|
| 725 | | - struct hpet_dev *hdev = per_cpu(cpu_hpet_dev, cpu); |
|---|
| 726 | | - |
|---|
| 727 | | - if (!hdev) |
|---|
| 728 | | - return 0; |
|---|
| 729 | | - free_irq(hdev->irq, hdev); |
|---|
| 730 | | - hdev->flags &= ~HPET_DEV_USED; |
|---|
| 731 | | - per_cpu(cpu_hpet_dev, cpu) = NULL; |
|---|
| 732 | | - return 0; |
|---|
| 733 | | -} |
|---|
| 734 | 638 | #else |
|---|
| 735 | 639 | |
|---|
| 736 | | -static void hpet_msi_capability_lookup(unsigned int start_timer) |
|---|
| 737 | | -{ |
|---|
| 738 | | - return; |
|---|
| 739 | | -} |
|---|
| 740 | | - |
|---|
| 741 | | -#ifdef CONFIG_HPET |
|---|
| 742 | | -static void hpet_reserve_msi_timers(struct hpet_data *hd) |
|---|
| 743 | | -{ |
|---|
| 744 | | - return; |
|---|
| 745 | | -} |
|---|
| 746 | | -#endif |
|---|
| 640 | +static inline void hpet_select_clockevents(void) { } |
|---|
| 747 | 641 | |
|---|
| 748 | 642 | #define hpet_cpuhp_online NULL |
|---|
| 749 | 643 | #define hpet_cpuhp_dead NULL |
|---|
| .. | .. |
|---|
| 757 | 651 | /* |
|---|
| 758 | 652 | * Reading the HPET counter is a very slow operation. If a large number of |
|---|
| 759 | 653 | * CPUs are trying to access the HPET counter simultaneously, it can cause |
|---|
| 760 | | - * massive delay and slow down system performance dramatically. This may |
|---|
| 654 | + * massive delays and slow down system performance dramatically. This may |
|---|
| 761 | 655 | * happen when HPET is the default clock source instead of TSC. For a |
|---|
| 762 | 656 | * really large system with hundreds of CPUs, the slowdown may be so |
|---|
| 763 | | - * severe that it may actually crash the system because of a NMI watchdog |
|---|
| 657 | + * severe, that it can actually crash the system because of a NMI watchdog |
|---|
| 764 | 658 | * soft lockup, for example. |
|---|
| 765 | 659 | * |
|---|
| 766 | 660 | * If multiple CPUs are trying to access the HPET counter at the same time, |
|---|
| .. | .. |
|---|
| 769 | 663 | * |
|---|
| 770 | 664 | * This special feature is only enabled on x86-64 systems. It is unlikely |
|---|
| 771 | 665 | * that 32-bit x86 systems will have enough CPUs to require this feature |
|---|
| 772 | | - * with its associated locking overhead. And we also need 64-bit atomic |
|---|
| 773 | | - * read. |
|---|
| 666 | + * with its associated locking overhead. We also need 64-bit atomic read. |
|---|
| 774 | 667 | * |
|---|
| 775 | | - * The lock and the hpet value are stored together and can be read in a |
|---|
| 668 | + * The lock and the HPET value are stored together and can be read in a |
|---|
| 776 | 669 | * single atomic 64-bit read. It is explicitly assumed that arch_spinlock_t |
|---|
| 777 | 670 | * is 32 bits in size. |
|---|
| 778 | 671 | */ |
|---|
| .. | .. |
|---|
| 861 | 754 | .resume = hpet_resume_counter, |
|---|
| 862 | 755 | }; |
|---|
| 863 | 756 | |
|---|
| 864 | | -static int hpet_clocksource_register(void) |
|---|
| 757 | +/* |
|---|
| 758 | + * AMD SB700 based systems with spread spectrum enabled use a SMM based |
|---|
| 759 | + * HPET emulation to provide proper frequency setting. |
|---|
| 760 | + * |
|---|
| 761 | + * On such systems the SMM code is initialized with the first HPET register |
|---|
| 762 | + * access and takes some time to complete. During this time the config |
|---|
| 763 | + * register reads 0xffffffff. We check for max 1000 loops whether the |
|---|
| 764 | + * config register reads a non-0xffffffff value to make sure that the |
|---|
| 765 | + * HPET is up and running before we proceed any further. |
|---|
| 766 | + * |
|---|
| 767 | + * A counting loop is safe, as the HPET access takes thousands of CPU cycles. |
|---|
| 768 | + * |
|---|
| 769 | + * On non-SB700 based machines this check is only done once and has no |
|---|
| 770 | + * side effects. |
|---|
| 771 | + */ |
|---|
| 772 | +static bool __init hpet_cfg_working(void) |
|---|
| 865 | 773 | { |
|---|
| 866 | | - u64 start, now; |
|---|
| 867 | | - u64 t1; |
|---|
| 774 | + int i; |
|---|
| 868 | 775 | |
|---|
| 869 | | - /* Start the counter */ |
|---|
| 776 | + for (i = 0; i < 1000; i++) { |
|---|
| 777 | + if (hpet_readl(HPET_CFG) != 0xFFFFFFFF) |
|---|
| 778 | + return true; |
|---|
| 779 | + } |
|---|
| 780 | + |
|---|
| 781 | + pr_warn("Config register invalid. Disabling HPET\n"); |
|---|
| 782 | + return false; |
|---|
| 783 | +} |
|---|
| 784 | + |
|---|
| 785 | +static bool __init hpet_counting(void) |
|---|
| 786 | +{ |
|---|
| 787 | + u64 start, now, t1; |
|---|
| 788 | + |
|---|
| 870 | 789 | hpet_restart_counter(); |
|---|
| 871 | 790 | |
|---|
| 872 | | - /* Verify whether hpet counter works */ |
|---|
| 873 | 791 | t1 = hpet_readl(HPET_COUNTER); |
|---|
| 874 | 792 | start = rdtsc(); |
|---|
| 875 | 793 | |
|---|
| .. | .. |
|---|
| 880 | 798 | * 1 GHz == 200us |
|---|
| 881 | 799 | */ |
|---|
| 882 | 800 | do { |
|---|
| 883 | | - rep_nop(); |
|---|
| 801 | + if (t1 != hpet_readl(HPET_COUNTER)) |
|---|
| 802 | + return true; |
|---|
| 884 | 803 | now = rdtsc(); |
|---|
| 885 | 804 | } while ((now - start) < 200000UL); |
|---|
| 886 | 805 | |
|---|
| 887 | | - if (t1 == hpet_readl(HPET_COUNTER)) { |
|---|
| 888 | | - printk(KERN_WARNING |
|---|
| 889 | | - "HPET counter not counting. HPET disabled\n"); |
|---|
| 890 | | - return -ENODEV; |
|---|
| 891 | | - } |
|---|
| 892 | | - |
|---|
| 893 | | - clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq); |
|---|
| 894 | | - return 0; |
|---|
| 806 | + pr_warn("Counter not counting. HPET disabled\n"); |
|---|
| 807 | + return false; |
|---|
| 895 | 808 | } |
|---|
| 896 | 809 | |
|---|
| 897 | | -static u32 *hpet_boot_cfg; |
|---|
| 810 | +static bool __init mwait_pc10_supported(void) |
|---|
| 811 | +{ |
|---|
| 812 | + unsigned int eax, ebx, ecx, mwait_substates; |
|---|
| 813 | + |
|---|
| 814 | + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) |
|---|
| 815 | + return false; |
|---|
| 816 | + |
|---|
| 817 | + if (!cpu_feature_enabled(X86_FEATURE_MWAIT)) |
|---|
| 818 | + return false; |
|---|
| 819 | + |
|---|
| 820 | + if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) |
|---|
| 821 | + return false; |
|---|
| 822 | + |
|---|
| 823 | + cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &mwait_substates); |
|---|
| 824 | + |
|---|
| 825 | + return (ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) && |
|---|
| 826 | + (ecx & CPUID5_ECX_INTERRUPT_BREAK) && |
|---|
| 827 | + (mwait_substates & (0xF << 28)); |
|---|
| 828 | +} |
|---|
| 829 | + |
|---|
| 830 | +/* |
|---|
| 831 | + * Check whether the system supports PC10. If so force disable HPET as that |
|---|
| 832 | + * stops counting in PC10. This check is overbroad as it does not take any |
|---|
| 833 | + * of the following into account: |
|---|
| 834 | + * |
|---|
| 835 | + * - ACPI tables |
|---|
| 836 | + * - Enablement of intel_idle |
|---|
| 837 | + * - Command line arguments which limit intel_idle C-state support |
|---|
| 838 | + * |
|---|
| 839 | + * That's perfectly fine. HPET is a piece of hardware designed by committee |
|---|
| 840 | + * and the only reasons why it is still in use on modern systems is the |
|---|
| 841 | + * fact that it is impossible to reliably query TSC and CPU frequency via |
|---|
| 842 | + * CPUID or firmware. |
|---|
| 843 | + * |
|---|
| 844 | + * If HPET is functional it is useful for calibrating TSC, but this can be |
|---|
| 845 | + * done via PMTIMER as well which seems to be the last remaining timer on |
|---|
| 846 | + * X86/INTEL platforms that has not been completely wreckaged by feature |
|---|
| 847 | + * creep. |
|---|
| 848 | + * |
|---|
| 849 | + * In theory HPET support should be removed altogether, but there are older |
|---|
| 850 | + * systems out there which depend on it because TSC and APIC timer are |
|---|
| 851 | + * dysfunctional in deeper C-states. |
|---|
| 852 | + * |
|---|
| 853 | + * It's only 20 years now that hardware people have been asked to provide |
|---|
| 854 | + * reliable and discoverable facilities which can be used for timekeeping |
|---|
| 855 | + * and per CPU timer interrupts. |
|---|
| 856 | + * |
|---|
| 857 | + * The probability that this problem is going to be solved in the |
|---|
| 858 | + * forseeable future is close to zero, so the kernel has to be cluttered |
|---|
| 859 | + * with heuristics to keep up with the ever growing amount of hardware and |
|---|
| 860 | + * firmware trainwrecks. Hopefully some day hardware people will understand |
|---|
| 861 | + * that the approach of "This can be fixed in software" is not sustainable. |
|---|
| 862 | + * Hope dies last... |
|---|
| 863 | + */ |
|---|
| 864 | +static bool __init hpet_is_pc10_damaged(void) |
|---|
| 865 | +{ |
|---|
| 866 | + unsigned long long pcfg; |
|---|
| 867 | + |
|---|
| 868 | + /* Check whether PC10 substates are supported */ |
|---|
| 869 | + if (!mwait_pc10_supported()) |
|---|
| 870 | + return false; |
|---|
| 871 | + |
|---|
| 872 | + /* Check whether PC10 is enabled in PKG C-state limit */ |
|---|
| 873 | + rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, pcfg); |
|---|
| 874 | + if ((pcfg & 0xF) < 8) |
|---|
| 875 | + return false; |
|---|
| 876 | + |
|---|
| 877 | + if (hpet_force_user) { |
|---|
| 878 | + pr_warn("HPET force enabled via command line, but dysfunctional in PC10.\n"); |
|---|
| 879 | + return false; |
|---|
| 880 | + } |
|---|
| 881 | + |
|---|
| 882 | + pr_info("HPET dysfunctional in PC10. Force disabled.\n"); |
|---|
| 883 | + boot_hpet_disable = true; |
|---|
| 884 | + return true; |
|---|
| 885 | +} |
|---|
| 898 | 886 | |
|---|
| 899 | 887 | /** |
|---|
| 900 | 888 | * hpet_enable - Try to setup the HPET timer. Returns 1 on success. |
|---|
| 901 | 889 | */ |
|---|
| 902 | 890 | int __init hpet_enable(void) |
|---|
| 903 | 891 | { |
|---|
| 904 | | - u32 hpet_period, cfg, id; |
|---|
| 892 | + u32 hpet_period, cfg, id, irq; |
|---|
| 893 | + unsigned int i, channels; |
|---|
| 894 | + struct hpet_channel *hc; |
|---|
| 905 | 895 | u64 freq; |
|---|
| 906 | | - unsigned int i, last; |
|---|
| 907 | 896 | |
|---|
| 908 | 897 | if (!is_hpet_capable()) |
|---|
| 898 | + return 0; |
|---|
| 899 | + |
|---|
| 900 | + if (hpet_is_pc10_damaged()) |
|---|
| 909 | 901 | return 0; |
|---|
| 910 | 902 | |
|---|
| 911 | 903 | hpet_set_mapping(); |
|---|
| 912 | 904 | if (!hpet_virt_address) |
|---|
| 913 | 905 | return 0; |
|---|
| 914 | 906 | |
|---|
| 907 | + /* Validate that the config register is working */ |
|---|
| 908 | + if (!hpet_cfg_working()) |
|---|
| 909 | + goto out_nohpet; |
|---|
| 910 | + |
|---|
| 915 | 911 | /* |
|---|
| 916 | 912 | * Read the period and check for a sane value: |
|---|
| 917 | 913 | */ |
|---|
| 918 | 914 | hpet_period = hpet_readl(HPET_PERIOD); |
|---|
| 919 | | - |
|---|
| 920 | | - /* |
|---|
| 921 | | - * AMD SB700 based systems with spread spectrum enabled use a |
|---|
| 922 | | - * SMM based HPET emulation to provide proper frequency |
|---|
| 923 | | - * setting. The SMM code is initialized with the first HPET |
|---|
| 924 | | - * register access and takes some time to complete. During |
|---|
| 925 | | - * this time the config register reads 0xffffffff. We check |
|---|
| 926 | | - * for max. 1000 loops whether the config register reads a non |
|---|
| 927 | | - * 0xffffffff value to make sure that HPET is up and running |
|---|
| 928 | | - * before we go further. A counting loop is safe, as the HPET |
|---|
| 929 | | - * access takes thousands of CPU cycles. On non SB700 based |
|---|
| 930 | | - * machines this check is only done once and has no side |
|---|
| 931 | | - * effects. |
|---|
| 932 | | - */ |
|---|
| 933 | | - for (i = 0; hpet_readl(HPET_CFG) == 0xFFFFFFFF; i++) { |
|---|
| 934 | | - if (i == 1000) { |
|---|
| 935 | | - printk(KERN_WARNING |
|---|
| 936 | | - "HPET config register value = 0xFFFFFFFF. " |
|---|
| 937 | | - "Disabling HPET\n"); |
|---|
| 938 | | - goto out_nohpet; |
|---|
| 939 | | - } |
|---|
| 940 | | - } |
|---|
| 941 | | - |
|---|
| 942 | 915 | if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD) |
|---|
| 943 | 916 | goto out_nohpet; |
|---|
| 944 | 917 | |
|---|
| 945 | | - /* |
|---|
| 946 | | - * The period is a femto seconds value. Convert it to a |
|---|
| 947 | | - * frequency. |
|---|
| 948 | | - */ |
|---|
| 918 | + /* The period is a femtoseconds value. Convert it to a frequency. */ |
|---|
| 949 | 919 | freq = FSEC_PER_SEC; |
|---|
| 950 | 920 | do_div(freq, hpet_period); |
|---|
| 951 | 921 | hpet_freq = freq; |
|---|
| .. | .. |
|---|
| 957 | 927 | id = hpet_readl(HPET_ID); |
|---|
| 958 | 928 | hpet_print_config(); |
|---|
| 959 | 929 | |
|---|
| 960 | | - last = (id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT; |
|---|
| 930 | + /* This is the HPET channel number which is zero based */ |
|---|
| 931 | + channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; |
|---|
| 961 | 932 | |
|---|
| 962 | | -#ifdef CONFIG_HPET_EMULATE_RTC |
|---|
| 963 | 933 | /* |
|---|
| 964 | 934 | * The legacy routing mode needs at least two channels, tick timer |
|---|
| 965 | 935 | * and the rtc emulation channel. |
|---|
| 966 | 936 | */ |
|---|
| 967 | | - if (!last) |
|---|
| 937 | + if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC) && channels < 2) |
|---|
| 968 | 938 | goto out_nohpet; |
|---|
| 969 | | -#endif |
|---|
| 970 | 939 | |
|---|
| 940 | + hc = kcalloc(channels, sizeof(*hc), GFP_KERNEL); |
|---|
| 941 | + if (!hc) { |
|---|
| 942 | + pr_warn("Disabling HPET.\n"); |
|---|
| 943 | + goto out_nohpet; |
|---|
| 944 | + } |
|---|
| 945 | + hpet_base.channels = hc; |
|---|
| 946 | + hpet_base.nr_channels = channels; |
|---|
| 947 | + |
|---|
| 948 | + /* Read, store and sanitize the global configuration */ |
|---|
| 971 | 949 | cfg = hpet_readl(HPET_CFG); |
|---|
| 972 | | - hpet_boot_cfg = kmalloc_array(last + 2, sizeof(*hpet_boot_cfg), |
|---|
| 973 | | - GFP_KERNEL); |
|---|
| 974 | | - if (hpet_boot_cfg) |
|---|
| 975 | | - *hpet_boot_cfg = cfg; |
|---|
| 976 | | - else |
|---|
| 977 | | - pr_warn("HPET initial state will not be saved\n"); |
|---|
| 950 | + hpet_base.boot_cfg = cfg; |
|---|
| 978 | 951 | cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); |
|---|
| 979 | 952 | hpet_writel(cfg, HPET_CFG); |
|---|
| 980 | 953 | if (cfg) |
|---|
| 981 | | - pr_warn("Unrecognized bits %#x set in global cfg\n", cfg); |
|---|
| 954 | + pr_warn("Global config: Unknown bits %#x\n", cfg); |
|---|
| 982 | 955 | |
|---|
| 983 | | - for (i = 0; i <= last; ++i) { |
|---|
| 956 | + /* Read, store and sanitize the per channel configuration */ |
|---|
| 957 | + for (i = 0; i < channels; i++, hc++) { |
|---|
| 958 | + hc->num = i; |
|---|
| 959 | + |
|---|
| 984 | 960 | cfg = hpet_readl(HPET_Tn_CFG(i)); |
|---|
| 985 | | - if (hpet_boot_cfg) |
|---|
| 986 | | - hpet_boot_cfg[i + 1] = cfg; |
|---|
| 961 | + hc->boot_cfg = cfg; |
|---|
| 962 | + irq = (cfg & Tn_INT_ROUTE_CNF_MASK) >> Tn_INT_ROUTE_CNF_SHIFT; |
|---|
| 963 | + hc->irq = irq; |
|---|
| 964 | + |
|---|
| 987 | 965 | cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB); |
|---|
| 988 | 966 | hpet_writel(cfg, HPET_Tn_CFG(i)); |
|---|
| 967 | + |
|---|
| 989 | 968 | cfg &= ~(HPET_TN_PERIODIC | HPET_TN_PERIODIC_CAP |
|---|
| 990 | 969 | | HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE |
|---|
| 991 | 970 | | HPET_TN_FSB | HPET_TN_FSB_CAP); |
|---|
| 992 | 971 | if (cfg) |
|---|
| 993 | | - pr_warn("Unrecognized bits %#x set in cfg#%u\n", |
|---|
| 994 | | - cfg, i); |
|---|
| 972 | + pr_warn("Channel #%u config: Unknown bits %#x\n", i, cfg); |
|---|
| 995 | 973 | } |
|---|
| 996 | 974 | hpet_print_config(); |
|---|
| 997 | 975 | |
|---|
| 998 | | - if (hpet_clocksource_register()) |
|---|
| 976 | + /* |
|---|
| 977 | + * Validate that the counter is counting. This needs to be done |
|---|
| 978 | + * after sanitizing the config registers to properly deal with |
|---|
| 979 | + * force enabled HPETs. |
|---|
| 980 | + */ |
|---|
| 981 | + if (!hpet_counting()) |
|---|
| 999 | 982 | goto out_nohpet; |
|---|
| 1000 | 983 | |
|---|
| 984 | + clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq); |
|---|
| 985 | + |
|---|
| 1001 | 986 | if (id & HPET_ID_LEGSUP) { |
|---|
| 1002 | | - hpet_legacy_clockevent_register(); |
|---|
| 987 | + hpet_legacy_clockevent_register(&hpet_base.channels[0]); |
|---|
| 988 | + hpet_base.channels[0].mode = HPET_MODE_LEGACY; |
|---|
| 989 | + if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC)) |
|---|
| 990 | + hpet_base.channels[1].mode = HPET_MODE_LEGACY; |
|---|
| 1003 | 991 | return 1; |
|---|
| 1004 | 992 | } |
|---|
| 1005 | 993 | return 0; |
|---|
| 1006 | 994 | |
|---|
| 1007 | 995 | out_nohpet: |
|---|
| 996 | + kfree(hpet_base.channels); |
|---|
| 997 | + hpet_base.channels = NULL; |
|---|
| 998 | + hpet_base.nr_channels = 0; |
|---|
| 1008 | 999 | hpet_clear_mapping(); |
|---|
| 1009 | 1000 | hpet_address = 0; |
|---|
| 1010 | 1001 | return 0; |
|---|
| 1011 | 1002 | } |
|---|
| 1012 | 1003 | |
|---|
| 1013 | 1004 | /* |
|---|
| 1014 | | - * Needs to be late, as the reserve_timer code calls kalloc ! |
|---|
| 1005 | + * The late initialization runs after the PCI quirks have been invoked |
|---|
| 1006 | + * which might have detected a system on which the HPET can be enforced. |
|---|
| 1015 | 1007 | * |
|---|
| 1016 | | - * Not a problem on i386 as hpet_enable is called from late_time_init, |
|---|
| 1017 | | - * but on x86_64 it is necessary ! |
|---|
| 1008 | + * Also, the MSI machinery is not working yet when the HPET is initialized |
|---|
| 1009 | + * early. |
|---|
| 1010 | + * |
|---|
| 1011 | + * If the HPET is enabled, then: |
|---|
| 1012 | + * |
|---|
| 1013 | + * 1) Reserve one channel for /dev/hpet if CONFIG_HPET=y |
|---|
| 1014 | + * 2) Reserve up to num_possible_cpus() channels as per CPU clockevents |
|---|
| 1015 | + * 3) Setup /dev/hpet if CONFIG_HPET=y |
|---|
| 1016 | + * 4) Register hotplug callbacks when clockevents are available |
|---|
| 1018 | 1017 | */ |
|---|
| 1019 | 1018 | static __init int hpet_late_init(void) |
|---|
| 1020 | 1019 | { |
|---|
| 1021 | 1020 | int ret; |
|---|
| 1022 | | - |
|---|
| 1023 | | - if (boot_hpet_disable) |
|---|
| 1024 | | - return -ENODEV; |
|---|
| 1025 | 1021 | |
|---|
| 1026 | 1022 | if (!hpet_address) { |
|---|
| 1027 | 1023 | if (!force_hpet_address) |
|---|
| .. | .. |
|---|
| 1034 | 1030 | if (!hpet_virt_address) |
|---|
| 1035 | 1031 | return -ENODEV; |
|---|
| 1036 | 1032 | |
|---|
| 1037 | | - if (hpet_readl(HPET_ID) & HPET_ID_LEGSUP) |
|---|
| 1038 | | - hpet_msi_capability_lookup(2); |
|---|
| 1039 | | - else |
|---|
| 1040 | | - hpet_msi_capability_lookup(0); |
|---|
| 1041 | | - |
|---|
| 1042 | | - hpet_reserve_platform_timers(hpet_readl(HPET_ID)); |
|---|
| 1033 | + hpet_select_device_channel(); |
|---|
| 1034 | + hpet_select_clockevents(); |
|---|
| 1035 | + hpet_reserve_platform_timers(); |
|---|
| 1043 | 1036 | hpet_print_config(); |
|---|
| 1044 | 1037 | |
|---|
| 1045 | | - if (hpet_msi_disable) |
|---|
| 1038 | + if (!hpet_base.nr_clockevents) |
|---|
| 1046 | 1039 | return 0; |
|---|
| 1047 | 1040 | |
|---|
| 1048 | | - if (boot_cpu_has(X86_FEATURE_ARAT)) |
|---|
| 1049 | | - return 0; |
|---|
| 1050 | | - |
|---|
| 1051 | | - /* This notifier should be called after workqueue is ready */ |
|---|
| 1052 | 1041 | ret = cpuhp_setup_state(CPUHP_AP_X86_HPET_ONLINE, "x86/hpet:online", |
|---|
| 1053 | 1042 | hpet_cpuhp_online, NULL); |
|---|
| 1054 | 1043 | if (ret) |
|---|
| .. | .. |
|---|
| 1067 | 1056 | |
|---|
| 1068 | 1057 | void hpet_disable(void) |
|---|
| 1069 | 1058 | { |
|---|
| 1070 | | - if (is_hpet_capable() && hpet_virt_address) { |
|---|
| 1071 | | - unsigned int cfg = hpet_readl(HPET_CFG), id, last; |
|---|
| 1059 | + unsigned int i; |
|---|
| 1060 | + u32 cfg; |
|---|
| 1072 | 1061 | |
|---|
| 1073 | | - if (hpet_boot_cfg) |
|---|
| 1074 | | - cfg = *hpet_boot_cfg; |
|---|
| 1075 | | - else if (hpet_legacy_int_enabled) { |
|---|
| 1076 | | - cfg &= ~HPET_CFG_LEGACY; |
|---|
| 1077 | | - hpet_legacy_int_enabled = false; |
|---|
| 1078 | | - } |
|---|
| 1079 | | - cfg &= ~HPET_CFG_ENABLE; |
|---|
| 1080 | | - hpet_writel(cfg, HPET_CFG); |
|---|
| 1062 | + if (!is_hpet_capable() || !hpet_virt_address) |
|---|
| 1063 | + return; |
|---|
| 1081 | 1064 | |
|---|
| 1082 | | - if (!hpet_boot_cfg) |
|---|
| 1083 | | - return; |
|---|
| 1065 | + /* Restore boot configuration with the enable bit cleared */ |
|---|
| 1066 | + cfg = hpet_base.boot_cfg; |
|---|
| 1067 | + cfg &= ~HPET_CFG_ENABLE; |
|---|
| 1068 | + hpet_writel(cfg, HPET_CFG); |
|---|
| 1084 | 1069 | |
|---|
| 1085 | | - id = hpet_readl(HPET_ID); |
|---|
| 1086 | | - last = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); |
|---|
| 1070 | + /* Restore the channel boot configuration */ |
|---|
| 1071 | + for (i = 0; i < hpet_base.nr_channels; i++) |
|---|
| 1072 | + hpet_writel(hpet_base.channels[i].boot_cfg, HPET_Tn_CFG(i)); |
|---|
| 1087 | 1073 | |
|---|
| 1088 | | - for (id = 0; id <= last; ++id) |
|---|
| 1089 | | - hpet_writel(hpet_boot_cfg[id + 1], HPET_Tn_CFG(id)); |
|---|
| 1090 | | - |
|---|
| 1091 | | - if (*hpet_boot_cfg & HPET_CFG_ENABLE) |
|---|
| 1092 | | - hpet_writel(*hpet_boot_cfg, HPET_CFG); |
|---|
| 1093 | | - } |
|---|
| 1074 | + /* If the HPET was enabled at boot time, reenable it */ |
|---|
| 1075 | + if (hpet_base.boot_cfg & HPET_CFG_ENABLE) |
|---|
| 1076 | + hpet_writel(hpet_base.boot_cfg, HPET_CFG); |
|---|
| 1094 | 1077 | } |
|---|
| 1095 | 1078 | |
|---|
| 1096 | 1079 | #ifdef CONFIG_HPET_EMULATE_RTC |
|---|
| 1097 | 1080 | |
|---|
| 1098 | | -/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET |
|---|
| 1081 | +/* |
|---|
| 1082 | + * HPET in LegacyReplacement mode eats up the RTC interrupt line. When HPET |
|---|
| 1099 | 1083 | * is enabled, we support RTC interrupt functionality in software. |
|---|
| 1084 | + * |
|---|
| 1100 | 1085 | * RTC has 3 kinds of interrupts: |
|---|
| 1101 | | - * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock |
|---|
| 1102 | | - * is updated |
|---|
| 1103 | | - * 2) Alarm Interrupt - generate an interrupt at a specific time of day |
|---|
| 1104 | | - * 3) Periodic Interrupt - generate periodic interrupt, with frequencies |
|---|
| 1105 | | - * 2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2) |
|---|
| 1106 | | - * (1) and (2) above are implemented using polling at a frequency of |
|---|
| 1107 | | - * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt |
|---|
| 1108 | | - * overhead. (DEFAULT_RTC_INT_FREQ) |
|---|
| 1109 | | - * For (3), we use interrupts at 64Hz or user specified periodic |
|---|
| 1110 | | - * frequency, whichever is higher. |
|---|
| 1086 | + * |
|---|
| 1087 | + * 1) Update Interrupt - generate an interrupt, every second, when the |
|---|
| 1088 | + * RTC clock is updated |
|---|
| 1089 | + * 2) Alarm Interrupt - generate an interrupt at a specific time of day |
|---|
| 1090 | + * 3) Periodic Interrupt - generate periodic interrupt, with frequencies |
|---|
| 1091 | + * 2Hz-8192Hz (2Hz-64Hz for non-root user) (all frequencies in powers of 2) |
|---|
| 1092 | + * |
|---|
| 1093 | + * (1) and (2) above are implemented using polling at a frequency of 64 Hz: |
|---|
| 1094 | + * DEFAULT_RTC_INT_FREQ. |
|---|
| 1095 | + * |
|---|
| 1096 | + * The exact frequency is a tradeoff between accuracy and interrupt overhead. |
|---|
| 1097 | + * |
|---|
| 1098 | + * For (3), we use interrupts at 64 Hz, or the user specified periodic frequency, |
|---|
| 1099 | + * if it's higher. |
|---|
| 1111 | 1100 | */ |
|---|
| 1112 | 1101 | #include <linux/mc146818rtc.h> |
|---|
| 1113 | 1102 | #include <linux/rtc.h> |
|---|
| .. | .. |
|---|
| 1128 | 1117 | static rtc_irq_handler irq_handler; |
|---|
| 1129 | 1118 | |
|---|
| 1130 | 1119 | /* |
|---|
| 1131 | | - * Check that the hpet counter c1 is ahead of the c2 |
|---|
| 1120 | + * Check that the HPET counter c1 is ahead of c2 |
|---|
| 1132 | 1121 | */ |
|---|
| 1133 | 1122 | static inline int hpet_cnt_ahead(u32 c1, u32 c2) |
|---|
| 1134 | 1123 | { |
|---|
| .. | .. |
|---|
| 1166 | 1155 | EXPORT_SYMBOL_GPL(hpet_unregister_irq_handler); |
|---|
| 1167 | 1156 | |
|---|
| 1168 | 1157 | /* |
|---|
| 1169 | | - * Timer 1 for RTC emulation. We use one shot mode, as periodic mode |
|---|
| 1170 | | - * is not supported by all HPET implementations for timer 1. |
|---|
| 1158 | + * Channel 1 for RTC emulation. We use one shot mode, as periodic mode |
|---|
| 1159 | + * is not supported by all HPET implementations for channel 1. |
|---|
| 1171 | 1160 | * |
|---|
| 1172 | 1161 | * hpet_rtc_timer_init() is called when the rtc is initialized. |
|---|
| 1173 | 1162 | */ |
|---|
| .. | .. |
|---|
| 1180 | 1169 | return 0; |
|---|
| 1181 | 1170 | |
|---|
| 1182 | 1171 | if (!hpet_default_delta) { |
|---|
| 1172 | + struct clock_event_device *evt = &hpet_base.channels[0].evt; |
|---|
| 1183 | 1173 | uint64_t clc; |
|---|
| 1184 | 1174 | |
|---|
| 1185 | | - clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; |
|---|
| 1186 | | - clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT; |
|---|
| 1175 | + clc = (uint64_t) evt->mult * NSEC_PER_SEC; |
|---|
| 1176 | + clc >>= evt->shift + DEFAULT_RTC_SHIFT; |
|---|
| 1187 | 1177 | hpet_default_delta = clc; |
|---|
| 1188 | 1178 | } |
|---|
| 1189 | 1179 | |
|---|
| .. | .. |
|---|
| 1212 | 1202 | static void hpet_disable_rtc_channel(void) |
|---|
| 1213 | 1203 | { |
|---|
| 1214 | 1204 | u32 cfg = hpet_readl(HPET_T1_CFG); |
|---|
| 1205 | + |
|---|
| 1215 | 1206 | cfg &= ~HPET_TN_ENABLE; |
|---|
| 1216 | 1207 | hpet_writel(cfg, HPET_T1_CFG); |
|---|
| 1217 | 1208 | } |
|---|
| .. | .. |
|---|
| 1253 | 1244 | } |
|---|
| 1254 | 1245 | EXPORT_SYMBOL_GPL(hpet_set_rtc_irq_bit); |
|---|
| 1255 | 1246 | |
|---|
| 1256 | | -int hpet_set_alarm_time(unsigned char hrs, unsigned char min, |
|---|
| 1257 | | - unsigned char sec) |
|---|
| 1247 | +int hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec) |
|---|
| 1258 | 1248 | { |
|---|
| 1259 | 1249 | if (!is_hpet_enabled()) |
|---|
| 1260 | 1250 | return 0; |
|---|
| .. | .. |
|---|
| 1274 | 1264 | if (!is_hpet_enabled()) |
|---|
| 1275 | 1265 | return 0; |
|---|
| 1276 | 1266 | |
|---|
| 1277 | | - if (freq <= DEFAULT_RTC_INT_FREQ) |
|---|
| 1267 | + if (freq <= DEFAULT_RTC_INT_FREQ) { |
|---|
| 1278 | 1268 | hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq; |
|---|
| 1279 | | - else { |
|---|
| 1280 | | - clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC; |
|---|
| 1269 | + } else { |
|---|
| 1270 | + struct clock_event_device *evt = &hpet_base.channels[0].evt; |
|---|
| 1271 | + |
|---|
| 1272 | + clc = (uint64_t) evt->mult * NSEC_PER_SEC; |
|---|
| 1281 | 1273 | do_div(clc, freq); |
|---|
| 1282 | | - clc >>= hpet_clockevent.shift; |
|---|
| 1274 | + clc >>= evt->shift; |
|---|
| 1283 | 1275 | hpet_pie_delta = clc; |
|---|
| 1284 | 1276 | hpet_pie_limit = 0; |
|---|
| 1285 | 1277 | } |
|---|
| 1278 | + |
|---|
| 1286 | 1279 | return 1; |
|---|
| 1287 | 1280 | } |
|---|
| 1288 | 1281 | EXPORT_SYMBOL_GPL(hpet_set_periodic_freq); |
|---|
| .. | .. |
|---|
| 1320 | 1313 | if (hpet_rtc_flags & RTC_PIE) |
|---|
| 1321 | 1314 | hpet_pie_count += lost_ints; |
|---|
| 1322 | 1315 | if (printk_ratelimit()) |
|---|
| 1323 | | - printk(KERN_WARNING "hpet1: lost %d rtc interrupts\n", |
|---|
| 1324 | | - lost_ints); |
|---|
| 1316 | + pr_warn("Lost %d RTC interrupts\n", lost_ints); |
|---|
| 1325 | 1317 | } |
|---|
| 1326 | 1318 | } |
|---|
| 1327 | 1319 | |
|---|
| .. | .. |
|---|
| 1333 | 1325 | hpet_rtc_timer_reinit(); |
|---|
| 1334 | 1326 | memset(&curr_time, 0, sizeof(struct rtc_time)); |
|---|
| 1335 | 1327 | |
|---|
| 1336 | | - if (hpet_rtc_flags & (RTC_UIE | RTC_AIE)) |
|---|
| 1337 | | - mc146818_get_time(&curr_time); |
|---|
| 1328 | + if (hpet_rtc_flags & (RTC_UIE | RTC_AIE)) { |
|---|
| 1329 | + if (unlikely(mc146818_get_time(&curr_time) < 0)) { |
|---|
| 1330 | + pr_err_ratelimited("unable to read current time from RTC\n"); |
|---|
| 1331 | + return IRQ_HANDLED; |
|---|
| 1332 | + } |
|---|
| 1333 | + } |
|---|
| 1338 | 1334 | |
|---|
| 1339 | 1335 | if (hpet_rtc_flags & RTC_UIE && |
|---|
| 1340 | 1336 | curr_time.tm_sec != hpet_prev_update_sec) { |
|---|
| .. | .. |
|---|
| 1343 | 1339 | hpet_prev_update_sec = curr_time.tm_sec; |
|---|
| 1344 | 1340 | } |
|---|
| 1345 | 1341 | |
|---|
| 1346 | | - if (hpet_rtc_flags & RTC_PIE && |
|---|
| 1347 | | - ++hpet_pie_count >= hpet_pie_limit) { |
|---|
| 1342 | + if (hpet_rtc_flags & RTC_PIE && ++hpet_pie_count >= hpet_pie_limit) { |
|---|
| 1348 | 1343 | rtc_int_flag |= RTC_PF; |
|---|
| 1349 | 1344 | hpet_pie_count = 0; |
|---|
| 1350 | 1345 | } |
|---|
| .. | .. |
|---|
| 1353 | 1348 | (curr_time.tm_sec == hpet_alarm_time.tm_sec) && |
|---|
| 1354 | 1349 | (curr_time.tm_min == hpet_alarm_time.tm_min) && |
|---|
| 1355 | 1350 | (curr_time.tm_hour == hpet_alarm_time.tm_hour)) |
|---|
| 1356 | | - rtc_int_flag |= RTC_AF; |
|---|
| 1351 | + rtc_int_flag |= RTC_AF; |
|---|
| 1357 | 1352 | |
|---|
| 1358 | 1353 | if (rtc_int_flag) { |
|---|
| 1359 | 1354 | rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8)); |
|---|