Skip to content
Snippets Groups Projects
io_apic.c 103 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    		}
    		/*
    		 * Cleanup, just in case ...
    		 */
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    		local_irq_disable();
    
    		legacy_pic->mask(0);
    
    		apic_printk(APIC_QUIET, KERN_INFO "....... failed.\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	if (nmi_watchdog == NMI_IO_APIC) {
    
    		apic_printk(APIC_QUIET, KERN_WARNING "timer doesn't work "
    			    "through the IO-APIC - disabling NMI Watchdog!\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    #ifdef CONFIG_X86_32
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	apic_printk(APIC_QUIET, KERN_INFO
    		    "...trying to set up timer as Virtual Wire IRQ...\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	lapic_register_intr(0, desc);
    
    	apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector);	/* Fixed mode */
    
    	legacy_pic->unmask(0);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (timer_irq_works()) {
    
    		apic_printk(APIC_QUIET, KERN_INFO "..... works.\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    	local_irq_disable();
    
    	legacy_pic->mask(0);
    
    	apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector);
    
    	apic_printk(APIC_QUIET, KERN_INFO "..... failed.\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	apic_printk(APIC_QUIET, KERN_INFO
    		    "...trying to set up timer as ExtINT IRQ...\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	legacy_pic->init(0);
    	legacy_pic->make_irq(0);
    
    	apic_write(APIC_LVT0, APIC_DM_EXTINT);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	unlock_ExtINT_logic();
    
    	if (timer_irq_works()) {
    
    		apic_printk(APIC_QUIET, KERN_INFO "..... works.\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    	local_irq_disable();
    
    	apic_printk(APIC_QUIET, KERN_INFO "..... failed :(.\n");
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	panic("IO-APIC + timer doesn't work!  Boot with apic=debug and send a "
    
    		"report.  Then try booting with the 'noapic' option.\n");
    
     * Traditionally ISA IRQ2 is the cascade IRQ, and is not available
     * to devices.  However there may be an I/O APIC pin available for
     * this interrupt regardless.  The pin may be left unconnected, but
     * typically it will be reused as an ExtINT cascade interrupt for
     * the master 8259A.  In the MPS case such a pin will normally be
     * reported as an ExtINT interrupt in the MP table.  With ACPI
     * there is no provision for ExtINT interrupts, and in the absence
     * of an override it would be treated as an ordinary ISA I/O APIC
     * interrupt, that is edge-triggered and unmasked by default.  We
     * used to do this, but it caused problems on some systems because
     * of the NMI watchdog and sometimes IRQ0 of the 8254 timer using
     * the same ExtINT cascade interrupt to drive the local APIC of the
     * bootstrap processor.  Therefore we refrain from routing IRQ2 to
     * the I/O APIC in all cases now.  No actual device should request
     * it anyway.  --macro
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    #define PIC_IRQS	(1UL << PIC_CASCADE_IR)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    void __init setup_IO_APIC(void)
    {
    
    
    	/*
    	 * calling enable_IO_APIC() is moved to setup_local_APIC for BP
    	 */
    
    	io_apic_irqs = legacy_pic->nr_legacy_irqs ? ~PIC_IRQS : ~0UL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	apic_printk(APIC_VERBOSE, "ENABLING IO-APIC IRQs\n");
    
             * Set up IO-APIC IRQ routing.
             */
    
    	x86_init.mpparse.setup_ioapic_ids();
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	sync_Arb_IDs();
    	setup_IO_APIC_irqs();
    	init_IO_APIC_traps();
    
    	if (legacy_pic->nr_legacy_irqs)
    
    		check_timer();
    
     *      Called after all the initialization is done. If we didnt find any
     *      APIC bugs then we can allow the modify fast path
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    static int __init io_apic_bug_finalize(void)
    {
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	if (sis_apic_bug == -1)
    		sis_apic_bug = 0;
    	return 0;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    late_initcall(io_apic_bug_finalize);
    
    struct sysfs_ioapic_data {
    	struct sys_device dev;
    	struct IO_APIC_route_entry entry[0];
    };
    
    static struct sysfs_ioapic_data * mp_ioapic_data[MAX_IO_APICS];
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static int ioapic_suspend(struct sys_device *dev, pm_message_t state)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	struct IO_APIC_route_entry *entry;
    	struct sysfs_ioapic_data *data;
    	int i;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	data = container_of(dev, struct sysfs_ioapic_data, dev);
    	entry = data->entry;
    
    	for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ )
    		*entry = ioapic_read_entry(dev->id, i);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	return 0;
    }
    
    static int ioapic_resume(struct sys_device *dev)
    {
    	struct IO_APIC_route_entry *entry;
    	struct sysfs_ioapic_data *data;
    	unsigned long flags;
    	union IO_APIC_reg_00 reg_00;
    	int i;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	data = container_of(dev, struct sysfs_ioapic_data, dev);
    	entry = data->entry;
    
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	reg_00.raw = io_apic_read(dev->id, 0);
    
    	if (reg_00.bits.ID != mp_ioapics[dev->id].apicid) {
    		reg_00.bits.ID = mp_ioapics[dev->id].apicid;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		io_apic_write(dev->id, 0, reg_00.raw);
    	}
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    	for (i = 0; i < nr_ioapic_registers[dev->id]; i++)
    
    		ioapic_write_entry(dev->id, i, entry[i]);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	return 0;
    }
    
    static struct sysdev_class ioapic_sysdev_class = {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	.suspend = ioapic_suspend,
    	.resume = ioapic_resume,
    };
    
    static int __init ioapic_init_sysfs(void)
    {
    
    	struct sys_device * dev;
    	int i, size, error;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	error = sysdev_class_register(&ioapic_sysdev_class);
    	if (error)
    		return error;
    
    
    	for (i = 0; i < nr_ioapics; i++ ) {
    
    		size = sizeof(struct sys_device) + nr_ioapic_registers[i]
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			* sizeof(struct IO_APIC_route_entry);
    
    		mp_ioapic_data[i] = kzalloc(size, GFP_KERNEL);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		if (!mp_ioapic_data[i]) {
    			printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i);
    			continue;
    		}
    		dev = &mp_ioapic_data[i]->dev;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		dev->cls = &ioapic_sysdev_class;
    		error = sysdev_register(dev);
    		if (error) {
    			kfree(mp_ioapic_data[i]);
    			mp_ioapic_data[i] = NULL;
    			printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i);
    			continue;
    		}
    	}
    
    	return 0;
    }
    
    device_initcall(ioapic_init_sysfs);
    
    
     * Dynamic irq allocate and deallocation
    
    unsigned int create_irq_nr(unsigned int irq_want, int node)
    
    	unsigned int irq;
    	unsigned int new;
    
    	unsigned long flags;
    
    	struct irq_cfg *cfg_new = NULL;
    	struct irq_desc *desc_new = NULL;
    
    	if (irq_want < nr_irqs_gsi)
    		irq_want = nr_irqs_gsi;
    
    
    	raw_spin_lock_irqsave(&vector_lock, flags);
    
    	for (new = irq_want; new < nr_irqs; new++) {
    
    		desc_new = irq_to_desc_alloc_node(new, node);
    
    		if (!desc_new) {
    			printk(KERN_INFO "can not get irq_desc for %d\n", new);
    
    		cfg_new = get_irq_desc_chip_data(desc_new);
    
    
    		if (cfg_new->vector != 0)
    
    		desc_new = move_irq_desc(desc_new, node);
    
    		cfg_new = get_irq_desc_chip_data(desc_new);
    
    		if (__assign_irq_vector(new, cfg_new, apic->target_cpus()) == 0)
    
    	raw_spin_unlock_irqrestore(&vector_lock, flags);
    
    	if (irq > 0)
    		dynamic_irq_init_keep_chip_data(irq);
    
    int create_irq(void)
    {
    
    	int node = cpu_to_node(0);
    
    	unsigned int irq_want;
    
    	irq_want = nr_irqs_gsi;
    
    	irq = create_irq_nr(irq_want, node);
    
    void destroy_irq(unsigned int irq)
    {
    	unsigned long flags;
    
    
    	dynamic_irq_cleanup_keep_chip_data(irq);
    
    	raw_spin_lock_irqsave(&vector_lock, flags);
    
    	__clear_irq_vector(irq, get_irq_chip_data(irq));
    
    	raw_spin_unlock_irqrestore(&vector_lock, flags);
    
    Simon Arlott's avatar
    Simon Arlott committed
     * MSI message composition
    
    static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq,
    			   struct msi_msg *msg, u8 hpet_id)
    
    	struct irq_cfg *cfg;
    	int err;
    
    Jan Beulich's avatar
    Jan Beulich committed
    	if (disable_apic)
    		return -ENXIO;
    
    
    	cfg = irq_cfg(irq);
    
    	err = assign_irq_vector(irq, cfg, apic->target_cpus());
    
    	if (err)
    		return err;
    
    	dest = apic->cpu_mask_to_apicid_and(cfg->domain, apic->target_cpus());
    
    	if (irq_remapped(irq)) {
    		struct irte irte;
    		int ir_index;
    		u16 sub_handle;
    
    		ir_index = map_irq_to_irte_handle(irq, &sub_handle);
    		BUG_ON(ir_index == -1);
    
    
    		prepare_irte(&irte, cfg->vector, dest);
    
    		/* Set source-id of interrupt request */
    
    		if (pdev)
    			set_msi_sid(&irte, pdev);
    		else
    			set_hpet_sid(&irte, hpet_id);
    
    		modify_irte(irq, &irte);
    
    		msg->address_hi = MSI_ADDR_BASE_HI;
    		msg->data = sub_handle;
    		msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
    				  MSI_ADDR_IR_SHV |
    				  MSI_ADDR_IR_INDEX1(ir_index) |
    				  MSI_ADDR_IR_INDEX2(ir_index);
    
    		if (x2apic_enabled())
    			msg->address_hi = MSI_ADDR_BASE_HI |
    					  MSI_ADDR_EXT_DEST_ID(dest);
    		else
    			msg->address_hi = MSI_ADDR_BASE_HI;
    
    
    		msg->address_lo =
    			MSI_ADDR_BASE_LO |
    
    			((apic->irq_dest_mode == 0) ?
    
    				MSI_ADDR_DEST_MODE_PHYSICAL:
    				MSI_ADDR_DEST_MODE_LOGICAL) |
    
    			((apic->irq_delivery_mode != dest_LowestPrio) ?
    
    				MSI_ADDR_REDIRECTION_CPU:
    				MSI_ADDR_REDIRECTION_LOWPRI) |
    			MSI_ADDR_DEST_ID(dest);
    
    		msg->data =
    			MSI_DATA_TRIGGER_EDGE |
    			MSI_DATA_LEVEL_ASSERT |
    
    			((apic->irq_delivery_mode != dest_LowestPrio) ?
    
    				MSI_DATA_DELIVERY_FIXED:
    				MSI_DATA_DELIVERY_LOWPRI) |
    			MSI_DATA_VECTOR(cfg->vector);
    	}
    
    	return err;
    
    static int set_msi_irq_affinity(unsigned int irq, const struct cpumask *mask)
    
    	struct irq_desc *desc = irq_to_desc(irq);
    
    	struct irq_cfg *cfg;
    
    	if (set_desc_affinity(desc, mask, &dest))
    
    	cfg = get_irq_desc_chip_data(desc);
    
    	__get_cached_msi_msg(desc->irq_data.msi_desc, &msg);
    
    	msg.data |= MSI_DATA_VECTOR(cfg->vector);
    
    	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK;
    	msg.address_lo |= MSI_ADDR_DEST_ID(dest);
    
    
    	__write_msi_msg(desc->irq_data.msi_desc, &msg);
    
    #ifdef CONFIG_INTR_REMAP
    /*
     * Migrate the MSI irq to another cpumask. This migration is
     * done in the process context using interrupt-remapping hardware.
     */
    
    ir_set_msi_irq_affinity(unsigned int irq, const struct cpumask *mask)
    
    	struct irq_desc *desc = irq_to_desc(irq);
    
    	struct irq_cfg *cfg = get_irq_desc_chip_data(desc);
    
    	unsigned int dest;
    	struct irte irte;
    
    	if (get_irte(irq, &irte))
    
    	if (set_desc_affinity(desc, mask, &dest))
    
    
    	irte.vector = cfg->vector;
    	irte.dest_id = IRTE_DEST(dest);
    
    	/*
    	 * atomically update the IRTE with the new destination and vector.
    	 */
    	modify_irte(irq, &irte);
    
    	/*
    	 * After this point, all the interrupts will start arriving
    	 * at the new destination. So, time to cleanup the previous
    	 * vector allocation.
    	 */
    
    	if (cfg->move_in_progress)
    		send_cleanup_vector(cfg);
    
    /*
     * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices,
     * which implement the MSI or MSI-X Capability Structure.
     */
    static struct irq_chip msi_chip = {
    	.name		= "PCI-MSI",
    
    	.irq_unmask	= unmask_msi_irq,
    	.irq_mask	= mask_msi_irq,
    
    	.irq_ack	= ack_apic_edge,
    
    #ifdef CONFIG_SMP
    	.set_affinity	= set_msi_irq_affinity,
    #endif
    
    	.irq_retrigger	= ioapic_retrigger_irq,
    
    static struct irq_chip msi_ir_chip = {
    	.name		= "IR-PCI-MSI",
    
    	.irq_unmask	= unmask_msi_irq,
    	.irq_mask	= mask_msi_irq,
    
    	.irq_ack	= ir_ack_apic_edge,
    
    #ifdef CONFIG_SMP
    	.set_affinity	= ir_set_msi_irq_affinity,
    
    	.irq_retrigger	= ioapic_retrigger_irq,
    
    };
    
    /*
     * Map the PCI dev to the corresponding remapping hardware unit
     * and allocate 'nvec' consecutive interrupt-remapping table entries
     * in it.
     */
    static int msi_alloc_irte(struct pci_dev *dev, int irq, int nvec)
    {
    	struct intel_iommu *iommu;
    	int index;
    
    	iommu = map_dev_to_ir(dev);
    	if (!iommu) {
    		printk(KERN_ERR
    		       "Unable to map PCI %s to iommu\n", pci_name(dev));
    		return -ENOENT;
    	}
    
    	index = alloc_irte(iommu, irq, nvec);
    	if (index < 0) {
    		printk(KERN_ERR
    		       "Unable to allocate %d IRTE for PCI %s\n", nvec,
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    		       pci_name(dev));
    
    		return -ENOSPC;
    	}
    	return index;
    }
    
    static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int irq)
    
    {
    	int ret;
    	struct msi_msg msg;
    
    
    	ret = msi_compose_msg(dev, irq, &msg, -1);
    
    	if (ret < 0)
    		return ret;
    
    
    	set_irq_msi(irq, msidesc);
    
    	write_msi_msg(irq, &msg);
    
    
    	if (irq_remapped(irq)) {
    		struct irq_desc *desc = irq_to_desc(irq);
    		/*
    		 * irq migration in process context
    		 */
    		desc->status |= IRQ_MOVE_PCNTXT;
    		set_irq_chip_and_handler_name(irq, &msi_ir_chip, handle_edge_irq, "edge");
    	} else
    		set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge");
    
    	dev_printk(KERN_DEBUG, &dev->dev, "irq %d for MSI/MSI-X\n", irq);
    
    
    int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
    {
    
    	unsigned int irq;
    	int ret, sub_handle;
    
    	struct msi_desc *msidesc;
    
    	unsigned int irq_want;
    
    	/* x86 doesn't support multiple MSI yet */
    	if (type == PCI_CAP_ID_MSI && nvec > 1)
    		return 1;
    
    
    	node = dev_to_node(&dev->dev);
    
    	irq_want = nr_irqs_gsi;
    
    	list_for_each_entry(msidesc, &dev->msi_list, list) {
    
    		irq = create_irq_nr(irq_want, node);
    
    		if (irq == 0)
    			return -1;
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    		irq_want = irq + 1;
    
    		if (!intr_remapping_enabled)
    			goto no_ir;
    
    		if (!sub_handle) {
    			/*
    			 * allocate the consecutive block of IRTE's
    			 * for 'nvec'
    			 */
    			index = msi_alloc_irte(dev, irq, nvec);
    			if (index < 0) {
    				ret = index;
    				goto error;
    			}
    		} else {
    			iommu = map_dev_to_ir(dev);
    			if (!iommu) {
    				ret = -ENOENT;
    				goto error;
    			}
    			/*
    			 * setup the mapping between the irq and the IRTE
    			 * base index, the sub_handle pointing to the
    			 * appropriate interrupt remap table entry.
    			 */
    			set_irte_irq(irq, iommu, index, sub_handle);
    		}
    no_ir:
    
    		ret = setup_msi_irq(dev, msidesc, irq);
    
    		if (ret < 0)
    			goto error;
    		sub_handle++;
    	}
    	return 0;
    
    	destroy_irq(irq);
    	return ret;
    
    void arch_teardown_msi_irq(unsigned int irq)
    {
    
    	destroy_irq(irq);
    
    #if defined (CONFIG_DMAR) || defined (CONFIG_INTR_REMAP)
    
    #ifdef CONFIG_SMP
    
    static int dmar_msi_set_affinity(unsigned int irq, const struct cpumask *mask)
    
    	struct irq_desc *desc = irq_to_desc(irq);
    
    	struct irq_cfg *cfg;
    	struct msi_msg msg;
    	unsigned int dest;
    
    
    	if (set_desc_affinity(desc, mask, &dest))
    
    	cfg = get_irq_desc_chip_data(desc);
    
    
    	dmar_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);
    
    	dmar_msi_write(irq, &msg);
    
    #endif /* CONFIG_SMP */
    
    
    static struct irq_chip dmar_msi_type = {
    
    	.name = "DMAR_MSI",
    
    	.irq_unmask = dmar_msi_unmask,
    	.irq_mask = dmar_msi_mask,
    
    	.irq_ack = ack_apic_edge,
    
    #ifdef CONFIG_SMP
    	.set_affinity = dmar_msi_set_affinity,
    #endif
    
    	.irq_retrigger = ioapic_retrigger_irq,
    
    };
    
    int arch_setup_dmar_msi(unsigned int irq)
    {
    	int ret;
    	struct msi_msg msg;
    
    	ret = msi_compose_msg(NULL, irq, &msg, -1);
    
    	if (ret < 0)
    		return ret;
    	dmar_msi_write(irq, &msg);
    	set_irq_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
    		"edge");
    	return 0;
    }
    #endif
    
    
    #ifdef CONFIG_HPET_TIMER
    
    #ifdef CONFIG_SMP
    
    static int hpet_msi_set_affinity(struct irq_data *data,
    				 const struct cpumask *mask, bool force)
    
    	struct irq_desc *desc = irq_to_desc(data->irq);
    	struct irq_cfg *cfg = data->chip_data;
    
    	struct msi_msg msg;
    	unsigned int dest;
    
    
    	if (set_desc_affinity(desc, mask, &dest))
    
    	hpet_msi_read(data->handler_data, &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(data->handler_data, &msg);
    
    static struct irq_chip ir_hpet_msi_type = {
    	.name = "IR-HPET_MSI",
    
    	.irq_unmask = hpet_msi_unmask,
    	.irq_mask = hpet_msi_mask,
    
    #ifdef CONFIG_INTR_REMAP
    
    	.irq_ack = ir_ack_apic_edge,
    
    #ifdef CONFIG_SMP
    	.set_affinity = ir_set_msi_irq_affinity,
    #endif
    #endif
    
    	.irq_retrigger = ioapic_retrigger_irq,
    
    static struct irq_chip hpet_msi_type = {
    
    	.irq_unmask = hpet_msi_unmask,
    	.irq_mask = hpet_msi_mask,
    
    	.irq_ack = ack_apic_edge,
    
    	.irq_set_affinity = hpet_msi_set_affinity,
    
    	.irq_retrigger = ioapic_retrigger_irq,
    
    int arch_setup_hpet_msi(unsigned int irq, unsigned int id)
    
    	if (intr_remapping_enabled) {
    		struct intel_iommu *iommu = map_hpet_to_ir(id);
    		int index;
    
    		if (!iommu)
    			return -1;
    
    		index = alloc_irte(iommu, irq, 1);
    		if (index < 0)
    			return -1;
    	}
    
    	ret = msi_compose_msg(NULL, irq, &msg, id);
    
    	hpet_msi_write(get_irq_data(irq), &msg);
    	irq_set_status_flags(irq,IRQ_MOVE_PCNTXT);
    
    	if (irq_remapped(irq))
    		set_irq_chip_and_handler_name(irq, &ir_hpet_msi_type,
    					      handle_edge_irq, "edge");
    	else
    		set_irq_chip_and_handler_name(irq, &hpet_msi_type,
    					      handle_edge_irq, "edge");
    
    #endif /* CONFIG_PCI_MSI */
    
    /*
     * Hypertransport interrupt support
     */
    #ifdef CONFIG_HT_IRQ
    
    #ifdef CONFIG_SMP
    
    
    static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector)
    
    	struct ht_irq_msg msg;
    	fetch_ht_irq_msg(irq, &msg);
    
    	msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK);
    
    	msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK);
    
    	msg.address_lo |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest);
    
    	msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest);
    
    static int set_ht_irq_affinity(unsigned int irq, const struct cpumask *mask)
    
    	struct irq_desc *desc = irq_to_desc(irq);
    
    	struct irq_cfg *cfg;
    
    	if (set_desc_affinity(desc, mask, &dest))
    
    	cfg = get_irq_desc_chip_data(desc);
    
    	target_ht_irq(irq, dest, cfg->vector);
    
    static struct irq_chip ht_irq_chip = {
    
    	.irq_mask	= mask_ht_irq,
    	.irq_unmask	= unmask_ht_irq,
    
    	.irq_ack	= ack_apic_edge,
    
    #ifdef CONFIG_SMP
    	.set_affinity	= set_ht_irq_affinity,
    #endif
    
    	.irq_retrigger	= ioapic_retrigger_irq,
    
    };
    
    int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
    {
    
    	struct irq_cfg *cfg;
    	int err;
    
    Jan Beulich's avatar
    Jan Beulich committed
    	if (disable_apic)
    		return -ENXIO;
    
    
    	cfg = irq_cfg(irq);
    
    	err = assign_irq_vector(irq, cfg, apic->target_cpus());
    
    		dest = apic->cpu_mask_to_apicid_and(cfg->domain,
    						    apic->target_cpus());
    
    		msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest);
    
    			HT_IRQ_LOW_VECTOR(cfg->vector) |
    
    			((apic->irq_dest_mode == 0) ?
    
    				HT_IRQ_LOW_DM_PHYSICAL :
    				HT_IRQ_LOW_DM_LOGICAL) |
    			HT_IRQ_LOW_RQEOI_EDGE |
    
    			((apic->irq_delivery_mode != dest_LowestPrio) ?
    
    				HT_IRQ_LOW_MT_FIXED :
    				HT_IRQ_LOW_MT_ARBITRATED) |
    			HT_IRQ_LOW_IRQ_MASKED;
    
    
    		set_irq_chip_and_handler_name(irq, &ht_irq_chip,
    					      handle_edge_irq, "edge");
    
    
    		dev_printk(KERN_DEBUG, &dev->dev, "irq %d for HT\n", irq);
    
    	return err;
    
    int __init io_apic_get_redir_entries (int ioapic)
    {
    	union IO_APIC_reg_01	reg_01;
    	unsigned long flags;
    
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    	reg_01.raw = io_apic_read(ioapic, 1);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    	/* The register returns the maximum index redir index
    	 * supported, which is one less than the total number of redir
    	 * entries.
    	 */
    	return reg_01.bits.entries + 1;
    
    void __init probe_nr_irqs_gsi(void)
    
    	nr = gsi_top + NR_IRQS_LEGACY;
    
    	if (nr > nr_irqs_gsi)
    
    		nr_irqs_gsi = nr;
    
    
    	printk(KERN_DEBUG "nr_irqs_gsi: %d\n", nr_irqs_gsi);
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    #ifdef CONFIG_SPARSE_IRQ
    int __init arch_probe_nr_irqs(void)
    {
    	int nr;
    
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    	if (nr_irqs > (NR_VECTORS * nr_cpu_ids))
    		nr_irqs = NR_VECTORS * nr_cpu_ids;
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    	nr = nr_irqs_gsi + 8 * nr_cpu_ids;
    #if defined(CONFIG_PCI_MSI) || defined(CONFIG_HT_IRQ)
    	/*
    	 * for MSI and HT dyn irq
    	 */
    	nr += nr_irqs_gsi * 16;
    #endif
    	if (nr < nr_irqs)
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    		nr_irqs = nr;
    
    
    	return NR_IRQS_LEGACY;
    
    static int __io_apic_set_pci_routing(struct device *dev, int irq,
    				struct io_apic_irq_attr *irq_attr)
    
    {
    	struct irq_desc *desc;
    	struct irq_cfg *cfg;
    	int node;
    
    	int ioapic, pin;
    	int trigger, polarity;
    
    	ioapic = irq_attr->ioapic;
    
    	if (!IO_APIC_IRQ(irq)) {
    		apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n",
    			ioapic);
    		return -EINVAL;
    	}
    
    	if (dev)
    		node = dev_to_node(dev);
    	else
    
    		node = cpu_to_node(0);
    
    
    	desc = irq_to_desc_alloc_node(irq, node);
    	if (!desc) {
    		printk(KERN_INFO "can not get irq_desc %d\n", irq);
    		return 0;
    	}
    
    
    	pin = irq_attr->ioapic_pin;
    	trigger = irq_attr->trigger;
    	polarity = irq_attr->polarity;
    
    
    	/*
    	 * IRQs < 16 are already in the irq_2_pin[] map
    	 */
    
    	if (irq >= legacy_pic->nr_legacy_irqs) {
    
    		cfg = get_irq_desc_chip_data(desc);
    
    		if (add_pin_to_irq_node_nopanic(cfg, node, ioapic, pin)) {
    			printk(KERN_INFO "can not add pin %d for irq %d\n",
    				pin, irq);
    			return 0;
    		}
    
    	setup_IO_APIC_irq(ioapic, pin, irq, desc, trigger, polarity);
    
    int io_apic_set_pci_routing(struct device *dev, int irq,
    				struct io_apic_irq_attr *irq_attr)
    
    	int ioapic, pin;
    
    	/*
    	 * Avoid pin reprogramming.  PRTs typically include entries
    	 * with redundant pin->gsi mappings (but unique PCI devices);
    	 * we only program the IOAPIC on the first.
    	 */
    
    	ioapic = irq_attr->ioapic;
    	pin = irq_attr->ioapic_pin;
    
    	if (test_bit(pin, mp_ioapic_routing[ioapic].pin_programmed)) {
    		pr_debug("Pin %d-%d already programmed\n",
    			 mp_ioapics[ioapic].apicid, pin);
    		return 0;
    	}
    	set_bit(pin, mp_ioapic_routing[ioapic].pin_programmed);
    
    
    	return __io_apic_set_pci_routing(dev, irq, irq_attr);
    
    u8 __init io_apic_unique_id(u8 id)
    {
    #ifdef CONFIG_X86_32
    	if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
    	    !APIC_XAPIC(apic_version[boot_cpu_physical_apicid]))
    		return io_apic_get_unique_id(nr_ioapics, id);
    	else
    		return id;
    #else
    	int i;
    	DECLARE_BITMAP(used, 256);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	bitmap_zero(used, 256);
    	for (i = 0; i < nr_ioapics; i++) {
    		struct mpc_ioapic *ia = &mp_ioapics[i];
    		__set_bit(ia->apicid, used);
    	}
    	if (!test_bit(id, used))
    		return id;
    	return find_first_zero_bit(used, 256);
    #endif
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    #ifdef CONFIG_X86_32
    
    int __init io_apic_get_unique_id(int ioapic, int apic_id)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	union IO_APIC_reg_00 reg_00;
    	static physid_mask_t apic_id_map = PHYSID_MASK_NONE;
    	physid_mask_t tmp;
    	unsigned long flags;
    	int i = 0;
    
    	/*
    
    	 * The P4 platform supports up to 256 APIC IDs on two separate APIC
    	 * buses (one for LAPICs, one for IOAPICs), where predecessors only
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	 * supports up to 16 on one shared APIC bus.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	 * TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full
    	 *      advantage of new APIC bus architecture.
    	 */
    
    	if (physids_empty(apic_id_map))
    
    		apic->ioapic_phys_id_map(&phys_cpu_present_map, &apic_id_map);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	reg_00.raw = io_apic_read(ioapic, 0);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	if (apic_id >= get_physical_broadcast()) {
    		printk(KERN_WARNING "IOAPIC[%d]: Invalid apic_id %d, trying "
    			"%d\n", ioapic, apic_id, reg_00.bits.ID);
    		apic_id = reg_00.bits.ID;
    	}
    
    	/*
    
    	 * Every APIC in a system must have a unique ID or we get lots of nice
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	 * 'stuck on smp_invalidate_needed IPI wait' messages.
    	 */
    
    	if (apic->check_apicid_used(&apic_id_map, apic_id)) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		for (i = 0; i < get_physical_broadcast(); i++) {
    
    			if (!apic->check_apicid_used(&apic_id_map, i))
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				break;
    		}
    
    		if (i == get_physical_broadcast())
    			panic("Max apic_id exceeded!\n");
    
    		printk(KERN_WARNING "IOAPIC[%d]: apic_id %d already used, "
    			"trying %d\n", ioapic, apic_id, i);
    
    		apic_id = i;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	apic->apicid_to_cpu_present(apic_id, &tmp);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	physids_or(apic_id_map, apic_id_map, tmp);
    
    	if (reg_00.bits.ID != apic_id) {
    		reg_00.bits.ID = apic_id;
    
    
    		raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		io_apic_write(ioapic, 0, reg_00.raw);
    		reg_00.raw = io_apic_read(ioapic, 0);
    
    		raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    		/* Sanity check */
    
    		if (reg_00.bits.ID != apic_id) {
    			printk("IOAPIC[%d]: Unable to change apic_id!\n", ioapic);
    			return -1;
    		}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    	apic_printk(APIC_VERBOSE, KERN_INFO
    			"IOAPIC[%d]: Assigned apic_id %d\n", ioapic, apic_id);
    
    	return apic_id;
    }