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 */ 24static 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 39static int 40ht_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 54static 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 64int 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