1 /*
2  * Support Hypertransport IRQ
3  *
4  * Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
5  *	Moved from arch/x86/kernel/apic/io_apic.c.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11 #include <linux/mm.h>
12 #include <linux/interrupt.h>
13 #include <linux/init.h>
14 #include <linux/device.h>
15 #include <linux/pci.h>
16 #include <linux/htirq.h>
17 #include <asm/hw_irq.h>
18 #include <asm/apic.h>
19 #include <asm/hypertransport.h>
20 
21 /*
22  * Hypertransport interrupt support
23  */
target_ht_irq(unsigned int irq,unsigned int dest,u8 vector)24 static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector)
25 {
26 	struct ht_irq_msg msg;
27 
28 	fetch_ht_irq_msg(irq, &msg);
29 
30 	msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK);
31 	msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
32 
33 	msg.address_lo |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest);
34 	msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest);
35 
36 	write_ht_irq_msg(irq, &msg);
37 }
38 
39 static int
ht_set_affinity(struct irq_data * data,const struct cpumask * mask,bool force)40 ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
41 {
42 	struct irq_cfg *cfg = irqd_cfg(data);
43 	unsigned int dest;
44 	int ret;
45 
46 	ret = apic_set_affinity(data, mask, &dest);
47 	if (ret)
48 		return ret;
49 
50 	target_ht_irq(data->irq, dest, cfg->vector);
51 	return IRQ_SET_MASK_OK_NOCOPY;
52 }
53 
54 static struct irq_chip ht_irq_chip = {
55 	.name			= "PCI-HT",
56 	.irq_mask		= mask_ht_irq,
57 	.irq_unmask		= unmask_ht_irq,
58 	.irq_ack		= apic_ack_edge,
59 	.irq_set_affinity	= ht_set_affinity,
60 	.irq_retrigger		= apic_retrigger_irq,
61 	.flags			= IRQCHIP_SKIP_SET_WAKE,
62 };
63 
arch_setup_ht_irq(unsigned int irq,struct pci_dev * dev)64 int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
65 {
66 	struct irq_cfg *cfg;
67 	struct ht_irq_msg msg;
68 	unsigned dest;
69 	int err;
70 
71 	if (disable_apic)
72 		return -ENXIO;
73 
74 	cfg = irq_cfg(irq);
75 	err = assign_irq_vector(irq, cfg, apic->target_cpus());
76 	if (err)
77 		return err;
78 
79 	err = apic->cpu_mask_to_apicid_and(cfg->domain,
80 					   apic->target_cpus(), &dest);
81 	if (err)
82 		return err;
83 
84 	msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest);
85 
86 	msg.address_lo =
87 		HT_IRQ_LOW_BASE |
88 		HT_IRQ_LOW_DEST_ID(dest) |
89 		HT_IRQ_LOW_VECTOR(cfg->vector) |
90 		((apic->irq_dest_mode == 0) ?
91 			HT_IRQ_LOW_DM_PHYSICAL :
92 			HT_IRQ_LOW_DM_LOGICAL) |
93 		HT_IRQ_LOW_RQEOI_EDGE |
94 		((apic->irq_delivery_mode != dest_LowestPrio) ?
95 			HT_IRQ_LOW_MT_FIXED :
96 			HT_IRQ_LOW_MT_ARBITRATED) |
97 		HT_IRQ_LOW_IRQ_MASKED;
98 
99 	write_ht_irq_msg(irq, &msg);
100 
101 	irq_set_chip_and_handler_name(irq, &ht_irq_chip,
102 				      handle_edge_irq, "edge");
103 
104 	dev_dbg(&dev->dev, "irq %d for HT\n", irq);
105 
106 	return 0;
107 }
108