Skip to content

Commit

Permalink
x86: HPET_MSI Basic HPET_MSI setup code
Browse files Browse the repository at this point in the history
Basic HPET MSI setup code. Routines to perform basic MSI read write
in HPET memory map and setting up irq_chip for HPET MSI.

Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
  • Loading branch information
venkatesh.pallipadi@intel.com authored and Ingo Molnar committed Oct 16, 2008
1 parent b40d575 commit 58ac1e7
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 0 deletions.
54 changes: 54 additions & 0 deletions arch/x86/kernel/hpet.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <linux/init.h>
#include <linux/sysdev.h>
#include <linux/pm.h>
#include <linux/interrupt.h>
#include <linux/cpu.h>

#include <asm/fixmap.h>
#include <asm/hpet.h>
Expand All @@ -25,6 +27,15 @@
unsigned long hpet_address;
static void __iomem *hpet_virt_address;

struct hpet_dev {
struct clock_event_device evt;
unsigned int num;
int cpu;
unsigned int irq;
unsigned int flags;
char name[10];
};

unsigned long hpet_readl(unsigned long a)
{
return readl(hpet_virt_address + a);
Expand Down Expand Up @@ -304,6 +315,49 @@ static int hpet_legacy_next_event(unsigned long delta,
return hpet_next_event(delta, evt, 0);
}

/*
* HPET MSI Support
*/

void hpet_msi_unmask(unsigned int irq)
{
struct hpet_dev *hdev = get_irq_data(irq);
unsigned long cfg;

/* unmask it */
cfg = hpet_readl(HPET_Tn_CFG(hdev->num));
cfg |= HPET_TN_FSB;
hpet_writel(cfg, HPET_Tn_CFG(hdev->num));
}

void hpet_msi_mask(unsigned int irq)
{
unsigned long cfg;
struct hpet_dev *hdev = get_irq_data(irq);

/* mask it */
cfg = hpet_readl(HPET_Tn_CFG(hdev->num));
cfg &= ~HPET_TN_FSB;
hpet_writel(cfg, HPET_Tn_CFG(hdev->num));
}

void hpet_msi_write(unsigned int irq, struct msi_msg *msg)
{
struct hpet_dev *hdev = get_irq_data(irq);

hpet_writel(msg->data, HPET_Tn_ROUTE(hdev->num));
hpet_writel(msg->address_lo, HPET_Tn_ROUTE(hdev->num) + 4);
}

void hpet_msi_read(unsigned int irq, struct msi_msg *msg)
{
struct hpet_dev *hdev = get_irq_data(irq);

msg->data = hpet_readl(HPET_Tn_ROUTE(hdev->num));
msg->address_lo = hpet_readl(HPET_Tn_ROUTE(hdev->num) + 4);
msg->address_hi = 0;
}

/*
* Clock source related code
*/
Expand Down
64 changes: 64 additions & 0 deletions arch/x86/kernel/io_apic.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#endif
#include <linux/bootmem.h>
#include <linux/dmar.h>
#include <linux/hpet.h>

#include <asm/idle.h>
#include <asm/io.h>
Expand All @@ -56,6 +57,7 @@
#include <asm/hypertransport.h>
#include <asm/setup.h>
#include <asm/irq_remapping.h>
#include <asm/hpet.h>

#include <mach_ipi.h>
#include <mach_apic.h>
Expand Down Expand Up @@ -3522,6 +3524,68 @@ int arch_setup_dmar_msi(unsigned int irq)
}
#endif

#ifdef CONFIG_HPET_TIMER

#ifdef CONFIG_SMP
static void hpet_msi_set_affinity(unsigned int irq, cpumask_t mask)
{
struct irq_cfg *cfg;
struct irq_desc *desc;
struct msi_msg msg;
unsigned int dest;
cpumask_t tmp;

cpus_and(tmp, mask, cpu_online_map);
if (cpus_empty(tmp))
return;

if (assign_irq_vector(irq, mask))
return;

cfg = irq_cfg(irq);
cpus_and(tmp, cfg->domain, mask);
dest = cpu_mask_to_apicid(tmp);

hpet_msi_read(irq, &msg);

msg.data &= ~MSI_DATA_VECTOR_MASK;
msg.data |= MSI_DATA_VECTOR(cfg->vector);
msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
msg.address_lo |= MSI_ADDR_DEST_ID(dest);

hpet_msi_write(irq, &msg);
desc = irq_to_desc(irq);
desc->affinity = mask;
}
#endif /* CONFIG_SMP */

struct irq_chip hpet_msi_type = {
.name = "HPET_MSI",
.unmask = hpet_msi_unmask,
.mask = hpet_msi_mask,
.ack = ack_apic_edge,
#ifdef CONFIG_SMP
.set_affinity = hpet_msi_set_affinity,
#endif
.retrigger = ioapic_retrigger_irq,
};

int arch_setup_hpet_msi(unsigned int irq)
{
int ret;
struct msi_msg msg;

ret = msi_compose_msg(NULL, irq, &msg);
if (ret < 0)
return ret;

hpet_msi_write(irq, &msg);
set_irq_chip_and_handler_name(irq, &hpet_msi_type, handle_edge_irq,
"edge");
return 0;
}
#endif

#endif /* CONFIG_PCI_MSI */
/*
* Hypertransport interrupt support
Expand Down
21 changes: 21 additions & 0 deletions include/asm-x86/hpet.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef ASM_X86__HPET_H
#define ASM_X86__HPET_H

#include <linux/msi.h>

#ifdef CONFIG_HPET_TIMER

#define HPET_MMAP_SIZE 1024
Expand All @@ -10,6 +12,11 @@
#define HPET_CFG 0x010
#define HPET_STATUS 0x020
#define HPET_COUNTER 0x0f0

#define HPET_Tn_CFG(n) (0x100 + 0x20 * n)
#define HPET_Tn_CMP(n) (0x108 + 0x20 * n)
#define HPET_Tn_ROUTE(n) (0x110 + 0x20 * n)

#define HPET_T0_CFG 0x100
#define HPET_T0_CMP 0x108
#define HPET_T0_ROUTE 0x110
Expand Down Expand Up @@ -65,6 +72,20 @@ extern void hpet_disable(void);
extern unsigned long hpet_readl(unsigned long a);
extern void force_hpet_resume(void);

extern void hpet_msi_unmask(unsigned int irq);
extern void hpet_msi_mask(unsigned int irq);
extern void hpet_msi_write(unsigned int irq, struct msi_msg *msg);
extern void hpet_msi_read(unsigned int irq, struct msi_msg *msg);

#ifdef CONFIG_PCI_MSI
extern int arch_setup_hpet_msi(unsigned int irq);
#else
static inline int arch_setup_hpet_msi(unsigned int irq)
{
return -EINVAL;
}
#endif

#ifdef CONFIG_HPET_EMULATE_RTC

#include <linux/interrupt.h>
Expand Down

0 comments on commit 58ac1e7

Please sign in to comment.