162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/clockchips.h>
362306a36Sopenharmony_ci#include <linux/interrupt.h>
462306a36Sopenharmony_ci#include <linux/export.h>
562306a36Sopenharmony_ci#include <linux/delay.h>
662306a36Sopenharmony_ci#include <linux/hpet.h>
762306a36Sopenharmony_ci#include <linux/cpu.h>
862306a36Sopenharmony_ci#include <linux/irq.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <asm/irq_remapping.h>
1162306a36Sopenharmony_ci#include <asm/hpet.h>
1262306a36Sopenharmony_ci#include <asm/time.h>
1362306a36Sopenharmony_ci#include <asm/mwait.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#undef  pr_fmt
1662306a36Sopenharmony_ci#define pr_fmt(fmt) "hpet: " fmt
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cienum hpet_mode {
1962306a36Sopenharmony_ci	HPET_MODE_UNUSED,
2062306a36Sopenharmony_ci	HPET_MODE_LEGACY,
2162306a36Sopenharmony_ci	HPET_MODE_CLOCKEVT,
2262306a36Sopenharmony_ci	HPET_MODE_DEVICE,
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct hpet_channel {
2662306a36Sopenharmony_ci	struct clock_event_device	evt;
2762306a36Sopenharmony_ci	unsigned int			num;
2862306a36Sopenharmony_ci	unsigned int			cpu;
2962306a36Sopenharmony_ci	unsigned int			irq;
3062306a36Sopenharmony_ci	unsigned int			in_use;
3162306a36Sopenharmony_ci	enum hpet_mode			mode;
3262306a36Sopenharmony_ci	unsigned int			boot_cfg;
3362306a36Sopenharmony_ci	char				name[10];
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct hpet_base {
3762306a36Sopenharmony_ci	unsigned int			nr_channels;
3862306a36Sopenharmony_ci	unsigned int			nr_clockevents;
3962306a36Sopenharmony_ci	unsigned int			boot_cfg;
4062306a36Sopenharmony_ci	struct hpet_channel		*channels;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define HPET_MASK			CLOCKSOURCE_MASK(32)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define HPET_MIN_CYCLES			128
4662306a36Sopenharmony_ci#define HPET_MIN_PROG_DELTA		(HPET_MIN_CYCLES + (HPET_MIN_CYCLES >> 1))
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * HPET address is set in acpi/boot.c, when an ACPI entry exists
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ciunsigned long				hpet_address;
5262306a36Sopenharmony_ciu8					hpet_blockid; /* OS timer block num */
5362306a36Sopenharmony_cibool					hpet_msi_disable;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_MSI_IRQ
5662306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct hpet_channel *, cpu_hpet_channel);
5762306a36Sopenharmony_cistatic struct irq_domain		*hpet_domain;
5862306a36Sopenharmony_ci#endif
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic void __iomem			*hpet_virt_address;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct hpet_base			hpet_base;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic bool				hpet_legacy_int_enabled;
6562306a36Sopenharmony_cistatic unsigned long			hpet_freq;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cibool					boot_hpet_disable;
6862306a36Sopenharmony_cibool					hpet_force_user;
6962306a36Sopenharmony_cistatic bool				hpet_verbose;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline
7262306a36Sopenharmony_cistruct hpet_channel *clockevent_to_channel(struct clock_event_device *evt)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	return container_of(evt, struct hpet_channel, evt);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ciinline unsigned int hpet_readl(unsigned int a)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	return readl(hpet_virt_address + a);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic inline void hpet_writel(unsigned int d, unsigned int a)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	writel(d, hpet_virt_address + a);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline void hpet_set_mapping(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	hpet_virt_address = ioremap(hpet_address, HPET_MMAP_SIZE);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic inline void hpet_clear_mapping(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	iounmap(hpet_virt_address);
9562306a36Sopenharmony_ci	hpet_virt_address = NULL;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*
9962306a36Sopenharmony_ci * HPET command line enable / disable
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistatic int __init hpet_setup(char *str)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	while (str) {
10462306a36Sopenharmony_ci		char *next = strchr(str, ',');
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		if (next)
10762306a36Sopenharmony_ci			*next++ = 0;
10862306a36Sopenharmony_ci		if (!strncmp("disable", str, 7))
10962306a36Sopenharmony_ci			boot_hpet_disable = true;
11062306a36Sopenharmony_ci		if (!strncmp("force", str, 5))
11162306a36Sopenharmony_ci			hpet_force_user = true;
11262306a36Sopenharmony_ci		if (!strncmp("verbose", str, 7))
11362306a36Sopenharmony_ci			hpet_verbose = true;
11462306a36Sopenharmony_ci		str = next;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	return 1;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci__setup("hpet=", hpet_setup);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int __init disable_hpet(char *str)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	boot_hpet_disable = true;
12362306a36Sopenharmony_ci	return 1;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci__setup("nohpet", disable_hpet);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic inline int is_hpet_capable(void)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	return !boot_hpet_disable && hpet_address;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/**
13362306a36Sopenharmony_ci * is_hpet_enabled - Check whether the legacy HPET timer interrupt is enabled
13462306a36Sopenharmony_ci */
13562306a36Sopenharmony_ciint is_hpet_enabled(void)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	return is_hpet_capable() && hpet_legacy_int_enabled;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(is_hpet_enabled);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic void _hpet_print_config(const char *function, int line)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	u32 i, id, period, cfg, status, channels, l, h;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	pr_info("%s(%d):\n", function, line);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	id = hpet_readl(HPET_ID);
14862306a36Sopenharmony_ci	period = hpet_readl(HPET_PERIOD);
14962306a36Sopenharmony_ci	pr_info("ID: 0x%x, PERIOD: 0x%x\n", id, period);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	cfg = hpet_readl(HPET_CFG);
15262306a36Sopenharmony_ci	status = hpet_readl(HPET_STATUS);
15362306a36Sopenharmony_ci	pr_info("CFG: 0x%x, STATUS: 0x%x\n", cfg, status);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	l = hpet_readl(HPET_COUNTER);
15662306a36Sopenharmony_ci	h = hpet_readl(HPET_COUNTER+4);
15762306a36Sopenharmony_ci	pr_info("COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	for (i = 0; i < channels; i++) {
16262306a36Sopenharmony_ci		l = hpet_readl(HPET_Tn_CFG(i));
16362306a36Sopenharmony_ci		h = hpet_readl(HPET_Tn_CFG(i)+4);
16462306a36Sopenharmony_ci		pr_info("T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", i, l, h);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		l = hpet_readl(HPET_Tn_CMP(i));
16762306a36Sopenharmony_ci		h = hpet_readl(HPET_Tn_CMP(i)+4);
16862306a36Sopenharmony_ci		pr_info("T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", i, l, h);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		l = hpet_readl(HPET_Tn_ROUTE(i));
17162306a36Sopenharmony_ci		h = hpet_readl(HPET_Tn_ROUTE(i)+4);
17262306a36Sopenharmony_ci		pr_info("T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", i, l, h);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci#define hpet_print_config()					\
17762306a36Sopenharmony_cido {								\
17862306a36Sopenharmony_ci	if (hpet_verbose)					\
17962306a36Sopenharmony_ci		_hpet_print_config(__func__, __LINE__);	\
18062306a36Sopenharmony_ci} while (0)
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci * When the HPET driver (/dev/hpet) is enabled, we need to reserve
18462306a36Sopenharmony_ci * timer 0 and timer 1 in case of RTC emulation.
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_ci#ifdef CONFIG_HPET
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void __init hpet_reserve_platform_timers(void)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct hpet_data hd;
19162306a36Sopenharmony_ci	unsigned int i;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	memset(&hd, 0, sizeof(hd));
19462306a36Sopenharmony_ci	hd.hd_phys_address	= hpet_address;
19562306a36Sopenharmony_ci	hd.hd_address		= hpet_virt_address;
19662306a36Sopenharmony_ci	hd.hd_nirqs		= hpet_base.nr_channels;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	/*
19962306a36Sopenharmony_ci	 * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254
20062306a36Sopenharmony_ci	 * is wrong for i8259!) not the output IRQ.  Many BIOS writers
20162306a36Sopenharmony_ci	 * don't bother configuring *any* comparator interrupts.
20262306a36Sopenharmony_ci	 */
20362306a36Sopenharmony_ci	hd.hd_irq[0] = HPET_LEGACY_8254;
20462306a36Sopenharmony_ci	hd.hd_irq[1] = HPET_LEGACY_RTC;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	for (i = 0; i < hpet_base.nr_channels; i++) {
20762306a36Sopenharmony_ci		struct hpet_channel *hc = hpet_base.channels + i;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		if (i >= 2)
21062306a36Sopenharmony_ci			hd.hd_irq[i] = hc->irq;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		switch (hc->mode) {
21362306a36Sopenharmony_ci		case HPET_MODE_UNUSED:
21462306a36Sopenharmony_ci		case HPET_MODE_DEVICE:
21562306a36Sopenharmony_ci			hc->mode = HPET_MODE_DEVICE;
21662306a36Sopenharmony_ci			break;
21762306a36Sopenharmony_ci		case HPET_MODE_CLOCKEVT:
21862306a36Sopenharmony_ci		case HPET_MODE_LEGACY:
21962306a36Sopenharmony_ci			hpet_reserve_timer(&hd, hc->num);
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	hpet_alloc(&hd);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void __init hpet_select_device_channel(void)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	int i;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	for (i = 0; i < hpet_base.nr_channels; i++) {
23262306a36Sopenharmony_ci		struct hpet_channel *hc = hpet_base.channels + i;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		/* Associate the first unused channel to /dev/hpet */
23562306a36Sopenharmony_ci		if (hc->mode == HPET_MODE_UNUSED) {
23662306a36Sopenharmony_ci			hc->mode = HPET_MODE_DEVICE;
23762306a36Sopenharmony_ci			return;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci#else
24362306a36Sopenharmony_cistatic inline void hpet_reserve_platform_timers(void) { }
24462306a36Sopenharmony_cistatic inline void hpet_select_device_channel(void) {}
24562306a36Sopenharmony_ci#endif
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/* Common HPET functions */
24862306a36Sopenharmony_cistatic void hpet_stop_counter(void)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	u32 cfg = hpet_readl(HPET_CFG);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	cfg &= ~HPET_CFG_ENABLE;
25362306a36Sopenharmony_ci	hpet_writel(cfg, HPET_CFG);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void hpet_reset_counter(void)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	hpet_writel(0, HPET_COUNTER);
25962306a36Sopenharmony_ci	hpet_writel(0, HPET_COUNTER + 4);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void hpet_start_counter(void)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	unsigned int cfg = hpet_readl(HPET_CFG);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	cfg |= HPET_CFG_ENABLE;
26762306a36Sopenharmony_ci	hpet_writel(cfg, HPET_CFG);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic void hpet_restart_counter(void)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	hpet_stop_counter();
27362306a36Sopenharmony_ci	hpet_reset_counter();
27462306a36Sopenharmony_ci	hpet_start_counter();
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void hpet_resume_device(void)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	force_hpet_resume();
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic void hpet_resume_counter(struct clocksource *cs)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	hpet_resume_device();
28562306a36Sopenharmony_ci	hpet_restart_counter();
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void hpet_enable_legacy_int(void)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	unsigned int cfg = hpet_readl(HPET_CFG);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	cfg |= HPET_CFG_LEGACY;
29362306a36Sopenharmony_ci	hpet_writel(cfg, HPET_CFG);
29462306a36Sopenharmony_ci	hpet_legacy_int_enabled = true;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int hpet_clkevt_set_state_periodic(struct clock_event_device *evt)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	unsigned int channel = clockevent_to_channel(evt)->num;
30062306a36Sopenharmony_ci	unsigned int cfg, cmp, now;
30162306a36Sopenharmony_ci	uint64_t delta;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	hpet_stop_counter();
30462306a36Sopenharmony_ci	delta = ((uint64_t)(NSEC_PER_SEC / HZ)) * evt->mult;
30562306a36Sopenharmony_ci	delta >>= evt->shift;
30662306a36Sopenharmony_ci	now = hpet_readl(HPET_COUNTER);
30762306a36Sopenharmony_ci	cmp = now + (unsigned int)delta;
30862306a36Sopenharmony_ci	cfg = hpet_readl(HPET_Tn_CFG(channel));
30962306a36Sopenharmony_ci	cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
31062306a36Sopenharmony_ci	       HPET_TN_32BIT;
31162306a36Sopenharmony_ci	hpet_writel(cfg, HPET_Tn_CFG(channel));
31262306a36Sopenharmony_ci	hpet_writel(cmp, HPET_Tn_CMP(channel));
31362306a36Sopenharmony_ci	udelay(1);
31462306a36Sopenharmony_ci	/*
31562306a36Sopenharmony_ci	 * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL
31662306a36Sopenharmony_ci	 * cleared) to T0_CMP to set the period. The HPET_TN_SETVAL
31762306a36Sopenharmony_ci	 * bit is automatically cleared after the first write.
31862306a36Sopenharmony_ci	 * (See AMD-8111 HyperTransport I/O Hub Data Sheet,
31962306a36Sopenharmony_ci	 * Publication # 24674)
32062306a36Sopenharmony_ci	 */
32162306a36Sopenharmony_ci	hpet_writel((unsigned int)delta, HPET_Tn_CMP(channel));
32262306a36Sopenharmony_ci	hpet_start_counter();
32362306a36Sopenharmony_ci	hpet_print_config();
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return 0;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic int hpet_clkevt_set_state_oneshot(struct clock_event_device *evt)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	unsigned int channel = clockevent_to_channel(evt)->num;
33162306a36Sopenharmony_ci	unsigned int cfg;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	cfg = hpet_readl(HPET_Tn_CFG(channel));
33462306a36Sopenharmony_ci	cfg &= ~HPET_TN_PERIODIC;
33562306a36Sopenharmony_ci	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
33662306a36Sopenharmony_ci	hpet_writel(cfg, HPET_Tn_CFG(channel));
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int hpet_clkevt_set_state_shutdown(struct clock_event_device *evt)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	unsigned int channel = clockevent_to_channel(evt)->num;
34462306a36Sopenharmony_ci	unsigned int cfg;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	cfg = hpet_readl(HPET_Tn_CFG(channel));
34762306a36Sopenharmony_ci	cfg &= ~HPET_TN_ENABLE;
34862306a36Sopenharmony_ci	hpet_writel(cfg, HPET_Tn_CFG(channel));
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	return 0;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int hpet_clkevt_legacy_resume(struct clock_event_device *evt)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	hpet_enable_legacy_int();
35662306a36Sopenharmony_ci	hpet_print_config();
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int
36162306a36Sopenharmony_cihpet_clkevt_set_next_event(unsigned long delta, struct clock_event_device *evt)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	unsigned int channel = clockevent_to_channel(evt)->num;
36462306a36Sopenharmony_ci	u32 cnt;
36562306a36Sopenharmony_ci	s32 res;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	cnt = hpet_readl(HPET_COUNTER);
36862306a36Sopenharmony_ci	cnt += (u32) delta;
36962306a36Sopenharmony_ci	hpet_writel(cnt, HPET_Tn_CMP(channel));
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/*
37262306a36Sopenharmony_ci	 * HPETs are a complete disaster. The compare register is
37362306a36Sopenharmony_ci	 * based on a equal comparison and neither provides a less
37462306a36Sopenharmony_ci	 * than or equal functionality (which would require to take
37562306a36Sopenharmony_ci	 * the wraparound into account) nor a simple count down event
37662306a36Sopenharmony_ci	 * mode. Further the write to the comparator register is
37762306a36Sopenharmony_ci	 * delayed internally up to two HPET clock cycles in certain
37862306a36Sopenharmony_ci	 * chipsets (ATI, ICH9,10). Some newer AMD chipsets have even
37962306a36Sopenharmony_ci	 * longer delays. We worked around that by reading back the
38062306a36Sopenharmony_ci	 * compare register, but that required another workaround for
38162306a36Sopenharmony_ci	 * ICH9,10 chips where the first readout after write can
38262306a36Sopenharmony_ci	 * return the old stale value. We already had a minimum
38362306a36Sopenharmony_ci	 * programming delta of 5us enforced, but a NMI or SMI hitting
38462306a36Sopenharmony_ci	 * between the counter readout and the comparator write can
38562306a36Sopenharmony_ci	 * move us behind that point easily. Now instead of reading
38662306a36Sopenharmony_ci	 * the compare register back several times, we make the ETIME
38762306a36Sopenharmony_ci	 * decision based on the following: Return ETIME if the
38862306a36Sopenharmony_ci	 * counter value after the write is less than HPET_MIN_CYCLES
38962306a36Sopenharmony_ci	 * away from the event or if the counter is already ahead of
39062306a36Sopenharmony_ci	 * the event. The minimum programming delta for the generic
39162306a36Sopenharmony_ci	 * clockevents code is set to 1.5 * HPET_MIN_CYCLES.
39262306a36Sopenharmony_ci	 */
39362306a36Sopenharmony_ci	res = (s32)(cnt - hpet_readl(HPET_COUNTER));
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return res < HPET_MIN_CYCLES ? -ETIME : 0;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void hpet_init_clockevent(struct hpet_channel *hc, unsigned int rating)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct clock_event_device *evt = &hc->evt;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	evt->rating		= rating;
40362306a36Sopenharmony_ci	evt->irq		= hc->irq;
40462306a36Sopenharmony_ci	evt->name		= hc->name;
40562306a36Sopenharmony_ci	evt->cpumask		= cpumask_of(hc->cpu);
40662306a36Sopenharmony_ci	evt->set_state_oneshot	= hpet_clkevt_set_state_oneshot;
40762306a36Sopenharmony_ci	evt->set_next_event	= hpet_clkevt_set_next_event;
40862306a36Sopenharmony_ci	evt->set_state_shutdown	= hpet_clkevt_set_state_shutdown;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	evt->features = CLOCK_EVT_FEAT_ONESHOT;
41162306a36Sopenharmony_ci	if (hc->boot_cfg & HPET_TN_PERIODIC) {
41262306a36Sopenharmony_ci		evt->features		|= CLOCK_EVT_FEAT_PERIODIC;
41362306a36Sopenharmony_ci		evt->set_state_periodic	= hpet_clkevt_set_state_periodic;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void __init hpet_legacy_clockevent_register(struct hpet_channel *hc)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	/*
42062306a36Sopenharmony_ci	 * Start HPET with the boot CPU's cpumask and make it global after
42162306a36Sopenharmony_ci	 * the IO_APIC has been initialized.
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	hc->cpu = boot_cpu_data.cpu_index;
42462306a36Sopenharmony_ci	strscpy(hc->name, "hpet", sizeof(hc->name));
42562306a36Sopenharmony_ci	hpet_init_clockevent(hc, 50);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	hc->evt.tick_resume	= hpet_clkevt_legacy_resume;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/*
43062306a36Sopenharmony_ci	 * Legacy horrors and sins from the past. HPET used periodic mode
43162306a36Sopenharmony_ci	 * unconditionally forever on the legacy channel 0. Removing the
43262306a36Sopenharmony_ci	 * below hack and using the conditional in hpet_init_clockevent()
43362306a36Sopenharmony_ci	 * makes at least Qemu and one hardware machine fail to boot.
43462306a36Sopenharmony_ci	 * There are two issues which cause the boot failure:
43562306a36Sopenharmony_ci	 *
43662306a36Sopenharmony_ci	 * #1 After the timer delivery test in IOAPIC and the IOAPIC setup
43762306a36Sopenharmony_ci	 *    the next interrupt is not delivered despite the HPET channel
43862306a36Sopenharmony_ci	 *    being programmed correctly. Reprogramming the HPET after
43962306a36Sopenharmony_ci	 *    switching to IOAPIC makes it work again. After fixing this,
44062306a36Sopenharmony_ci	 *    the next issue surfaces:
44162306a36Sopenharmony_ci	 *
44262306a36Sopenharmony_ci	 * #2 Due to the unconditional periodic mode availability the Local
44362306a36Sopenharmony_ci	 *    APIC timer calibration can hijack the global clockevents
44462306a36Sopenharmony_ci	 *    event handler without causing damage. Using oneshot at this
44562306a36Sopenharmony_ci	 *    stage makes if hang because the HPET does not get
44662306a36Sopenharmony_ci	 *    reprogrammed due to the handler hijacking. Duh, stupid me!
44762306a36Sopenharmony_ci	 *
44862306a36Sopenharmony_ci	 * Both issues require major surgery and especially the kick HPET
44962306a36Sopenharmony_ci	 * again after enabling IOAPIC results in really nasty hackery.
45062306a36Sopenharmony_ci	 * This 'assume periodic works' magic has survived since HPET
45162306a36Sopenharmony_ci	 * support got added, so it's questionable whether this should be
45262306a36Sopenharmony_ci	 * fixed. Both Qemu and the failing hardware machine support
45362306a36Sopenharmony_ci	 * periodic mode despite the fact that both don't advertise it in
45462306a36Sopenharmony_ci	 * the configuration register and both need that extra kick after
45562306a36Sopenharmony_ci	 * switching to IOAPIC. Seems to be a feature...
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	hc->evt.features		|= CLOCK_EVT_FEAT_PERIODIC;
45862306a36Sopenharmony_ci	hc->evt.set_state_periodic	= hpet_clkevt_set_state_periodic;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Start HPET legacy interrupts */
46162306a36Sopenharmony_ci	hpet_enable_legacy_int();
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	clockevents_config_and_register(&hc->evt, hpet_freq,
46462306a36Sopenharmony_ci					HPET_MIN_PROG_DELTA, 0x7FFFFFFF);
46562306a36Sopenharmony_ci	global_clock_event = &hc->evt;
46662306a36Sopenharmony_ci	pr_debug("Clockevent registered\n");
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/*
47062306a36Sopenharmony_ci * HPET MSI Support
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_MSI_IRQ
47362306a36Sopenharmony_cistatic void hpet_msi_unmask(struct irq_data *data)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct hpet_channel *hc = irq_data_get_irq_handler_data(data);
47662306a36Sopenharmony_ci	unsigned int cfg;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	cfg = hpet_readl(HPET_Tn_CFG(hc->num));
47962306a36Sopenharmony_ci	cfg |= HPET_TN_ENABLE | HPET_TN_FSB;
48062306a36Sopenharmony_ci	hpet_writel(cfg, HPET_Tn_CFG(hc->num));
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic void hpet_msi_mask(struct irq_data *data)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct hpet_channel *hc = irq_data_get_irq_handler_data(data);
48662306a36Sopenharmony_ci	unsigned int cfg;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	cfg = hpet_readl(HPET_Tn_CFG(hc->num));
48962306a36Sopenharmony_ci	cfg &= ~(HPET_TN_ENABLE | HPET_TN_FSB);
49062306a36Sopenharmony_ci	hpet_writel(cfg, HPET_Tn_CFG(hc->num));
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic void hpet_msi_write(struct hpet_channel *hc, struct msi_msg *msg)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	hpet_writel(msg->data, HPET_Tn_ROUTE(hc->num));
49662306a36Sopenharmony_ci	hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hc->num) + 4);
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic void hpet_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	hpet_msi_write(irq_data_get_irq_handler_data(data), msg);
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic struct irq_chip hpet_msi_controller __ro_after_init = {
50562306a36Sopenharmony_ci	.name = "HPET-MSI",
50662306a36Sopenharmony_ci	.irq_unmask = hpet_msi_unmask,
50762306a36Sopenharmony_ci	.irq_mask = hpet_msi_mask,
50862306a36Sopenharmony_ci	.irq_ack = irq_chip_ack_parent,
50962306a36Sopenharmony_ci	.irq_set_affinity = msi_domain_set_affinity,
51062306a36Sopenharmony_ci	.irq_retrigger = irq_chip_retrigger_hierarchy,
51162306a36Sopenharmony_ci	.irq_write_msi_msg = hpet_msi_write_msg,
51262306a36Sopenharmony_ci	.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_AFFINITY_PRE_STARTUP,
51362306a36Sopenharmony_ci};
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic int hpet_msi_init(struct irq_domain *domain,
51662306a36Sopenharmony_ci			 struct msi_domain_info *info, unsigned int virq,
51762306a36Sopenharmony_ci			 irq_hw_number_t hwirq, msi_alloc_info_t *arg)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	irq_set_status_flags(virq, IRQ_MOVE_PCNTXT);
52062306a36Sopenharmony_ci	irq_domain_set_info(domain, virq, arg->hwirq, info->chip, NULL,
52162306a36Sopenharmony_ci			    handle_edge_irq, arg->data, "edge");
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic void hpet_msi_free(struct irq_domain *domain,
52762306a36Sopenharmony_ci			  struct msi_domain_info *info, unsigned int virq)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	irq_clear_status_flags(virq, IRQ_MOVE_PCNTXT);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic struct msi_domain_ops hpet_msi_domain_ops = {
53362306a36Sopenharmony_ci	.msi_init	= hpet_msi_init,
53462306a36Sopenharmony_ci	.msi_free	= hpet_msi_free,
53562306a36Sopenharmony_ci};
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic struct msi_domain_info hpet_msi_domain_info = {
53862306a36Sopenharmony_ci	.ops		= &hpet_msi_domain_ops,
53962306a36Sopenharmony_ci	.chip		= &hpet_msi_controller,
54062306a36Sopenharmony_ci	.flags		= MSI_FLAG_USE_DEF_DOM_OPS,
54162306a36Sopenharmony_ci};
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic struct irq_domain *hpet_create_irq_domain(int hpet_id)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct msi_domain_info *domain_info;
54662306a36Sopenharmony_ci	struct irq_domain *parent, *d;
54762306a36Sopenharmony_ci	struct fwnode_handle *fn;
54862306a36Sopenharmony_ci	struct irq_fwspec fwspec;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (x86_vector_domain == NULL)
55162306a36Sopenharmony_ci		return NULL;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	domain_info = kzalloc(sizeof(*domain_info), GFP_KERNEL);
55462306a36Sopenharmony_ci	if (!domain_info)
55562306a36Sopenharmony_ci		return NULL;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	*domain_info = hpet_msi_domain_info;
55862306a36Sopenharmony_ci	domain_info->data = (void *)(long)hpet_id;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	fn = irq_domain_alloc_named_id_fwnode(hpet_msi_controller.name,
56162306a36Sopenharmony_ci					      hpet_id);
56262306a36Sopenharmony_ci	if (!fn) {
56362306a36Sopenharmony_ci		kfree(domain_info);
56462306a36Sopenharmony_ci		return NULL;
56562306a36Sopenharmony_ci	}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	fwspec.fwnode = fn;
56862306a36Sopenharmony_ci	fwspec.param_count = 1;
56962306a36Sopenharmony_ci	fwspec.param[0] = hpet_id;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	parent = irq_find_matching_fwspec(&fwspec, DOMAIN_BUS_ANY);
57262306a36Sopenharmony_ci	if (!parent) {
57362306a36Sopenharmony_ci		irq_domain_free_fwnode(fn);
57462306a36Sopenharmony_ci		kfree(domain_info);
57562306a36Sopenharmony_ci		return NULL;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	if (parent != x86_vector_domain)
57862306a36Sopenharmony_ci		hpet_msi_controller.name = "IR-HPET-MSI";
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	d = msi_create_irq_domain(fn, domain_info, parent);
58162306a36Sopenharmony_ci	if (!d) {
58262306a36Sopenharmony_ci		irq_domain_free_fwnode(fn);
58362306a36Sopenharmony_ci		kfree(domain_info);
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci	return d;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic inline int hpet_dev_id(struct irq_domain *domain)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct msi_domain_info *info = msi_get_domain_info(domain);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	return (int)(long)info->data;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic int hpet_assign_irq(struct irq_domain *domain, struct hpet_channel *hc,
59662306a36Sopenharmony_ci			   int dev_num)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct irq_alloc_info info;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	init_irq_alloc_info(&info, NULL);
60162306a36Sopenharmony_ci	info.type = X86_IRQ_ALLOC_TYPE_HPET;
60262306a36Sopenharmony_ci	info.data = hc;
60362306a36Sopenharmony_ci	info.devid = hpet_dev_id(domain);
60462306a36Sopenharmony_ci	info.hwirq = dev_num;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, &info);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int hpet_clkevt_msi_resume(struct clock_event_device *evt)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct hpet_channel *hc = clockevent_to_channel(evt);
61262306a36Sopenharmony_ci	struct irq_data *data = irq_get_irq_data(hc->irq);
61362306a36Sopenharmony_ci	struct msi_msg msg;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* Restore the MSI msg and unmask the interrupt */
61662306a36Sopenharmony_ci	irq_chip_compose_msi_msg(data, &msg);
61762306a36Sopenharmony_ci	hpet_msi_write(hc, &msg);
61862306a36Sopenharmony_ci	hpet_msi_unmask(data);
61962306a36Sopenharmony_ci	return 0;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic irqreturn_t hpet_msi_interrupt_handler(int irq, void *data)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct hpet_channel *hc = data;
62562306a36Sopenharmony_ci	struct clock_event_device *evt = &hc->evt;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (!evt->event_handler) {
62862306a36Sopenharmony_ci		pr_info("Spurious interrupt HPET channel %d\n", hc->num);
62962306a36Sopenharmony_ci		return IRQ_HANDLED;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	evt->event_handler(evt);
63362306a36Sopenharmony_ci	return IRQ_HANDLED;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic int hpet_setup_msi_irq(struct hpet_channel *hc)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	if (request_irq(hc->irq, hpet_msi_interrupt_handler,
63962306a36Sopenharmony_ci			IRQF_TIMER | IRQF_NOBALANCING,
64062306a36Sopenharmony_ci			hc->name, hc))
64162306a36Sopenharmony_ci		return -1;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	disable_irq(hc->irq);
64462306a36Sopenharmony_ci	irq_set_affinity(hc->irq, cpumask_of(hc->cpu));
64562306a36Sopenharmony_ci	enable_irq(hc->irq);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	pr_debug("%s irq %u for MSI\n", hc->name, hc->irq);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return 0;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci/* Invoked from the hotplug callback on @cpu */
65362306a36Sopenharmony_cistatic void init_one_hpet_msi_clockevent(struct hpet_channel *hc, int cpu)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	struct clock_event_device *evt = &hc->evt;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	hc->cpu = cpu;
65862306a36Sopenharmony_ci	per_cpu(cpu_hpet_channel, cpu) = hc;
65962306a36Sopenharmony_ci	hpet_setup_msi_irq(hc);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	hpet_init_clockevent(hc, 110);
66262306a36Sopenharmony_ci	evt->tick_resume = hpet_clkevt_msi_resume;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	clockevents_config_and_register(evt, hpet_freq, HPET_MIN_PROG_DELTA,
66562306a36Sopenharmony_ci					0x7FFFFFFF);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic struct hpet_channel *hpet_get_unused_clockevent(void)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	int i;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	for (i = 0; i < hpet_base.nr_channels; i++) {
67362306a36Sopenharmony_ci		struct hpet_channel *hc = hpet_base.channels + i;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		if (hc->mode != HPET_MODE_CLOCKEVT || hc->in_use)
67662306a36Sopenharmony_ci			continue;
67762306a36Sopenharmony_ci		hc->in_use = 1;
67862306a36Sopenharmony_ci		return hc;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci	return NULL;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int hpet_cpuhp_online(unsigned int cpu)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct hpet_channel *hc = hpet_get_unused_clockevent();
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (hc)
68862306a36Sopenharmony_ci		init_one_hpet_msi_clockevent(hc, cpu);
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic int hpet_cpuhp_dead(unsigned int cpu)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct hpet_channel *hc = per_cpu(cpu_hpet_channel, cpu);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (!hc)
69762306a36Sopenharmony_ci		return 0;
69862306a36Sopenharmony_ci	free_irq(hc->irq, hc);
69962306a36Sopenharmony_ci	hc->in_use = 0;
70062306a36Sopenharmony_ci	per_cpu(cpu_hpet_channel, cpu) = NULL;
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void __init hpet_select_clockevents(void)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	unsigned int i;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	hpet_base.nr_clockevents = 0;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* No point if MSI is disabled or CPU has an Always Runing APIC Timer */
71162306a36Sopenharmony_ci	if (hpet_msi_disable || boot_cpu_has(X86_FEATURE_ARAT))
71262306a36Sopenharmony_ci		return;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	hpet_print_config();
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	hpet_domain = hpet_create_irq_domain(hpet_blockid);
71762306a36Sopenharmony_ci	if (!hpet_domain)
71862306a36Sopenharmony_ci		return;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	for (i = 0; i < hpet_base.nr_channels; i++) {
72162306a36Sopenharmony_ci		struct hpet_channel *hc = hpet_base.channels + i;
72262306a36Sopenharmony_ci		int irq;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		if (hc->mode != HPET_MODE_UNUSED)
72562306a36Sopenharmony_ci			continue;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		/* Only consider HPET channel with MSI support */
72862306a36Sopenharmony_ci		if (!(hc->boot_cfg & HPET_TN_FSB_CAP))
72962306a36Sopenharmony_ci			continue;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci		sprintf(hc->name, "hpet%d", i);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		irq = hpet_assign_irq(hpet_domain, hc, hc->num);
73462306a36Sopenharmony_ci		if (irq <= 0)
73562306a36Sopenharmony_ci			continue;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci		hc->irq = irq;
73862306a36Sopenharmony_ci		hc->mode = HPET_MODE_CLOCKEVT;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		if (++hpet_base.nr_clockevents == num_possible_cpus())
74162306a36Sopenharmony_ci			break;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	pr_info("%d channels of %d reserved for per-cpu timers\n",
74562306a36Sopenharmony_ci		hpet_base.nr_channels, hpet_base.nr_clockevents);
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci#else
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic inline void hpet_select_clockevents(void) { }
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci#define hpet_cpuhp_online	NULL
75362306a36Sopenharmony_ci#define hpet_cpuhp_dead		NULL
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci#endif
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci/*
75862306a36Sopenharmony_ci * Clock source related code
75962306a36Sopenharmony_ci */
76062306a36Sopenharmony_ci#if defined(CONFIG_SMP) && defined(CONFIG_64BIT)
76162306a36Sopenharmony_ci/*
76262306a36Sopenharmony_ci * Reading the HPET counter is a very slow operation. If a large number of
76362306a36Sopenharmony_ci * CPUs are trying to access the HPET counter simultaneously, it can cause
76462306a36Sopenharmony_ci * massive delays and slow down system performance dramatically. This may
76562306a36Sopenharmony_ci * happen when HPET is the default clock source instead of TSC. For a
76662306a36Sopenharmony_ci * really large system with hundreds of CPUs, the slowdown may be so
76762306a36Sopenharmony_ci * severe, that it can actually crash the system because of a NMI watchdog
76862306a36Sopenharmony_ci * soft lockup, for example.
76962306a36Sopenharmony_ci *
77062306a36Sopenharmony_ci * If multiple CPUs are trying to access the HPET counter at the same time,
77162306a36Sopenharmony_ci * we don't actually need to read the counter multiple times. Instead, the
77262306a36Sopenharmony_ci * other CPUs can use the counter value read by the first CPU in the group.
77362306a36Sopenharmony_ci *
77462306a36Sopenharmony_ci * This special feature is only enabled on x86-64 systems. It is unlikely
77562306a36Sopenharmony_ci * that 32-bit x86 systems will have enough CPUs to require this feature
77662306a36Sopenharmony_ci * with its associated locking overhead. We also need 64-bit atomic read.
77762306a36Sopenharmony_ci *
77862306a36Sopenharmony_ci * The lock and the HPET value are stored together and can be read in a
77962306a36Sopenharmony_ci * single atomic 64-bit read. It is explicitly assumed that arch_spinlock_t
78062306a36Sopenharmony_ci * is 32 bits in size.
78162306a36Sopenharmony_ci */
78262306a36Sopenharmony_ciunion hpet_lock {
78362306a36Sopenharmony_ci	struct {
78462306a36Sopenharmony_ci		arch_spinlock_t lock;
78562306a36Sopenharmony_ci		u32 value;
78662306a36Sopenharmony_ci	};
78762306a36Sopenharmony_ci	u64 lockval;
78862306a36Sopenharmony_ci};
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic union hpet_lock hpet __cacheline_aligned = {
79162306a36Sopenharmony_ci	{ .lock = __ARCH_SPIN_LOCK_UNLOCKED, },
79262306a36Sopenharmony_ci};
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic u64 read_hpet(struct clocksource *cs)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	unsigned long flags;
79762306a36Sopenharmony_ci	union hpet_lock old, new;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(union hpet_lock) != 8);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	/*
80262306a36Sopenharmony_ci	 * Read HPET directly if in NMI.
80362306a36Sopenharmony_ci	 */
80462306a36Sopenharmony_ci	if (in_nmi())
80562306a36Sopenharmony_ci		return (u64)hpet_readl(HPET_COUNTER);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	/*
80862306a36Sopenharmony_ci	 * Read the current state of the lock and HPET value atomically.
80962306a36Sopenharmony_ci	 */
81062306a36Sopenharmony_ci	old.lockval = READ_ONCE(hpet.lockval);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (arch_spin_is_locked(&old.lock))
81362306a36Sopenharmony_ci		goto contended;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	local_irq_save(flags);
81662306a36Sopenharmony_ci	if (arch_spin_trylock(&hpet.lock)) {
81762306a36Sopenharmony_ci		new.value = hpet_readl(HPET_COUNTER);
81862306a36Sopenharmony_ci		/*
81962306a36Sopenharmony_ci		 * Use WRITE_ONCE() to prevent store tearing.
82062306a36Sopenharmony_ci		 */
82162306a36Sopenharmony_ci		WRITE_ONCE(hpet.value, new.value);
82262306a36Sopenharmony_ci		arch_spin_unlock(&hpet.lock);
82362306a36Sopenharmony_ci		local_irq_restore(flags);
82462306a36Sopenharmony_ci		return (u64)new.value;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci	local_irq_restore(flags);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_cicontended:
82962306a36Sopenharmony_ci	/*
83062306a36Sopenharmony_ci	 * Contended case
83162306a36Sopenharmony_ci	 * --------------
83262306a36Sopenharmony_ci	 * Wait until the HPET value change or the lock is free to indicate
83362306a36Sopenharmony_ci	 * its value is up-to-date.
83462306a36Sopenharmony_ci	 *
83562306a36Sopenharmony_ci	 * It is possible that old.value has already contained the latest
83662306a36Sopenharmony_ci	 * HPET value while the lock holder was in the process of releasing
83762306a36Sopenharmony_ci	 * the lock. Checking for lock state change will enable us to return
83862306a36Sopenharmony_ci	 * the value immediately instead of waiting for the next HPET reader
83962306a36Sopenharmony_ci	 * to come along.
84062306a36Sopenharmony_ci	 */
84162306a36Sopenharmony_ci	do {
84262306a36Sopenharmony_ci		cpu_relax();
84362306a36Sopenharmony_ci		new.lockval = READ_ONCE(hpet.lockval);
84462306a36Sopenharmony_ci	} while ((new.value == old.value) && arch_spin_is_locked(&new.lock));
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	return (u64)new.value;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci#else
84962306a36Sopenharmony_ci/*
85062306a36Sopenharmony_ci * For UP or 32-bit.
85162306a36Sopenharmony_ci */
85262306a36Sopenharmony_cistatic u64 read_hpet(struct clocksource *cs)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	return (u64)hpet_readl(HPET_COUNTER);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci#endif
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic struct clocksource clocksource_hpet = {
85962306a36Sopenharmony_ci	.name		= "hpet",
86062306a36Sopenharmony_ci	.rating		= 250,
86162306a36Sopenharmony_ci	.read		= read_hpet,
86262306a36Sopenharmony_ci	.mask		= HPET_MASK,
86362306a36Sopenharmony_ci	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
86462306a36Sopenharmony_ci	.resume		= hpet_resume_counter,
86562306a36Sopenharmony_ci};
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci/*
86862306a36Sopenharmony_ci * AMD SB700 based systems with spread spectrum enabled use a SMM based
86962306a36Sopenharmony_ci * HPET emulation to provide proper frequency setting.
87062306a36Sopenharmony_ci *
87162306a36Sopenharmony_ci * On such systems the SMM code is initialized with the first HPET register
87262306a36Sopenharmony_ci * access and takes some time to complete. During this time the config
87362306a36Sopenharmony_ci * register reads 0xffffffff. We check for max 1000 loops whether the
87462306a36Sopenharmony_ci * config register reads a non-0xffffffff value to make sure that the
87562306a36Sopenharmony_ci * HPET is up and running before we proceed any further.
87662306a36Sopenharmony_ci *
87762306a36Sopenharmony_ci * A counting loop is safe, as the HPET access takes thousands of CPU cycles.
87862306a36Sopenharmony_ci *
87962306a36Sopenharmony_ci * On non-SB700 based machines this check is only done once and has no
88062306a36Sopenharmony_ci * side effects.
88162306a36Sopenharmony_ci */
88262306a36Sopenharmony_cistatic bool __init hpet_cfg_working(void)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	int i;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	for (i = 0; i < 1000; i++) {
88762306a36Sopenharmony_ci		if (hpet_readl(HPET_CFG) != 0xFFFFFFFF)
88862306a36Sopenharmony_ci			return true;
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	pr_warn("Config register invalid. Disabling HPET\n");
89262306a36Sopenharmony_ci	return false;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic bool __init hpet_counting(void)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	u64 start, now, t1;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	hpet_restart_counter();
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	t1 = hpet_readl(HPET_COUNTER);
90262306a36Sopenharmony_ci	start = rdtsc();
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/*
90562306a36Sopenharmony_ci	 * We don't know the TSC frequency yet, but waiting for
90662306a36Sopenharmony_ci	 * 200000 TSC cycles is safe:
90762306a36Sopenharmony_ci	 * 4 GHz == 50us
90862306a36Sopenharmony_ci	 * 1 GHz == 200us
90962306a36Sopenharmony_ci	 */
91062306a36Sopenharmony_ci	do {
91162306a36Sopenharmony_ci		if (t1 != hpet_readl(HPET_COUNTER))
91262306a36Sopenharmony_ci			return true;
91362306a36Sopenharmony_ci		now = rdtsc();
91462306a36Sopenharmony_ci	} while ((now - start) < 200000UL);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	pr_warn("Counter not counting. HPET disabled\n");
91762306a36Sopenharmony_ci	return false;
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic bool __init mwait_pc10_supported(void)
92162306a36Sopenharmony_ci{
92262306a36Sopenharmony_ci	unsigned int eax, ebx, ecx, mwait_substates;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
92562306a36Sopenharmony_ci		return false;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (!cpu_feature_enabled(X86_FEATURE_MWAIT))
92862306a36Sopenharmony_ci		return false;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
93162306a36Sopenharmony_ci		return false;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &mwait_substates);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	return (ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) &&
93662306a36Sopenharmony_ci	       (ecx & CPUID5_ECX_INTERRUPT_BREAK) &&
93762306a36Sopenharmony_ci	       (mwait_substates & (0xF << 28));
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci/*
94162306a36Sopenharmony_ci * Check whether the system supports PC10. If so force disable HPET as that
94262306a36Sopenharmony_ci * stops counting in PC10. This check is overbroad as it does not take any
94362306a36Sopenharmony_ci * of the following into account:
94462306a36Sopenharmony_ci *
94562306a36Sopenharmony_ci *	- ACPI tables
94662306a36Sopenharmony_ci *	- Enablement of intel_idle
94762306a36Sopenharmony_ci *	- Command line arguments which limit intel_idle C-state support
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * That's perfectly fine. HPET is a piece of hardware designed by committee
95062306a36Sopenharmony_ci * and the only reasons why it is still in use on modern systems is the
95162306a36Sopenharmony_ci * fact that it is impossible to reliably query TSC and CPU frequency via
95262306a36Sopenharmony_ci * CPUID or firmware.
95362306a36Sopenharmony_ci *
95462306a36Sopenharmony_ci * If HPET is functional it is useful for calibrating TSC, but this can be
95562306a36Sopenharmony_ci * done via PMTIMER as well which seems to be the last remaining timer on
95662306a36Sopenharmony_ci * X86/INTEL platforms that has not been completely wreckaged by feature
95762306a36Sopenharmony_ci * creep.
95862306a36Sopenharmony_ci *
95962306a36Sopenharmony_ci * In theory HPET support should be removed altogether, but there are older
96062306a36Sopenharmony_ci * systems out there which depend on it because TSC and APIC timer are
96162306a36Sopenharmony_ci * dysfunctional in deeper C-states.
96262306a36Sopenharmony_ci *
96362306a36Sopenharmony_ci * It's only 20 years now that hardware people have been asked to provide
96462306a36Sopenharmony_ci * reliable and discoverable facilities which can be used for timekeeping
96562306a36Sopenharmony_ci * and per CPU timer interrupts.
96662306a36Sopenharmony_ci *
96762306a36Sopenharmony_ci * The probability that this problem is going to be solved in the
96862306a36Sopenharmony_ci * forseeable future is close to zero, so the kernel has to be cluttered
96962306a36Sopenharmony_ci * with heuristics to keep up with the ever growing amount of hardware and
97062306a36Sopenharmony_ci * firmware trainwrecks. Hopefully some day hardware people will understand
97162306a36Sopenharmony_ci * that the approach of "This can be fixed in software" is not sustainable.
97262306a36Sopenharmony_ci * Hope dies last...
97362306a36Sopenharmony_ci */
97462306a36Sopenharmony_cistatic bool __init hpet_is_pc10_damaged(void)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	unsigned long long pcfg;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* Check whether PC10 substates are supported */
97962306a36Sopenharmony_ci	if (!mwait_pc10_supported())
98062306a36Sopenharmony_ci		return false;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Check whether PC10 is enabled in PKG C-state limit */
98362306a36Sopenharmony_ci	rdmsrl(MSR_PKG_CST_CONFIG_CONTROL, pcfg);
98462306a36Sopenharmony_ci	if ((pcfg & 0xF) < 8)
98562306a36Sopenharmony_ci		return false;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (hpet_force_user) {
98862306a36Sopenharmony_ci		pr_warn("HPET force enabled via command line, but dysfunctional in PC10.\n");
98962306a36Sopenharmony_ci		return false;
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	pr_info("HPET dysfunctional in PC10. Force disabled.\n");
99362306a36Sopenharmony_ci	boot_hpet_disable = true;
99462306a36Sopenharmony_ci	return true;
99562306a36Sopenharmony_ci}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci/**
99862306a36Sopenharmony_ci * hpet_enable - Try to setup the HPET timer. Returns 1 on success.
99962306a36Sopenharmony_ci */
100062306a36Sopenharmony_ciint __init hpet_enable(void)
100162306a36Sopenharmony_ci{
100262306a36Sopenharmony_ci	u32 hpet_period, cfg, id, irq;
100362306a36Sopenharmony_ci	unsigned int i, channels;
100462306a36Sopenharmony_ci	struct hpet_channel *hc;
100562306a36Sopenharmony_ci	u64 freq;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (!is_hpet_capable())
100862306a36Sopenharmony_ci		return 0;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (hpet_is_pc10_damaged())
101162306a36Sopenharmony_ci		return 0;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	hpet_set_mapping();
101462306a36Sopenharmony_ci	if (!hpet_virt_address)
101562306a36Sopenharmony_ci		return 0;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* Validate that the config register is working */
101862306a36Sopenharmony_ci	if (!hpet_cfg_working())
101962306a36Sopenharmony_ci		goto out_nohpet;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/*
102262306a36Sopenharmony_ci	 * Read the period and check for a sane value:
102362306a36Sopenharmony_ci	 */
102462306a36Sopenharmony_ci	hpet_period = hpet_readl(HPET_PERIOD);
102562306a36Sopenharmony_ci	if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD)
102662306a36Sopenharmony_ci		goto out_nohpet;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* The period is a femtoseconds value. Convert it to a frequency. */
102962306a36Sopenharmony_ci	freq = FSEC_PER_SEC;
103062306a36Sopenharmony_ci	do_div(freq, hpet_period);
103162306a36Sopenharmony_ci	hpet_freq = freq;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/*
103462306a36Sopenharmony_ci	 * Read the HPET ID register to retrieve the IRQ routing
103562306a36Sopenharmony_ci	 * information and the number of channels
103662306a36Sopenharmony_ci	 */
103762306a36Sopenharmony_ci	id = hpet_readl(HPET_ID);
103862306a36Sopenharmony_ci	hpet_print_config();
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* This is the HPET channel number which is zero based */
104162306a36Sopenharmony_ci	channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/*
104462306a36Sopenharmony_ci	 * The legacy routing mode needs at least two channels, tick timer
104562306a36Sopenharmony_ci	 * and the rtc emulation channel.
104662306a36Sopenharmony_ci	 */
104762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC) && channels < 2)
104862306a36Sopenharmony_ci		goto out_nohpet;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	hc = kcalloc(channels, sizeof(*hc), GFP_KERNEL);
105162306a36Sopenharmony_ci	if (!hc) {
105262306a36Sopenharmony_ci		pr_warn("Disabling HPET.\n");
105362306a36Sopenharmony_ci		goto out_nohpet;
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci	hpet_base.channels = hc;
105662306a36Sopenharmony_ci	hpet_base.nr_channels = channels;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	/* Read, store and sanitize the global configuration */
105962306a36Sopenharmony_ci	cfg = hpet_readl(HPET_CFG);
106062306a36Sopenharmony_ci	hpet_base.boot_cfg = cfg;
106162306a36Sopenharmony_ci	cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
106262306a36Sopenharmony_ci	hpet_writel(cfg, HPET_CFG);
106362306a36Sopenharmony_ci	if (cfg)
106462306a36Sopenharmony_ci		pr_warn("Global config: Unknown bits %#x\n", cfg);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* Read, store and sanitize the per channel configuration */
106762306a36Sopenharmony_ci	for (i = 0; i < channels; i++, hc++) {
106862306a36Sopenharmony_ci		hc->num = i;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci		cfg = hpet_readl(HPET_Tn_CFG(i));
107162306a36Sopenharmony_ci		hc->boot_cfg = cfg;
107262306a36Sopenharmony_ci		irq = (cfg & Tn_INT_ROUTE_CNF_MASK) >> Tn_INT_ROUTE_CNF_SHIFT;
107362306a36Sopenharmony_ci		hc->irq = irq;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci		cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB);
107662306a36Sopenharmony_ci		hpet_writel(cfg, HPET_Tn_CFG(i));
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		cfg &= ~(HPET_TN_PERIODIC | HPET_TN_PERIODIC_CAP
107962306a36Sopenharmony_ci			 | HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE
108062306a36Sopenharmony_ci			 | HPET_TN_FSB | HPET_TN_FSB_CAP);
108162306a36Sopenharmony_ci		if (cfg)
108262306a36Sopenharmony_ci			pr_warn("Channel #%u config: Unknown bits %#x\n", i, cfg);
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci	hpet_print_config();
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/*
108762306a36Sopenharmony_ci	 * Validate that the counter is counting. This needs to be done
108862306a36Sopenharmony_ci	 * after sanitizing the config registers to properly deal with
108962306a36Sopenharmony_ci	 * force enabled HPETs.
109062306a36Sopenharmony_ci	 */
109162306a36Sopenharmony_ci	if (!hpet_counting())
109262306a36Sopenharmony_ci		goto out_nohpet;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (tsc_clocksource_watchdog_disabled())
109562306a36Sopenharmony_ci		clocksource_hpet.flags |= CLOCK_SOURCE_MUST_VERIFY;
109662306a36Sopenharmony_ci	clocksource_register_hz(&clocksource_hpet, (u32)hpet_freq);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (id & HPET_ID_LEGSUP) {
109962306a36Sopenharmony_ci		hpet_legacy_clockevent_register(&hpet_base.channels[0]);
110062306a36Sopenharmony_ci		hpet_base.channels[0].mode = HPET_MODE_LEGACY;
110162306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC))
110262306a36Sopenharmony_ci			hpet_base.channels[1].mode = HPET_MODE_LEGACY;
110362306a36Sopenharmony_ci		return 1;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci	return 0;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ciout_nohpet:
110862306a36Sopenharmony_ci	kfree(hpet_base.channels);
110962306a36Sopenharmony_ci	hpet_base.channels = NULL;
111062306a36Sopenharmony_ci	hpet_base.nr_channels = 0;
111162306a36Sopenharmony_ci	hpet_clear_mapping();
111262306a36Sopenharmony_ci	hpet_address = 0;
111362306a36Sopenharmony_ci	return 0;
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci/*
111762306a36Sopenharmony_ci * The late initialization runs after the PCI quirks have been invoked
111862306a36Sopenharmony_ci * which might have detected a system on which the HPET can be enforced.
111962306a36Sopenharmony_ci *
112062306a36Sopenharmony_ci * Also, the MSI machinery is not working yet when the HPET is initialized
112162306a36Sopenharmony_ci * early.
112262306a36Sopenharmony_ci *
112362306a36Sopenharmony_ci * If the HPET is enabled, then:
112462306a36Sopenharmony_ci *
112562306a36Sopenharmony_ci *  1) Reserve one channel for /dev/hpet if CONFIG_HPET=y
112662306a36Sopenharmony_ci *  2) Reserve up to num_possible_cpus() channels as per CPU clockevents
112762306a36Sopenharmony_ci *  3) Setup /dev/hpet if CONFIG_HPET=y
112862306a36Sopenharmony_ci *  4) Register hotplug callbacks when clockevents are available
112962306a36Sopenharmony_ci */
113062306a36Sopenharmony_cistatic __init int hpet_late_init(void)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	int ret;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if (!hpet_address) {
113562306a36Sopenharmony_ci		if (!force_hpet_address)
113662306a36Sopenharmony_ci			return -ENODEV;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		hpet_address = force_hpet_address;
113962306a36Sopenharmony_ci		hpet_enable();
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	if (!hpet_virt_address)
114362306a36Sopenharmony_ci		return -ENODEV;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	hpet_select_device_channel();
114662306a36Sopenharmony_ci	hpet_select_clockevents();
114762306a36Sopenharmony_ci	hpet_reserve_platform_timers();
114862306a36Sopenharmony_ci	hpet_print_config();
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (!hpet_base.nr_clockevents)
115162306a36Sopenharmony_ci		return 0;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	ret = cpuhp_setup_state(CPUHP_AP_X86_HPET_ONLINE, "x86/hpet:online",
115462306a36Sopenharmony_ci				hpet_cpuhp_online, NULL);
115562306a36Sopenharmony_ci	if (ret)
115662306a36Sopenharmony_ci		return ret;
115762306a36Sopenharmony_ci	ret = cpuhp_setup_state(CPUHP_X86_HPET_DEAD, "x86/hpet:dead", NULL,
115862306a36Sopenharmony_ci				hpet_cpuhp_dead);
115962306a36Sopenharmony_ci	if (ret)
116062306a36Sopenharmony_ci		goto err_cpuhp;
116162306a36Sopenharmony_ci	return 0;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cierr_cpuhp:
116462306a36Sopenharmony_ci	cpuhp_remove_state(CPUHP_AP_X86_HPET_ONLINE);
116562306a36Sopenharmony_ci	return ret;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_cifs_initcall(hpet_late_init);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_civoid hpet_disable(void)
117062306a36Sopenharmony_ci{
117162306a36Sopenharmony_ci	unsigned int i;
117262306a36Sopenharmony_ci	u32 cfg;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (!is_hpet_capable() || !hpet_virt_address)
117562306a36Sopenharmony_ci		return;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/* Restore boot configuration with the enable bit cleared */
117862306a36Sopenharmony_ci	cfg = hpet_base.boot_cfg;
117962306a36Sopenharmony_ci	cfg &= ~HPET_CFG_ENABLE;
118062306a36Sopenharmony_ci	hpet_writel(cfg, HPET_CFG);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	/* Restore the channel boot configuration */
118362306a36Sopenharmony_ci	for (i = 0; i < hpet_base.nr_channels; i++)
118462306a36Sopenharmony_ci		hpet_writel(hpet_base.channels[i].boot_cfg, HPET_Tn_CFG(i));
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* If the HPET was enabled at boot time, reenable it */
118762306a36Sopenharmony_ci	if (hpet_base.boot_cfg & HPET_CFG_ENABLE)
118862306a36Sopenharmony_ci		hpet_writel(hpet_base.boot_cfg, HPET_CFG);
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci#ifdef CONFIG_HPET_EMULATE_RTC
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci/*
119462306a36Sopenharmony_ci * HPET in LegacyReplacement mode eats up the RTC interrupt line. When HPET
119562306a36Sopenharmony_ci * is enabled, we support RTC interrupt functionality in software.
119662306a36Sopenharmony_ci *
119762306a36Sopenharmony_ci * RTC has 3 kinds of interrupts:
119862306a36Sopenharmony_ci *
119962306a36Sopenharmony_ci *  1) Update Interrupt - generate an interrupt, every second, when the
120062306a36Sopenharmony_ci *     RTC clock is updated
120162306a36Sopenharmony_ci *  2) Alarm Interrupt - generate an interrupt at a specific time of day
120262306a36Sopenharmony_ci *  3) Periodic Interrupt - generate periodic interrupt, with frequencies
120362306a36Sopenharmony_ci *     2Hz-8192Hz (2Hz-64Hz for non-root user) (all frequencies in powers of 2)
120462306a36Sopenharmony_ci *
120562306a36Sopenharmony_ci * (1) and (2) above are implemented using polling at a frequency of 64 Hz:
120662306a36Sopenharmony_ci * DEFAULT_RTC_INT_FREQ.
120762306a36Sopenharmony_ci *
120862306a36Sopenharmony_ci * The exact frequency is a tradeoff between accuracy and interrupt overhead.
120962306a36Sopenharmony_ci *
121062306a36Sopenharmony_ci * For (3), we use interrupts at 64 Hz, or the user specified periodic frequency,
121162306a36Sopenharmony_ci * if it's higher.
121262306a36Sopenharmony_ci */
121362306a36Sopenharmony_ci#include <linux/mc146818rtc.h>
121462306a36Sopenharmony_ci#include <linux/rtc.h>
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci#define DEFAULT_RTC_INT_FREQ	64
121762306a36Sopenharmony_ci#define DEFAULT_RTC_SHIFT	6
121862306a36Sopenharmony_ci#define RTC_NUM_INTS		1
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic unsigned long hpet_rtc_flags;
122162306a36Sopenharmony_cistatic int hpet_prev_update_sec;
122262306a36Sopenharmony_cistatic struct rtc_time hpet_alarm_time;
122362306a36Sopenharmony_cistatic unsigned long hpet_pie_count;
122462306a36Sopenharmony_cistatic u32 hpet_t1_cmp;
122562306a36Sopenharmony_cistatic u32 hpet_default_delta;
122662306a36Sopenharmony_cistatic u32 hpet_pie_delta;
122762306a36Sopenharmony_cistatic unsigned long hpet_pie_limit;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic rtc_irq_handler irq_handler;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci/*
123262306a36Sopenharmony_ci * Check that the HPET counter c1 is ahead of c2
123362306a36Sopenharmony_ci */
123462306a36Sopenharmony_cistatic inline int hpet_cnt_ahead(u32 c1, u32 c2)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	return (s32)(c2 - c1) < 0;
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci/*
124062306a36Sopenharmony_ci * Registers a IRQ handler.
124162306a36Sopenharmony_ci */
124262306a36Sopenharmony_ciint hpet_register_irq_handler(rtc_irq_handler handler)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	if (!is_hpet_enabled())
124562306a36Sopenharmony_ci		return -ENODEV;
124662306a36Sopenharmony_ci	if (irq_handler)
124762306a36Sopenharmony_ci		return -EBUSY;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	irq_handler = handler;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	return 0;
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_register_irq_handler);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci/*
125662306a36Sopenharmony_ci * Deregisters the IRQ handler registered with hpet_register_irq_handler()
125762306a36Sopenharmony_ci * and does cleanup.
125862306a36Sopenharmony_ci */
125962306a36Sopenharmony_civoid hpet_unregister_irq_handler(rtc_irq_handler handler)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	if (!is_hpet_enabled())
126262306a36Sopenharmony_ci		return;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	irq_handler = NULL;
126562306a36Sopenharmony_ci	hpet_rtc_flags = 0;
126662306a36Sopenharmony_ci}
126762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_unregister_irq_handler);
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci/*
127062306a36Sopenharmony_ci * Channel 1 for RTC emulation. We use one shot mode, as periodic mode
127162306a36Sopenharmony_ci * is not supported by all HPET implementations for channel 1.
127262306a36Sopenharmony_ci *
127362306a36Sopenharmony_ci * hpet_rtc_timer_init() is called when the rtc is initialized.
127462306a36Sopenharmony_ci */
127562306a36Sopenharmony_ciint hpet_rtc_timer_init(void)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	unsigned int cfg, cnt, delta;
127862306a36Sopenharmony_ci	unsigned long flags;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	if (!is_hpet_enabled())
128162306a36Sopenharmony_ci		return 0;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (!hpet_default_delta) {
128462306a36Sopenharmony_ci		struct clock_event_device *evt = &hpet_base.channels[0].evt;
128562306a36Sopenharmony_ci		uint64_t clc;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		clc = (uint64_t) evt->mult * NSEC_PER_SEC;
128862306a36Sopenharmony_ci		clc >>= evt->shift + DEFAULT_RTC_SHIFT;
128962306a36Sopenharmony_ci		hpet_default_delta = clc;
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit)
129362306a36Sopenharmony_ci		delta = hpet_default_delta;
129462306a36Sopenharmony_ci	else
129562306a36Sopenharmony_ci		delta = hpet_pie_delta;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	local_irq_save(flags);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	cnt = delta + hpet_readl(HPET_COUNTER);
130062306a36Sopenharmony_ci	hpet_writel(cnt, HPET_T1_CMP);
130162306a36Sopenharmony_ci	hpet_t1_cmp = cnt;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	cfg = hpet_readl(HPET_T1_CFG);
130462306a36Sopenharmony_ci	cfg &= ~HPET_TN_PERIODIC;
130562306a36Sopenharmony_ci	cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
130662306a36Sopenharmony_ci	hpet_writel(cfg, HPET_T1_CFG);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	local_irq_restore(flags);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	return 1;
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_rtc_timer_init);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cistatic void hpet_disable_rtc_channel(void)
131562306a36Sopenharmony_ci{
131662306a36Sopenharmony_ci	u32 cfg = hpet_readl(HPET_T1_CFG);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	cfg &= ~HPET_TN_ENABLE;
131962306a36Sopenharmony_ci	hpet_writel(cfg, HPET_T1_CFG);
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci/*
132362306a36Sopenharmony_ci * The functions below are called from rtc driver.
132462306a36Sopenharmony_ci * Return 0 if HPET is not being used.
132562306a36Sopenharmony_ci * Otherwise do the necessary changes and return 1.
132662306a36Sopenharmony_ci */
132762306a36Sopenharmony_ciint hpet_mask_rtc_irq_bit(unsigned long bit_mask)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	if (!is_hpet_enabled())
133062306a36Sopenharmony_ci		return 0;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	hpet_rtc_flags &= ~bit_mask;
133362306a36Sopenharmony_ci	if (unlikely(!hpet_rtc_flags))
133462306a36Sopenharmony_ci		hpet_disable_rtc_channel();
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return 1;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_mask_rtc_irq_bit);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ciint hpet_set_rtc_irq_bit(unsigned long bit_mask)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	unsigned long oldbits = hpet_rtc_flags;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (!is_hpet_enabled())
134562306a36Sopenharmony_ci		return 0;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	hpet_rtc_flags |= bit_mask;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	if ((bit_mask & RTC_UIE) && !(oldbits & RTC_UIE))
135062306a36Sopenharmony_ci		hpet_prev_update_sec = -1;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (!oldbits)
135362306a36Sopenharmony_ci		hpet_rtc_timer_init();
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	return 1;
135662306a36Sopenharmony_ci}
135762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_set_rtc_irq_bit);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ciint hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	if (!is_hpet_enabled())
136262306a36Sopenharmony_ci		return 0;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	hpet_alarm_time.tm_hour = hrs;
136562306a36Sopenharmony_ci	hpet_alarm_time.tm_min = min;
136662306a36Sopenharmony_ci	hpet_alarm_time.tm_sec = sec;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	return 1;
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_set_alarm_time);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ciint hpet_set_periodic_freq(unsigned long freq)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	uint64_t clc;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (!is_hpet_enabled())
137762306a36Sopenharmony_ci		return 0;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (freq <= DEFAULT_RTC_INT_FREQ) {
138062306a36Sopenharmony_ci		hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq;
138162306a36Sopenharmony_ci	} else {
138262306a36Sopenharmony_ci		struct clock_event_device *evt = &hpet_base.channels[0].evt;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci		clc = (uint64_t) evt->mult * NSEC_PER_SEC;
138562306a36Sopenharmony_ci		do_div(clc, freq);
138662306a36Sopenharmony_ci		clc >>= evt->shift;
138762306a36Sopenharmony_ci		hpet_pie_delta = clc;
138862306a36Sopenharmony_ci		hpet_pie_limit = 0;
138962306a36Sopenharmony_ci	}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	return 1;
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_set_periodic_freq);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ciint hpet_rtc_dropped_irq(void)
139662306a36Sopenharmony_ci{
139762306a36Sopenharmony_ci	return is_hpet_enabled();
139862306a36Sopenharmony_ci}
139962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_rtc_dropped_irq);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic void hpet_rtc_timer_reinit(void)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	unsigned int delta;
140462306a36Sopenharmony_ci	int lost_ints = -1;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	if (unlikely(!hpet_rtc_flags))
140762306a36Sopenharmony_ci		hpet_disable_rtc_channel();
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit)
141062306a36Sopenharmony_ci		delta = hpet_default_delta;
141162306a36Sopenharmony_ci	else
141262306a36Sopenharmony_ci		delta = hpet_pie_delta;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	/*
141562306a36Sopenharmony_ci	 * Increment the comparator value until we are ahead of the
141662306a36Sopenharmony_ci	 * current count.
141762306a36Sopenharmony_ci	 */
141862306a36Sopenharmony_ci	do {
141962306a36Sopenharmony_ci		hpet_t1_cmp += delta;
142062306a36Sopenharmony_ci		hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
142162306a36Sopenharmony_ci		lost_ints++;
142262306a36Sopenharmony_ci	} while (!hpet_cnt_ahead(hpet_t1_cmp, hpet_readl(HPET_COUNTER)));
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	if (lost_ints) {
142562306a36Sopenharmony_ci		if (hpet_rtc_flags & RTC_PIE)
142662306a36Sopenharmony_ci			hpet_pie_count += lost_ints;
142762306a36Sopenharmony_ci		if (printk_ratelimit())
142862306a36Sopenharmony_ci			pr_warn("Lost %d RTC interrupts\n", lost_ints);
142962306a36Sopenharmony_ci	}
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ciirqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
143362306a36Sopenharmony_ci{
143462306a36Sopenharmony_ci	struct rtc_time curr_time;
143562306a36Sopenharmony_ci	unsigned long rtc_int_flag = 0;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	hpet_rtc_timer_reinit();
143862306a36Sopenharmony_ci	memset(&curr_time, 0, sizeof(struct rtc_time));
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	if (hpet_rtc_flags & (RTC_UIE | RTC_AIE)) {
144162306a36Sopenharmony_ci		if (unlikely(mc146818_get_time(&curr_time, 10) < 0)) {
144262306a36Sopenharmony_ci			pr_err_ratelimited("unable to read current time from RTC\n");
144362306a36Sopenharmony_ci			return IRQ_HANDLED;
144462306a36Sopenharmony_ci		}
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (hpet_rtc_flags & RTC_UIE &&
144862306a36Sopenharmony_ci	    curr_time.tm_sec != hpet_prev_update_sec) {
144962306a36Sopenharmony_ci		if (hpet_prev_update_sec >= 0)
145062306a36Sopenharmony_ci			rtc_int_flag = RTC_UF;
145162306a36Sopenharmony_ci		hpet_prev_update_sec = curr_time.tm_sec;
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	if (hpet_rtc_flags & RTC_PIE && ++hpet_pie_count >= hpet_pie_limit) {
145562306a36Sopenharmony_ci		rtc_int_flag |= RTC_PF;
145662306a36Sopenharmony_ci		hpet_pie_count = 0;
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	if (hpet_rtc_flags & RTC_AIE &&
146062306a36Sopenharmony_ci	    (curr_time.tm_sec == hpet_alarm_time.tm_sec) &&
146162306a36Sopenharmony_ci	    (curr_time.tm_min == hpet_alarm_time.tm_min) &&
146262306a36Sopenharmony_ci	    (curr_time.tm_hour == hpet_alarm_time.tm_hour))
146362306a36Sopenharmony_ci		rtc_int_flag |= RTC_AF;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	if (rtc_int_flag) {
146662306a36Sopenharmony_ci		rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
146762306a36Sopenharmony_ci		if (irq_handler)
146862306a36Sopenharmony_ci			irq_handler(rtc_int_flag, dev_id);
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci	return IRQ_HANDLED;
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hpet_rtc_interrupt);
147362306a36Sopenharmony_ci#endif
1474