Commit e37f0881 authored by Thomas Gleixner's avatar Thomas Gleixner

x86/hpet: Introduce struct hpet_base and struct hpet_channel

Introduce new data structures to replace the ad hoc collection of separate
variables and pointers.

Replace the boot configuration store and restore as a first step.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarIngo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ricardo Neri <ricardo.neri-calderon@linux.intel.com>
Cc: Ashok Raj <ashok.raj@intel.com>
Cc: Andi Kleen <andi.kleen@intel.com>
Cc: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Ravi Shankar <ravi.v.shankar@intel.com>
Link: https://lkml.kernel.org/r/20190623132435.728456320@linutronix.de
parent 0b5c597d
...@@ -22,6 +22,17 @@ struct hpet_dev { ...@@ -22,6 +22,17 @@ struct hpet_dev {
char name[10]; char name[10];
}; };
struct hpet_channel {
unsigned int num;
unsigned int boot_cfg;
};
struct hpet_base {
unsigned int nr_channels;
unsigned int boot_cfg;
struct hpet_channel *channels;
};
#define HPET_MASK CLOCKSOURCE_MASK(32) #define HPET_MASK CLOCKSOURCE_MASK(32)
#define HPET_DEV_USED_BIT 2 #define HPET_DEV_USED_BIT 2
...@@ -48,7 +59,7 @@ static struct irq_domain *hpet_domain; ...@@ -48,7 +59,7 @@ static struct irq_domain *hpet_domain;
#endif #endif
static void __iomem *hpet_virt_address; static void __iomem *hpet_virt_address;
static u32 *hpet_boot_cfg; static struct hpet_base hpet_base;
static bool hpet_legacy_int_enabled; static bool hpet_legacy_int_enabled;
static unsigned long hpet_freq; static unsigned long hpet_freq;
...@@ -860,6 +871,7 @@ int __init hpet_enable(void) ...@@ -860,6 +871,7 @@ int __init hpet_enable(void)
{ {
u32 hpet_period, cfg, id; u32 hpet_period, cfg, id;
unsigned int i, channels; unsigned int i, channels;
struct hpet_channel *hc;
u64 freq; u64 freq;
if (!is_hpet_capable()) if (!is_hpet_capable())
...@@ -899,34 +911,39 @@ int __init hpet_enable(void) ...@@ -899,34 +911,39 @@ int __init hpet_enable(void)
/* This is the HPET channel number which is zero based */ /* This is the HPET channel number which is zero based */
channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; channels = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1;
#ifdef CONFIG_HPET_EMULATE_RTC
/* /*
* The legacy routing mode needs at least two channels, tick timer * The legacy routing mode needs at least two channels, tick timer
* and the rtc emulation channel. * and the rtc emulation channel.
*/ */
if (channels < 2) if (IS_ENABLED(CONFIG_HPET_EMULATE_RTC) && channels < 2)
goto out_nohpet; goto out_nohpet;
#endif
hc = kcalloc(channels, sizeof(*hc), GFP_KERNEL);
if (!hc) {
pr_warn("Disabling HPET.\n");
goto out_nohpet;
}
hpet_base.channels = hc;
hpet_base.nr_channels = channels;
/* Read, store and sanitize the global configuration */
cfg = hpet_readl(HPET_CFG); cfg = hpet_readl(HPET_CFG);
/* Allocate entries for the global and the channel configurations */ hpet_base.boot_cfg = cfg;
hpet_boot_cfg = kmalloc_array(channels + 1, sizeof(*hpet_boot_cfg),
GFP_KERNEL);
if (hpet_boot_cfg)
*hpet_boot_cfg = cfg;
else
pr_warn("HPET initial state will not be saved\n");
cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);
hpet_writel(cfg, HPET_CFG); hpet_writel(cfg, HPET_CFG);
if (cfg) if (cfg)
pr_warn("Global config: Unknown bits %#x\n", cfg); pr_warn("Global config: Unknown bits %#x\n", cfg);
for (i = 0; i < channels; ++i) { /* Read, store and sanitize the per channel configuration */
for (i = 0; i < channels; i++, hc++) {
hc->num = i;
cfg = hpet_readl(HPET_Tn_CFG(i)); cfg = hpet_readl(HPET_Tn_CFG(i));
if (hpet_boot_cfg) hc->boot_cfg = cfg;
hpet_boot_cfg[i + 1] = cfg;
cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB); cfg &= ~(HPET_TN_ENABLE | HPET_TN_LEVEL | HPET_TN_FSB);
hpet_writel(cfg, HPET_Tn_CFG(i)); hpet_writel(cfg, HPET_Tn_CFG(i));
cfg &= ~(HPET_TN_PERIODIC | HPET_TN_PERIODIC_CAP cfg &= ~(HPET_TN_PERIODIC | HPET_TN_PERIODIC_CAP
| HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE | HPET_TN_64BIT_CAP | HPET_TN_32BIT | HPET_TN_ROUTE
| HPET_TN_FSB | HPET_TN_FSB_CAP); | HPET_TN_FSB | HPET_TN_FSB_CAP);
...@@ -944,6 +961,9 @@ int __init hpet_enable(void) ...@@ -944,6 +961,9 @@ int __init hpet_enable(void)
return 0; return 0;
out_nohpet: out_nohpet:
kfree(hpet_base.channels);
hpet_base.channels = NULL;
hpet_base.nr_channels = 0;
hpet_clear_mapping(); hpet_clear_mapping();
hpet_address = 0; hpet_address = 0;
return 0; return 0;
...@@ -1000,30 +1020,24 @@ fs_initcall(hpet_late_init); ...@@ -1000,30 +1020,24 @@ fs_initcall(hpet_late_init);
void hpet_disable(void) void hpet_disable(void)
{ {
if (is_hpet_capable() && hpet_virt_address) { unsigned int i;
unsigned int cfg = hpet_readl(HPET_CFG), id, last; u32 cfg;
if (hpet_boot_cfg)
cfg = *hpet_boot_cfg;
else if (hpet_legacy_int_enabled) {
cfg &= ~HPET_CFG_LEGACY;
hpet_legacy_int_enabled = false;
}
cfg &= ~HPET_CFG_ENABLE;
hpet_writel(cfg, HPET_CFG);
if (!hpet_boot_cfg) if (!is_hpet_capable() || !hpet_virt_address)
return; return;
id = hpet_readl(HPET_ID); /* Restore boot configuration with the enable bit cleared */
last = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT); cfg = hpet_base.boot_cfg;
cfg &= ~HPET_CFG_ENABLE;
hpet_writel(cfg, HPET_CFG);
for (id = 0; id <= last; ++id) /* Restore the channel boot configuration */
hpet_writel(hpet_boot_cfg[id + 1], HPET_Tn_CFG(id)); for (i = 0; i < hpet_base.nr_channels; i++)
hpet_writel(hpet_base.channels[i].boot_cfg, HPET_Tn_CFG(i));
if (*hpet_boot_cfg & HPET_CFG_ENABLE) /* If the HPET was enabled at boot time, reenable it */
hpet_writel(*hpet_boot_cfg, HPET_CFG); if (hpet_base.boot_cfg & HPET_CFG_ENABLE)
} hpet_writel(hpet_base.boot_cfg, HPET_CFG);
} }
#ifdef CONFIG_HPET_EMULATE_RTC #ifdef CONFIG_HPET_EMULATE_RTC
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment