blob: b307ee7a71482437f6cafad4b05ea08add7637dc [file] [log] [blame]
Jiang Liuc34689522014-10-27 16:12:03 +08001/*
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>
Jiang Liuaf87bae2015-04-13 14:11:28 +080017#include <linux/irqdomain.h>
Jiang Liuc34689522014-10-27 16:12:03 +080018#include <asm/hw_irq.h>
19#include <asm/apic.h>
20#include <asm/hypertransport.h>
21
22/*
23 * Hypertransport interrupt support
24 */
25static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector)
26{
27 struct ht_irq_msg msg;
28
29 fetch_ht_irq_msg(irq, &msg);
30
31 msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK);
32 msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
33
34 msg.address_lo |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest);
35 msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest);
36
37 write_ht_irq_msg(irq, &msg);
38}
39
40static int
41ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
42{
Jiang Liua9786092014-10-27 16:12:07 +080043 struct irq_cfg *cfg = irqd_cfg(data);
Jiang Liuc34689522014-10-27 16:12:03 +080044 unsigned int dest;
45 int ret;
46
47 ret = apic_set_affinity(data, mask, &dest);
48 if (ret)
49 return ret;
50
51 target_ht_irq(data->irq, dest, cfg->vector);
52 return IRQ_SET_MASK_OK_NOCOPY;
53}
54
55static struct irq_chip ht_irq_chip = {
56 .name = "PCI-HT",
57 .irq_mask = mask_ht_irq,
58 .irq_unmask = unmask_ht_irq,
59 .irq_ack = apic_ack_edge,
60 .irq_set_affinity = ht_set_affinity,
61 .irq_retrigger = apic_retrigger_irq,
62 .flags = IRQCHIP_SKIP_SET_WAKE,
63};
64
Jiang Liuaf87bae2015-04-13 14:11:28 +080065int arch_alloc_ht_irq(struct pci_dev *dev)
66{
67 return irq_domain_alloc_irqs(NULL, 1, dev_to_node(&dev->dev), NULL);
68}
69
70void arch_free_ht_irq(int irq)
71{
72 irq_domain_free_irqs(irq, 1);
73}
74
Jiang Liuc34689522014-10-27 16:12:03 +080075int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
76{
77 struct irq_cfg *cfg;
78 struct ht_irq_msg msg;
Jiang Liuc34689522014-10-27 16:12:03 +080079
80 if (disable_apic)
81 return -ENXIO;
82
83 cfg = irq_cfg(irq);
Jiang Liuaf87bae2015-04-13 14:11:28 +080084 msg.address_hi = HT_IRQ_HIGH_DEST_ID(cfg->dest_apicid);
Jiang Liuc34689522014-10-27 16:12:03 +080085
86 msg.address_lo =
87 HT_IRQ_LOW_BASE |
Jiang Liuaf87bae2015-04-13 14:11:28 +080088 HT_IRQ_LOW_DEST_ID(cfg->dest_apicid) |
Jiang Liuc34689522014-10-27 16:12:03 +080089 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}