Skip to content
Snippets Groups Projects
io_apic.c 94.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • void destroy_irq(unsigned int irq)
    {
    
    	struct irq_cfg *cfg = irq_get_chip_data(irq);
    
    	irq_set_status_flags(irq, IRQ_NOREQUEST|IRQ_NOPROBE);
    
    	raw_spin_lock_irqsave(&vector_lock, flags);
    
    	__clear_irq_vector(irq, cfg);
    
    	raw_spin_unlock_irqrestore(&vector_lock, flags);
    
    	free_irq_at(irq, cfg);
    
    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(cfg)) {
    
    		compose_remapped_msi_msg(pdev, irq, dest, msg, hpet_id);
    
    	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
    msi_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
    
    	struct irq_cfg *cfg = data->chip_data;
    
    	if (__ioapic_set_affinity(data, mask, &dest))
    
    	__get_cached_msi_msg(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(data->msi_desc, &msg);
    
    /*
     * 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,
    
    	.irq_set_affinity	= msi_set_affinity,
    
    	.irq_retrigger		= ioapic_retrigger_irq,
    
    static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, int irq)
    
    	struct irq_chip *chip = &msi_chip;
    
    	struct msi_msg msg;
    
    	ret = msi_compose_msg(dev, irq, &msg, -1);
    
    	if (ret < 0)
    		return ret;
    
    
    	irq_set_msi_desc(irq, msidesc);
    
    	write_msi_msg(irq, &msg);
    
    
    	if (irq_remapped(irq_get_chip_data(irq))) {
    
    		irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
    
    		irq_remap_modify_chip_defaults(chip);
    
    	}
    
    	irq_set_chip_and_handler_name(irq, chip, handle_edge_irq, "edge");
    
    	dev_printk(KERN_DEBUG, &dev->dev, "irq %d for MSI/MSI-X\n", irq);
    
    
    int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
    
    	int node, ret, sub_handle, index = 0;
    	unsigned int irq, irq_want;
    
    	struct msi_desc *msidesc;
    
    	/* 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 (!irq_remapping_enabled)
    
    			goto no_ir;
    
    		if (!sub_handle) {
    			/*
    			 * allocate the consecutive block of IRTE's
    			 * for 'nvec'
    			 */
    
    			index = msi_alloc_remapped_irq(dev, irq, nvec);
    
    			if (index < 0) {
    				ret = index;
    				goto error;
    			}
    		} else {
    
    			ret = msi_setup_remapped_irq(dev, irq, index,
    						     sub_handle);
    
    		ret = setup_msi_irq(dev, msidesc, irq);
    
    		if (ret < 0)
    			goto error;
    		sub_handle++;
    	}
    	return 0;
    
    	destroy_irq(irq);
    	return ret;
    
    void native_teardown_msi_irq(unsigned int irq)
    
    	destroy_irq(irq);
    
    #ifdef CONFIG_DMAR_TABLE
    
    #ifdef CONFIG_SMP
    
    static int
    dmar_msi_set_affinity(struct irq_data *data, const struct cpumask *mask,
    		      bool force)
    
    	struct irq_cfg *cfg = data->chip_data;
    	unsigned int dest, irq = data->irq;
    
    	if (__ioapic_set_affinity(data, mask, &dest))
    
    
    	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);
    
    	msg.address_hi = MSI_ADDR_BASE_HI | MSI_ADDR_EXT_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
    
    	.irq_set_affinity	= dmar_msi_set_affinity,
    
    	.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);
    
    	irq_set_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq,
    				      "edge");
    
    #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_cfg *cfg = data->chip_data;
    
    	struct msi_msg msg;
    	unsigned int dest;
    
    
    	if (__ioapic_set_affinity(data, 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 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)
    
    	struct irq_chip *chip = &hpet_msi_type;
    
    	if (irq_remapping_enabled) {
    		if (!setup_hpet_msi_remapped(irq, id))
    
    			return -1;
    	}
    
    	ret = msi_compose_msg(NULL, irq, &msg, id);
    
    	hpet_msi_write(irq_get_handler_data(irq), &msg);
    
    	irq_set_status_flags(irq, IRQ_MOVE_PCNTXT);
    
    	if (irq_remapped(irq_get_chip_data(irq)))
    
    		irq_remap_modify_chip_defaults(chip);
    
    	irq_set_chip_and_handler_name(irq, chip, 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
    ht_set_affinity(struct irq_data *data, const struct cpumask *mask, bool force)
    
    	struct irq_cfg *cfg = data->chip_data;
    
    	if (__ioapic_set_affinity(data, mask, &dest))
    
    	target_ht_irq(data->irq, dest, cfg->vector);
    
    static struct irq_chip ht_irq_chip = {
    
    	.name			= "PCI-HT",
    	.irq_mask		= mask_ht_irq,
    	.irq_unmask		= unmask_ht_irq,
    	.irq_ack		= ack_apic_edge,
    
    	.irq_set_affinity	= ht_set_affinity,
    
    	.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;
    
    
    		irq_set_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;
    
    io_apic_setup_irq_pin(unsigned int irq, int node, struct io_apic_irq_attr *attr)
    {
    	struct irq_cfg *cfg = alloc_irq_and_cfg_at(irq, node);
    	int ret;
    
    	if (!cfg)
    		return -EINVAL;
    	ret = __add_pin_to_irq_node(cfg, node, attr->ioapic, attr->ioapic_pin);
    	if (!ret)
    
    		setup_ioapic_irq(irq, cfg, attr);
    
    int io_apic_setup_irq_pin_once(unsigned int irq, int node,
    			       struct io_apic_irq_attr *attr)
    
    	unsigned int ioapic_idx = attr->ioapic, pin = attr->ioapic_pin;
    
    	int ret;
    
    	/* Avoid redundant programming */
    
    	if (test_bit(pin, ioapics[ioapic_idx].pin_programmed)) {
    
    		pr_debug("Pin %d-%d already programmed\n",
    
    			 mpc_ioapic_id(ioapic_idx), pin);
    
    		return 0;
    	}
    	ret = io_apic_setup_irq_pin(irq, node, attr);
    	if (!ret)
    
    		set_bit(pin, ioapics[ioapic_idx].pin_programmed);
    
    static 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;
    
    static 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);
    
    int get_nr_irqs_gsi(void)
    {
    	return nr_irqs_gsi;
    }
    
    
    Yinghai Lu's avatar
    Yinghai Lu committed
    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;
    
    int io_apic_set_pci_routing(struct device *dev, int irq,
    			    struct io_apic_irq_attr *irq_attr)
    
    {
    	int node;
    
    	if (!IO_APIC_IRQ(irq)) {
    		apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n",
    
    	node = dev ? dev_to_node(dev) : cpu_to_node(0);
    
    	return io_apic_setup_irq_pin_once(irq, node, irq_attr);
    
    #ifdef CONFIG_X86_32
    
    static 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;
    }
    
    
    static u8 __init io_apic_unique_id(u8 id)
    {
    	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
    static u8 __init io_apic_unique_id(u8 id)
    {
    	int i;
    	DECLARE_BITMAP(used, 256);
    
    	bitmap_zero(used, 256);
    	for (i = 0; i < nr_ioapics; i++) {
    
    		__set_bit(mpc_ioapic_id(i), used);
    
    	}
    	if (!test_bit(id, used))
    		return id;
    	return find_first_zero_bit(used, 256);
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static int __init io_apic_get_version(int ioapic)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    	union IO_APIC_reg_01	reg_01;
    	unsigned long flags;
    
    
    	raw_spin_lock_irqsave(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	reg_01.raw = io_apic_read(ioapic, 1);
    
    	raw_spin_unlock_irqrestore(&ioapic_lock, flags);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	return reg_01.bits.version;
    }
    
    
    int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity)
    
    
    	if (skip_ioapic_setup)
    		return -1;
    
    
    	ioapic = mp_find_ioapic(gsi);
    	if (ioapic < 0)
    
    	pin = mp_find_ioapic_pin(ioapic, gsi);
    	if (pin < 0)
    		return -1;
    
    	idx = find_irq_entry(ioapic, pin, mp_INT);
    	if (idx < 0)
    
    	*trigger = irq_trigger(idx);
    	*polarity = irq_polarity(idx);
    
    /*
     * This function currently is only a helper for the i386 smp boot process where
     * we need to reprogram the ioredtbls to cater for the cpus which have come online
    
     * so mask in all cases should simply be apic->target_cpus()
    
     */
    #ifdef CONFIG_SMP
    void __init setup_ioapic_dest(void)
    {
    
    	int pin, ioapic, irq, irq_entry;
    
    	const struct cpumask *mask;
    
    	struct irq_data *idata;
    
    
    	if (skip_ioapic_setup == 1)
    		return;
    
    
    	for (ioapic = 0; ioapic < nr_ioapics; ioapic++)
    
    	for (pin = 0; pin < ioapics[ioapic].nr_registers; pin++) {
    
    		irq_entry = find_irq_entry(ioapic, pin, mp_INT);
    		if (irq_entry == -1)
    			continue;
    		irq = pin_2_irq(irq_entry, ioapic, pin);
    
    		if ((ioapic > 0) && (irq > 16))
    			continue;
    
    
    		idata = irq_get_irq_data(irq);
    
    		/*
    		 * Honour affinities which have been set in early boot
    		 */
    
    		if (!irqd_can_balance(idata) || irqd_affinity_was_set(idata))
    			mask = idata->affinity;
    
    		else
    			mask = apic->target_cpus();
    
    		if (irq_remapping_enabled)
    			set_remapped_irq_affinity(idata, mask, false);
    
    			ioapic_set_affinity(idata, mask, false);
    
    #define IOAPIC_RESOURCE_NAME_SIZE 11
    
    static struct resource *ioapic_resources;
    
    
    static struct resource * __init ioapic_setup_resources(int nr_ioapics)
    
    {
    	unsigned long n;
    	struct resource *res;
    	char *mem;
    	int i;
    
    	if (nr_ioapics <= 0)
    		return NULL;
    
    	n = IOAPIC_RESOURCE_NAME_SIZE + sizeof(struct resource);
    	n *= nr_ioapics;
    
    	mem = alloc_bootmem(n);
    	res = (void *)mem;
    
    
    	mem += sizeof(struct resource) * nr_ioapics;
    
    	for (i = 0; i < nr_ioapics; i++) {
    		res[i].name = mem;
    		res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY;
    
    		snprintf(mem, IOAPIC_RESOURCE_NAME_SIZE, "IOAPIC %u", i);
    
    void __init native_io_apic_init_mappings(void)
    
    {
    	unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0;
    
    	struct resource *ioapic_res;
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    	int i;
    
    	ioapic_res = ioapic_setup_resources(nr_ioapics);
    
    	for (i = 0; i < nr_ioapics; i++) {
    		if (smp_found_config) {
    
    			ioapic_phys = mpc_ioapic_addr(i);
    
    #ifdef CONFIG_X86_32
    
    Thomas Gleixner's avatar
    Thomas Gleixner committed
    			if (!ioapic_phys) {
    				printk(KERN_ERR
    				       "WARNING: bogus zero IO-APIC "
    				       "address found in MPTABLE, "
    				       "disabling IO/APIC support!\n");
    				smp_found_config = 0;
    				skip_ioapic_setup = 1;
    				goto fake_ioapic_page;
    			}
    
    #ifdef CONFIG_X86_32
    
    			ioapic_phys = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
    
    			ioapic_phys = __pa(ioapic_phys);
    		}
    		set_fixmap_nocache(idx, ioapic_phys);
    
    		apic_printk(APIC_VERBOSE, "mapped IOAPIC to %08lx (%08lx)\n",
    			__fix_to_virt(idx) + (ioapic_phys & ~PAGE_MASK),
    			ioapic_phys);
    
    		ioapic_res->end = ioapic_phys + IO_APIC_SLOT_SIZE - 1;
    
    void __init ioapic_insert_resources(void)
    
    {
    	int i;
    	struct resource *r = ioapic_resources;
    
    	if (!r) {
    
    			printk(KERN_ERR
    				"IO APIC resources couldn't be allocated.\n");
    
    	}
    
    	for (i = 0; i < nr_ioapics; i++) {
    		insert_resource(&iomem_resource, r);
    		r++;
    	}
    }
    
    int mp_find_ioapic(u32 gsi)
    
    	/* Find the IOAPIC that manages this GSI. */
    	for (i = 0; i < nr_ioapics; i++) {
    
    		struct mp_ioapic_gsi *gsi_cfg = mp_ioapic_gsi_routing(i);
    		if ((gsi >= gsi_cfg->gsi_base)
    		    && (gsi <= gsi_cfg->gsi_end))
    
    	printk(KERN_ERR "ERROR: Unable to locate IOAPIC for GSI %d\n", gsi);
    	return -1;
    }
    
    
    int mp_find_ioapic_pin(int ioapic, u32 gsi)
    
    	if (WARN_ON(ioapic == -1))
    		return -1;
    
    
    	gsi_cfg = mp_ioapic_gsi_routing(ioapic);
    	if (WARN_ON(gsi > gsi_cfg->gsi_end))
    
    	return gsi - gsi_cfg->gsi_base;
    
    static __init int bad_ioapic(unsigned long address)
    
    {
    	if (nr_ioapics >= MAX_IO_APICS) {
    
    		pr_warn("WARNING: Max # of I/O APICs (%d) exceeded (found %d), skipping\n",
    			MAX_IO_APICS, nr_ioapics);
    
    		pr_warn("WARNING: Bogus (zero) I/O APIC address found in table, skipping!\n");
    
    static __init int bad_ioapic_register(int idx)
    {
    	union IO_APIC_reg_00 reg_00;
    	union IO_APIC_reg_01 reg_01;
    	union IO_APIC_reg_02 reg_02;
    
    	reg_00.raw = io_apic_read(idx, 0);
    	reg_01.raw = io_apic_read(idx, 1);
    	reg_02.raw = io_apic_read(idx, 2);
    
    	if (reg_00.raw == -1 && reg_01.raw == -1 && reg_02.raw == -1) {
    		pr_warn("I/O APIC 0x%x registers return all ones, skipping!\n",
    			mpc_ioapic_addr(idx));
    		return 1;
    	}
    
    	return 0;
    }
    
    
    void __init mp_register_ioapic(int id, u32 address, u32 gsi_base)
    {
    	int idx = 0;
    
    	struct mp_ioapic_gsi *gsi_cfg;
    
    
    	if (bad_ioapic(address))
    		return;
    
    	idx = nr_ioapics;
    
    
    	ioapics[idx].mp_config.type = MP_IOAPIC;
    	ioapics[idx].mp_config.flags = MPC_APIC_USABLE;
    	ioapics[idx].mp_config.apicaddr = address;
    
    
    	set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address);
    
    
    	if (bad_ioapic_register(idx)) {
    		clear_fixmap(FIX_IO_APIC_BASE_0 + idx);
    		return;
    	}
    
    
    	ioapics[idx].mp_config.apicid = io_apic_unique_id(id);
    	ioapics[idx].mp_config.apicver = io_apic_get_version(idx);
    
    
    	/*
    	 * Build basic GSI lookup table to facilitate gsi->io_apic lookups
    	 * and to prevent reprogramming of IOAPIC pins (PCI GSIs).
    	 */
    
    	entries = io_apic_get_redir_entries(idx);
    
    	gsi_cfg = mp_ioapic_gsi_routing(idx);
    	gsi_cfg->gsi_base = gsi_base;
    	gsi_cfg->gsi_end = gsi_base + entries - 1;
    
    
    	/*
    	 * The number of IO-APIC IRQ registers (== #pins):
    	 */
    
    	ioapics[idx].nr_registers = entries;
    
    	if (gsi_cfg->gsi_end >= gsi_top)
    		gsi_top = gsi_cfg->gsi_end + 1;
    
    	pr_info("IOAPIC[%d]: apic_id %d, version %d, address 0x%x, GSI %d-%d\n",
    		idx, mpc_ioapic_id(idx),
    		mpc_ioapic_ver(idx), mpc_ioapic_addr(idx),
    		gsi_cfg->gsi_base, gsi_cfg->gsi_end);
    
    
    /* Enable IOAPIC early just for system timer */
    void __init pre_init_apic_IRQ0(void)
    {
    
    	struct io_apic_irq_attr attr = { 0, 0, 0, 0 };
    
    
    	printk(KERN_INFO "Early APIC setup for system timer0\n");
    #ifndef CONFIG_SMP
    
    	physid_set_mask_of_physid(boot_cpu_physical_apicid,
    					 &phys_cpu_present_map);
    
    #endif
    	setup_local_APIC();
    
    
    	io_apic_setup_irq_pin(0, 0, &attr);
    
    	irq_set_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq,
    				      "edge");