aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c
Commit message (Expand)AuthorAgeFilesLines
* gpio-button-hotplug: fix data raceAndrey Erokhin2022-04-091-1/+1
* gpio-button-hotplug: convert to gpio descriptor (gpiod_) APIChristian Lamparter2021-08-261-79/+63
* gpio-button-hotplug: remove duplicate logging textPetr Štetiar2020-03-081-3/+3
* gpio-button-hotplug: add volume button handlingChuanhong Guo2019-08-081-0/+2
* gpio-button-hotplug: unify polled and interrupt codeDavid Bauer2019-07-141-65/+76
* gpio-button-hotplug: fix 4.19 build breakage on malta/be64Petr Štetiar2019-06-171-0/+1
* gpio-button-hotplug: gpio-keys: fix always missing first eventPetr Štetiar2019-06-091-9/+2
* gpio-button-hotplug: fix wrong initial seen valuePetr Štetiar2019-06-091-0/+3
* gpio-button-hotplug: use pr_debug and pr_errPetr Štetiar2019-06-091-17/+6
* gpio-button-hotplug: support interrupt propertiesChristian Lamparter2019-05-311-21/+93
* gpio-button-hotplug: add KEY_POWER2 handlingAlan Swanson2019-05-301-0/+1
* gpio-button-hotplug: leave platform_device.dev.platform_data untouchedFurong Xu2017-08-211-2/+1
* gpio-button-hotplug: add more buttonsMathias Kresin2016-11-271-15/+24
* treewide: replace nbd@openwrt.org with nbd@nbd.nameFelix Fietkau2016-06-071-3/+3
* kernel: gpio-button-hotplug: Add missing ONESHOT flag to threaded IRQ requestJohn Crispin2016-03-031-1/+1
* kernel: gpio-button-hotplug: update to use threaded irq'sJohn Crispin2016-02-121-8/+5
* gpio-button-hotplug: handle EPROBE_DEFER and other errorsHauke Mehrtens2015-07-261-5/+15
* gpio-button-hotplug: remove #ifdef CONFIG_HOTPLUG, it is gone in newer kernel...Felix Fietkau2014-05-231-7/+0
* gpio-button-hotplug: add wwan buttonHauke Mehrtens2014-01-141-0/+1
* gpio-button-hotplug: fix crash on removeJonas Gorski2013-12-171-1/+1
* gpio-button-hotplug: add irq mode to driverJohn Crispin2013-12-091-94/+176
* gpio-button-hotplug: add support for sliding switchesJohn Crispin2013-11-111-12/+8
* gpio-button-hotplug: add support for power buttonsJohn Crispin2013-10-281-0/+1
* gpio-button-hotplug: debounce the initial button state, the first reads at bo...Felix Fietkau2013-08-051-2/+4
* gpio-button-hotplug: cleanup, fix compiler warningFelix Fietkau2013-08-031-4/+2
* gpio-button-hotplug: fix active_low handling, possibly broken in r37643Felix Fietkau2013-08-031-8/+9
* gpio-button-hotplug: use gpio_button_get_value() to initialize last_state.John Crispin2013-08-011-1/+1
* gpio-button-hotplug: use gpio_button_get_value() to fetch state.John Crispin2013-08-011-4/+1
* gpio-button-hotplug: add inline function gpio_button_get_value().John Crispin2013-08-011-0/+9
* gpio-button-hotplug: add support for EV_SWLuka Perkov2013-07-021-7/+21
* gpio-button-hotplug: improve gpio button debouncing, verify state changes ove...Felix Fietkau2013-06-291-6/+8
* packages: clean up the package folderJohn Crispin2013-06-211-0/+564
439' href='#n439'>439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
/*
 * I/O SAPIC support.
 *
 * Copyright (C) 1999 Intel Corp.
 * Copyright (C) 1999 Asit Mallick <asit.k.mallick@intel.com>
 * Copyright (C) 2000-2002 J.I. Lee <jung-ik.lee@intel.com>
 * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co.
 *	David Mosberger-Tang <davidm@hpl.hp.com>
 * Copyright (C) 1999 VA Linux Systems
 * Copyright (C) 1999,2000 Walt Drummond <drummond@valinux.com>
 *
 * 00/04/19	D. Mosberger	Rewritten to mirror more closely the x86 I/O APIC code.
 *				In particular, we now have separate handlers for edge
 *				and level triggered interrupts.
 * 00/10/27	Asit Mallick, Goutham Rao <goutham.rao@intel.com> IRQ vector allocation
 *				PCI to vector mapping, shared PCI interrupts.
 * 00/10/27	D. Mosberger	Document things a bit more to make them more understandable.
 *				Clean up much of the old IOSAPIC cruft.
 * 01/07/27	J.I. Lee	PCI irq routing, Platform/Legacy interrupts and fixes for
 *				ACPI S5(SoftOff) support.
 * 02/01/23	J.I. Lee	iosapic pgm fixes for PCI irq routing from _PRT
 * 02/01/07     E. Focht        <efocht@ess.nec.de> Redirectable interrupt vectors in
 *                              iosapic_set_affinity(), initializations for
 *                              /proc/irq/#/smp_affinity
 * 02/04/02	P. Diefenbaugh	Cleaned up ACPI PCI IRQ routing.
 * 02/04/18	J.I. Lee	bug fix in iosapic_init_pci_irq
 * 02/04/30	J.I. Lee	bug fix in find_iosapic to fix ACPI PCI IRQ to IOSAPIC mapping
 *				error
 * 02/07/29	T. Kochi	Allocate interrupt vectors dynamically
 * 02/08/04	T. Kochi	Cleaned up terminology (irq, global system interrupt, vector, etc.)
 * 02/09/20	D. Mosberger	Simplified by taking advantage of ACPI's pci_irq code.
 * 03/02/19	B. Helgaas	Make pcat_compat system-wide, not per-IOSAPIC.
 *				Remove iosapic_address & gsi_base from external interfaces.
 *				Rationalize __init/__devinit attributes.
 * 04/12/04 Ashok Raj	<ashok.raj@intel.com> Intel Corporation 2004
 *				Updated to work with irq migration necessary for CPU Hotplug
 */
/*
 * Here is what the interrupt logic between a PCI device and the kernel looks like:
 *
 * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, INTD).  The
 *     device is uniquely identified by its bus--, and slot-number (the function
 *     number does not matter here because all functions share the same interrupt
 *     lines).
 *
 * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC controller.
 *     Multiple interrupt lines may have to share the same IOSAPIC pin (if they're level
 *     triggered and use the same polarity).  Each interrupt line has a unique Global
 *     System Interrupt (GSI) number which can be calculated as the sum of the controller's
 *     base GSI number and the IOSAPIC pin number to which the line connects.
 *
 * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the IOSAPIC pin
 *     into the IA-64 interrupt vector.  This interrupt vector is then sent to the CPU.
 *
 * (4) The kernel recognizes an interrupt as an IRQ.  The IRQ interface is used as
 *     architecture-independent interrupt handling mechanism in Linux.  As an
 *     IRQ is a number, we have to have IA-64 interrupt vector number <-> IRQ number
 *     mapping.  On smaller systems, we use one-to-one mapping between IA-64 vector and
 *     IRQ.  A platform can implement platform_irq_to_vector(irq) and
 *     platform_local_vector_to_irq(vector) APIs to differentiate the mapping.
 *     Please see also include/asm-ia64/hw_irq.h for those APIs.
 *
 * To sum up, there are three levels of mappings involved:
 *
 *	PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ
 *
 * Note: The term "IRQ" is loosely used everywhere in Linux kernel to describe interrupts.
 * Now we use "IRQ" only for Linux IRQ's.  ISA IRQ (isa_irq) is the only exception in this
 * source code.
 */
#include <linux/config.h>

#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/list.h>
#ifndef XEN
#include <linux/pci.h>
#else
#include <xen/errno.h>
#endif
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/bootmem.h>

#include <asm/delay.h>
#include <asm/hw_irq.h>
#include <asm/io.h>
#include <asm/iosapic.h>
#include <asm/machvec.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/system.h>


#undef DEBUG_INTERRUPT_ROUTING

#ifdef DEBUG_INTERRUPT_ROUTING
#define DBG(fmt...)	printk(fmt)
#else
#define DBG(fmt...)
#endif

#define NR_PREALLOCATE_RTE_ENTRIES	(PAGE_SIZE / sizeof(struct iosapic_rte_info))
#define RTE_PREALLOCATED	(1)

static DEFINE_SPINLOCK(iosapic_lock);

/* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */

struct iosapic_rte_info {
	struct list_head rte_list;	/* node in list of RTEs sharing the same vector */
	char __iomem	*addr;		/* base address of IOSAPIC */
	unsigned int	gsi_base;	/* first GSI assigned to this IOSAPIC */
	char		rte_index;	/* IOSAPIC RTE index */
	int		refcnt;		/* reference counter */
	unsigned int	flags;		/* flags */
} ____cacheline_aligned;

static struct iosapic_intr_info {
	struct list_head rtes;		/* RTEs using this vector (empty => not an IOSAPIC interrupt) */
	int		count;		/* # of RTEs that shares this vector */
	u32		low32;		/* current value of low word of Redirection table entry */
	unsigned int	dest;		/* destination CPU physical ID */
	unsigned char	dmode	: 3;	/* delivery mode (see iosapic.h) */
	unsigned char 	polarity: 1;	/* interrupt polarity (see iosapic.h) */
	unsigned char	trigger	: 1;	/* trigger mode (see iosapic.h) */
} iosapic_intr_info[IA64_NUM_VECTORS];

static struct iosapic {
	char __iomem	*addr;		/* base address of IOSAPIC */
	unsigned int 	gsi_base;	/* first GSI assigned to this IOSAPIC */
	unsigned short 	num_rte;	/* number of RTE in this IOSAPIC */
	int		rtes_inuse;	/* # of RTEs in use on this IOSAPIC */
#ifdef CONFIG_NUMA
	unsigned short	node;		/* numa node association via pxm */
#endif
} iosapic_lists[NR_IOSAPICS];

static unsigned char pcat_compat __devinitdata;	/* 8259 compatibility flag */

static int iosapic_kmalloc_ok;
static LIST_HEAD(free_rte_list);

/*
 * Find an IOSAPIC associated with a GSI
 */
static inline int
find_iosapic (unsigned int gsi)
{
	int i;

	for (i = 0; i < NR_IOSAPICS; i++) {
		if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < iosapic_lists[i].num_rte)
			return i;
	}

	return -1;
}

static inline int
_gsi_to_vector (unsigned int gsi)
{
	struct iosapic_intr_info *info;
	struct iosapic_rte_info *rte;

	for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info)
		list_for_each_entry(rte, &info->rtes, rte_list)
			if (rte->gsi_base + rte->rte_index == gsi)
				return info - iosapic_intr_info;
	return -1;
}

/*
 * Translate GSI number to the corresponding IA-64 interrupt vector.  If no
 * entry exists, return -1.
 */
inline int
gsi_to_vector (unsigned int gsi)
{
	return _gsi_to_vector(gsi);
}

int
gsi_to_irq (unsigned int gsi)
{
	unsigned long flags;
	int irq;
	/*
	 * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq
	 * numbers...
	 */
	spin_lock_irqsave(&iosapic_lock, flags);
	{
		irq = _gsi_to_vector(gsi);
	}
	spin_unlock_irqrestore(&iosapic_lock, flags);

	return irq;
}

static struct iosapic_rte_info *gsi_vector_to_rte(unsigned int gsi, unsigned int vec)
{
	struct iosapic_rte_info *rte;

	list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list)
		if (rte->gsi_base + rte->rte_index == gsi)
			return rte;
	return NULL;
}

static void
set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask)
{
	unsigned long pol, trigger, dmode;
	u32 low32, high32;
	char __iomem *addr;
	int rte_index;
	char redir;
	struct iosapic_rte_info *rte;

	DBG(KERN_DEBUG"IOSAPIC: routing vector %d to 0x%x\n", vector, dest);

	rte = gsi_vector_to_rte(gsi, vector);
	if (!rte)
		return;		/* not an IOSAPIC interrupt */

	rte_index = rte->rte_index;
	addr	= rte->addr;
	pol     = iosapic_intr_info[vector].polarity;
	trigger = iosapic_intr_info[vector].trigger;
	dmode   = iosapic_intr_info[vector].dmode;

	redir = (dmode == IOSAPIC_LOWEST_PRIORITY) ? 1 : 0;

#ifdef CONFIG_SMP
	{
		unsigned int irq;

		for (irq = 0; irq < NR_IRQS; ++irq)
			if (irq_to_vector(irq) == vector) {
				set_irq_affinity_info(irq, (int)(dest & 0xffff), redir);
				break;
			}
	}
#endif

	low32 = ((pol << IOSAPIC_POLARITY_SHIFT) |
		 (trigger << IOSAPIC_TRIGGER_SHIFT) |
		 (dmode << IOSAPIC_DELIVERY_SHIFT) |
		 ((mask ? 1 : 0) << IOSAPIC_MASK_SHIFT) |
		 vector);

	/* dest contains both id and eid */
	high32 = (dest << IOSAPIC_DEST_SHIFT);

	iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
	iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
	iosapic_intr_info[vector].low32 = low32;
	iosapic_intr_info[vector].dest = dest;
}

static void
nop (unsigned int vector)
{
	/* do nothing... */
}

void
kexec_disable_iosapic(void)
{
        struct iosapic_intr_info *info;
        struct iosapic_rte_info *rte;
        u8 vec = 0;
        for (info = iosapic_intr_info; info <
                        iosapic_intr_info + IA64_NUM_VECTORS; ++info, ++vec) {
                list_for_each_entry(rte, &info->rtes,
                                rte_list) {
                        iosapic_write(rte->addr,
                                        IOSAPIC_RTE_LOW(rte->rte_index),
                                        IOSAPIC_MASK|vec);
                        iosapic_eoi(rte->addr, vec);
                }
        }
}

static void
mask_irq (unsigned int irq)
{
	unsigned long flags;
	char __iomem *addr;
	u32 low32;
	int rte_index;
	ia64_vector vec = irq_to_vector(irq);
	struct iosapic_rte_info *rte;

	if (list_empty(&iosapic_intr_info[vec].rtes))
		return;			/* not an IOSAPIC interrupt! */

	spin_lock_irqsave(&iosapic_lock, flags);
	{
		/* set only the mask bit */
		low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK;
		list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
			addr = rte->addr;
			rte_index = rte->rte_index;
			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
		}
	}
	spin_unlock_irqrestore(&iosapic_lock, flags);
}

static void
unmask_irq (unsigned int irq)
{
	unsigned long flags;
	char __iomem *addr;
	u32 low32;
	int rte_index;
	ia64_vector vec = irq_to_vector(irq);
	struct iosapic_rte_info *rte;

	if (list_empty(&iosapic_intr_info[vec].rtes))
		return;			/* not an IOSAPIC interrupt! */

	spin_lock_irqsave(&iosapic_lock, flags);
	{
		low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK;
		list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
			addr = rte->addr;
			rte_index = rte->rte_index;
			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
		}
	}
	spin_unlock_irqrestore(&iosapic_lock, flags);
}


static void
iosapic_set_affinity (unsigned int irq, cpumask_t mask)
{
#ifdef CONFIG_SMP
	unsigned long flags;
	u32 high32, low32;
	int dest, rte_index;
	char __iomem *addr;
	int redir = (irq & IA64_IRQ_REDIRECTED) ? 1 : 0;
	ia64_vector vec;
	struct iosapic_rte_info *rte;

	irq &= (~IA64_IRQ_REDIRECTED);
	vec = irq_to_vector(irq);

	if (cpus_empty(mask))
		return;

	dest = cpu_physical_id(first_cpu(mask));

	if (list_empty(&iosapic_intr_info[vec].rtes))
		return;			/* not an IOSAPIC interrupt */

	set_irq_affinity_info(irq, dest, redir);

	/* dest contains both id and eid */
	high32 = dest << IOSAPIC_DEST_SHIFT;

	spin_lock_irqsave(&iosapic_lock, flags);
	{
		low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT);

		if (redir)
		        /* change delivery mode to lowest priority */
			low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT);
		else
		        /* change delivery mode to fixed */
			low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT);

		iosapic_intr_info[vec].low32 = low32;
		iosapic_intr_info[vec].dest = dest;
		list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) {
			addr = rte->addr;
			rte_index = rte->rte_index;
			iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32);
			iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32);
		}
	}
	spin_unlock_irqrestore(&iosapic_lock, flags);
#endif
}

/*
 * Handlers for level-triggered interrupts.
 */

static unsigned int
iosapic_startup_level_irq (unsigned int irq)
{
	unmask_irq(irq);
	return 0;
}

static void
iosapic_end_level_irq (unsigned int irq)
{
	ia64_vector vec = irq_to_vector(irq);
	struct iosapic_rte_info *rte;

	move_irq(irq);
	list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list)
		iosapic_eoi(rte->addr, vec);
}

#define iosapic_shutdown_level_irq	mask_irq
#define iosapic_enable_level_irq	unmask_irq
#define iosapic_disable_level_irq	mask_irq
#define iosapic_ack_level_irq		nop

struct hw_interrupt_type irq_type_iosapic_level = {
	.typename =	"IO-SAPIC-level",
	.startup =	iosapic_startup_level_irq,
	.shutdown =	iosapic_shutdown_level_irq,
	.enable =	iosapic_enable_level_irq,
	.disable =	iosapic_disable_level_irq,
	.ack =		iosapic_ack_level_irq,
	.end =		iosapic_end_level_irq,
	.set_affinity =	iosapic_set_affinity
};

/*
 * Handlers for edge-triggered interrupts.
 */

static unsigned int
iosapic_startup_edge_irq (unsigned int irq)
{
	unmask_irq(irq);
	/*
	 * IOSAPIC simply drops interrupts pended while the
	 * corresponding pin was masked, so we can't know if an
	 * interrupt is pending already.  Let's hope not...
	 */
	return 0;
}

static void
iosapic_ack_edge_irq (unsigned int irq)
{
	irq_desc_t *idesc = irq_descp(irq);

	move_irq(irq);
	/*
	 * Once we have recorded IRQ_PENDING already, we can mask the
	 * interrupt for real. This prevents IRQ storms from unhandled
	 * devices.
	 */
	if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED))
		mask_irq(irq);
}

#define iosapic_enable_edge_irq		unmask_irq
#define iosapic_disable_edge_irq	nop
#define iosapic_end_edge_irq		nop

struct hw_interrupt_type irq_type_iosapic_edge = {
	.typename =	"IO-SAPIC-edge",
	.startup =	iosapic_startup_edge_irq,
	.shutdown =	iosapic_disable_edge_irq,
	.enable =	iosapic_enable_edge_irq,
	.disable =	iosapic_disable_edge_irq,
	.ack =		iosapic_ack_edge_irq,
	.end =		iosapic_end_edge_irq,
	.set_affinity =	iosapic_set_affinity
};

unsigned int
iosapic_version (char __iomem *addr)
{
	/*
	 * IOSAPIC Version Register return 32 bit structure like:
	 * {
	 *	unsigned int version   : 8;
	 *	unsigned int reserved1 : 8;
	 *	unsigned int max_redir : 8;
	 *	unsigned int reserved2 : 8;
	 * }
	 */
	return iosapic_read(addr, IOSAPIC_VERSION);
}

static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long pol)
{
	int i, vector = -1, min_count = -1;
	struct iosapic_intr_info *info;

	/*
	 * shared vectors for edge-triggered interrupts are not
	 * supported yet
	 */
	if (trigger == IOSAPIC_EDGE)
		return -1;

	for (i = IA64_FIRST_DEVICE_VECTOR; i <= IA64_LAST_DEVICE_VECTOR; i++) {
		info = &iosapic_intr_info[i];
		if (info->trigger == trigger && info->polarity == pol &&
		    (info->dmode == IOSAPIC_FIXED || info->dmode == IOSAPIC_LOWEST_PRIORITY)) {
			if (min_count == -1 || info->count < min_count) {
				vector = i;
				min_count = info->count;
			}
		}
	}

	return vector;
}

/*
 * if the given vector is already owned by other,
 *  assign a new vector for the other and make the vector available
 */
static void __init
iosapic_reassign_vector (int vector)
{
	int new_vector;

	if (!list_empty(&iosapic_intr_info[vector].rtes)) {
		new_vector = assign_irq_vector(AUTO_ASSIGN);
		if (new_vector < 0)
			panic("%s: out of interrupt vectors!\n", __FUNCTION__);
		printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector);
		memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector],
		       sizeof(struct iosapic_intr_info));
		INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rtes);
		list_move(iosapic_intr_info[vector].rtes.next, &iosapic_intr_info[new_vector].rtes);
		memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
		iosapic_intr_info[vector].low32 = IOSAPIC_MASK;
		INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);
	}
}

static struct iosapic_rte_info *iosapic_alloc_rte (void)
{
	int i;
	struct iosapic_rte_info *rte;
	int preallocated = 0;

	if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) {
#ifdef XEN
		rte = xmalloc_bytes(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES);
#else
		rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES);
#endif
		if (!rte)
			return NULL;
		for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++)
			list_add(&rte->rte_list, &free_rte_list);
	}

	if (!list_empty(&free_rte_list)) {
		rte = list_entry(free_rte_list.next, struct iosapic_rte_info, rte_list);
		list_del(&rte->rte_list);
		preallocated++;
	} else {
		rte = kmalloc(sizeof(struct iosapic_rte_info), GFP_ATOMIC);
		if (!rte)
			return NULL;
	}

	memset(rte, 0, sizeof(struct iosapic_rte_info));
	if (preallocated)
		rte->flags |= RTE_PREALLOCATED;

	return rte;
}

static void iosapic_free_rte (struct iosapic_rte_info *rte)
{
	if (rte->flags & RTE_PREALLOCATED)
		list_add_tail(&rte->rte_list, &free_rte_list);
	else
		kfree(rte);
}

static inline int vector_is_shared (int vector)
{
	return (iosapic_intr_info[vector].count > 1);
}

static int
register_intr (unsigned int gsi, int vector, unsigned char delivery,
	       unsigned long polarity, unsigned long trigger)
{
	irq_desc_t *idesc;
	struct hw_interrupt_type *irq_type;
	int rte_index;
	int index;
	unsigned long gsi_base;
	void __iomem *iosapic_address;
	struct iosapic_rte_info *rte;

	index = find_iosapic(gsi);
	if (index < 0) {
		printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", __FUNCTION__, gsi);
		return -ENODEV;
	}

	iosapic_address = iosapic_lists[index].addr;
	gsi_base = iosapic_lists[index].gsi_base;

	rte = gsi_vector_to_rte(gsi, vector);
	if (!rte) {
		rte = iosapic_alloc_rte();
		if (!rte) {
			printk(KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__);
			return -ENOMEM;
		}

		rte_index = gsi - gsi_base;
		rte->rte_index	= rte_index;
		rte->addr	= iosapic_address;
		rte->gsi_base	= gsi_base;
		rte->refcnt++;
		list_add_tail(&rte->rte_list, &iosapic_intr_info[vector].rtes);
		iosapic_intr_info[vector].count++;
		iosapic_lists[index].rtes_inuse++;
	}
	else if (vector_is_shared(vector)) {
		struct iosapic_intr_info *info = &iosapic_intr_info[vector];
		if (info->trigger != trigger || info->polarity != polarity) {
			printk (KERN_WARNING "%s: cannot override the interrupt\n", __FUNCTION__);
			return -EINVAL;
		}
	}

	iosapic_intr_info[vector].polarity = polarity;
	iosapic_intr_info[vector].dmode    = delivery;
	iosapic_intr_info[vector].trigger  = trigger;

	if (trigger == IOSAPIC_EDGE)
		irq_type = &irq_type_iosapic_edge;
	else
		irq_type = &irq_type_iosapic_level;

	idesc = irq_descp(vector);
	if (idesc->handler != irq_type) {
		if (idesc->handler != &no_irq_type)
			printk(KERN_WARNING "%s: changing vector %d from %s to %s\n",
			       __FUNCTION__, vector, idesc->handler->typename, irq_type->typename);
		idesc->handler = irq_type;
	}
	return 0;
}

static unsigned int
get_target_cpu (unsigned int gsi, int vector)
{
#ifdef CONFIG_SMP
	static int cpu = -1;

	/*
	 * In case of vector shared by multiple RTEs, all RTEs that
	 * share the vector need to use the same destination CPU.
	 */
	if (!list_empty(&iosapic_intr_info[vector].rtes))
		return iosapic_intr_info[vector].dest;

	/*
	 * If the platform supports redirection via XTP, let it
	 * distribute interrupts.
	 */
	if (smp_int_redirect & SMP_IRQ_REDIRECTION)
		return cpu_physical_id(smp_processor_id());

	/*
	 * Some interrupts (ACPI SCI, for instance) are registered
	 * before the BSP is marked as online.
	 */
	if (!cpu_online(smp_processor_id()))
		return cpu_physical_id(smp_processor_id());

#ifdef CONFIG_NUMA
	{
		int num_cpus, cpu_index, iosapic_index, numa_cpu, i = 0;
		cpumask_t cpu_mask;

		iosapic_index = find_iosapic(gsi);
		if (iosapic_index < 0 ||
		    iosapic_lists[iosapic_index].node == MAX_NUMNODES)
			goto skip_numa_setup;

		cpu_mask = node_to_cpumask(iosapic_lists[iosapic_index].node);

		for_each_cpu_mask(numa_cpu, cpu_mask) {
			if (!cpu_online(numa_cpu))
				cpu_clear(numa_cpu, cpu_mask);
		}

		num_cpus = cpus_weight(cpu_mask);

		if (!num_cpus)
			goto skip_numa_setup;

		/* Use vector assigment to distribute across cpus in node */
		cpu_index = vector % num_cpus;

		for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++)
			numa_cpu = next_cpu(numa_cpu, cpu_mask);

		if (numa_cpu != NR_CPUS)
			return cpu_physical_id(numa_cpu);
	}
skip_numa_setup:
#endif
	/*
	 * Otherwise, round-robin interrupt vectors across all the
	 * processors.  (It'd be nice if we could be smarter in the
	 * case of NUMA.)
	 */
	do {
		if (++cpu >= NR_CPUS)
			cpu = 0;
	} while (!cpu_online(cpu));

	return cpu_physical_id(cpu);
#else
	return cpu_physical_id(smp_processor_id());
#endif
}

/*
 * ACPI can describe IOSAPIC interrupts via static tables and namespace
 * methods.  This provides an interface to register those interrupts and
 * program the IOSAPIC RTE.
 */
int
iosapic_register_intr (unsigned int gsi,
		       unsigned long polarity, unsigned long trigger)
{
	int vector, mask = 1, err;
	unsigned int dest;
	unsigned long flags;
	struct iosapic_rte_info *rte;
	u32 low32;
again:
	/*
	 * If this GSI has already been registered (i.e., it's a
	 * shared interrupt, or we lost a race to register it),
	 * don't touch the RTE.
	 */
	spin_lock_irqsave(&iosapic_lock, flags);
	{
		vector = gsi_to_vector(gsi);
		if (vector > 0) {
			rte = gsi_vector_to_rte(gsi, vector);
			rte->refcnt++;
			spin_unlock_irqrestore(&iosapic_lock, flags);
			return vector;
		}
	}
	spin_unlock_irqrestore(&iosapic_lock, flags);

	/* If vector is running out, we try to find a sharable vector */
	vector = assign_irq_vector(AUTO_ASSIGN);
	if (vector < 0) {
		vector = iosapic_find_sharable_vector(trigger, polarity);
  		if (vector < 0)
			return -ENOSPC;
	}

	spin_lock_irqsave(&irq_descp(vector)->lock, flags);
	spin_lock(&iosapic_lock);
	{
		if (gsi_to_vector(gsi) > 0) {
			if (list_empty(&iosapic_intr_info[vector].rtes))
				free_irq_vector(vector);
			spin_unlock(&iosapic_lock);
			spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
			goto again;
		}

		dest = get_target_cpu(gsi, vector);
		err = register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY,
			      polarity, trigger);
		if (err < 0) {
			spin_unlock(&iosapic_lock);
			spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);
			return err;
		}

		/*
		 * If the vector is shared and already unmasked for
		 * other interrupt sources, don't mask it.
		 */
		low32 = iosapic_intr_info[vector].low32;
		if (vector_is_shared(vector) && !(low32 & IOSAPIC_MASK))
			mask = 0;
		set_rte(gsi, vector, dest, mask);
	}
	spin_unlock(&iosapic_lock);
	spin_unlock_irqrestore(&irq_descp(vector)->lock, flags);

	printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
	       gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
	       (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
	       cpu_logical_id(dest), dest, vector);

	return vector;
}

void
iosapic_unregister_intr (unsigned int gsi)
{
	unsigned long flags;
	int irq, vector, index;
	irq_desc_t *idesc;
	u32 low32;
	unsigned long trigger, polarity;
	unsigned int dest;
	struct iosapic_rte_info *rte;

	/*
	 * If the irq associated with the gsi is not found,
	 * iosapic_unregister_intr() is unbalanced. We need to check
	 * this again after getting locks.
	 */
	irq = gsi_to_irq(gsi);
	if (irq < 0) {
		printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
		WARN_ON(1);
		return;
	}
	vector = irq_to_vector(irq);

	idesc = irq_descp(irq);
	spin_lock_irqsave(&idesc->lock, flags);
	spin_lock(&iosapic_lock);
	{
		if ((rte = gsi_vector_to_rte(gsi, vector)) == NULL) {
			printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi);
			WARN_ON(1);
			goto out;
		}

		if (--rte->refcnt > 0)
			goto out;

		/* Mask the interrupt */
		low32 = iosapic_intr_info[vector].low32 | IOSAPIC_MASK;
		iosapic_write(rte->addr, IOSAPIC_RTE_LOW(rte->rte_index), low32);

		/* Remove the rte entry from the list */
		list_del(&rte->rte_list);
		iosapic_intr_info[vector].count--;
		iosapic_free_rte(rte);
		index = find_iosapic(gsi);
		iosapic_lists[index].rtes_inuse--;
		WARN_ON(iosapic_lists[index].rtes_inuse < 0);

		trigger	 = iosapic_intr_info[vector].trigger;
		polarity = iosapic_intr_info[vector].polarity;
		dest     = iosapic_intr_info[vector].dest;
		printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n",
		       gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
		       (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
		       cpu_logical_id(dest), dest, vector);

		if (list_empty(&iosapic_intr_info[vector].rtes)) {
			/* Sanity check */
			BUG_ON(iosapic_intr_info[vector].count);

			/* Clear the interrupt controller descriptor */
			idesc->handler = &no_irq_type;

			/* Clear the interrupt information */
			memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info));
			iosapic_intr_info[vector].low32 |= IOSAPIC_MASK;
			INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);

			if (idesc->action) {
				printk(KERN_ERR "interrupt handlers still exist on IRQ %u\n", irq);
				WARN_ON(1);
			}

			/* Free the interrupt vector */
			free_irq_vector(vector);
		}
	}
 out:
	spin_unlock(&iosapic_lock);
	spin_unlock_irqrestore(&idesc->lock, flags);
}

/*
 * ACPI calls this when it finds an entry for a platform interrupt.
 * Note that the irq_base and IOSAPIC address must be set in iosapic_init().
 */
int __init
iosapic_register_platform_intr (u32 int_type, unsigned int gsi,
				int iosapic_vector, u16 eid, u16 id,
				unsigned long polarity, unsigned long trigger)
{
	static const char * const name[] = {"unknown", "PMI", "INIT", "CPEI"};
	unsigned char delivery;
	int vector, mask = 0;
	unsigned int dest = ((id << 8) | eid) & 0xffff;

	switch (int_type) {
	      case ACPI_INTERRUPT_PMI:
		vector = iosapic_vector;
		/*
		 * since PMI vector is alloc'd by FW(ACPI) not by kernel,
		 * we need to make sure the vector is available
		 */
		iosapic_reassign_vector(vector);
		delivery = IOSAPIC_PMI;
		break;
	      case ACPI_INTERRUPT_INIT:
		vector = assign_irq_vector(AUTO_ASSIGN);
		if (vector < 0)
			panic("%s: out of interrupt vectors!\n", __FUNCTION__);
		delivery = IOSAPIC_INIT;
		break;
	      case ACPI_INTERRUPT_CPEI:
		vector = IA64_CPE_VECTOR;
		delivery = IOSAPIC_LOWEST_PRIORITY;
		mask = 1;
		break;
	      default:
		printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type 0x%x\n", int_type);
		return -1;
	}

	register_intr(gsi, vector, delivery, polarity, trigger);

	printk(KERN_INFO "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n",
	       int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown",
	       int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"),
	       (polarity == IOSAPIC_POL_HIGH ? "high" : "low"),
	       cpu_logical_id(dest), dest, vector);

	set_rte(gsi, vector, dest, mask);
	return vector;
}


/*
 * ACPI calls this when it finds an entry for a legacy ISA IRQ override.
 * Note that the gsi_base and IOSAPIC address must be set in iosapic_init().
 */
void __init
iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi,
			  unsigned long polarity,
			  unsigned long trigger)
{
	int vector;
	unsigned int dest = cpu_physical_id(smp_processor_id());

	vector = isa_irq_to_vector(isa_irq);

	register_intr(gsi, vector, IOSAPIC_LOWEST_PRIORITY, polarity, trigger);

	DBG("ISA: IRQ %u -> GSI %u (%s,%s) -> CPU %d (0x%04x) vector %d\n",
	    isa_irq, gsi, trigger == IOSAPIC_EDGE ? "edge" : "level",
	    polarity == IOSAPIC_POL_HIGH ? "high" : "low",
	    cpu_logical_id(dest), dest, vector);

	set_rte(gsi, vector, dest, 1);
}

void __init
iosapic_system_init (int system_pcat_compat)
{
	int vector;

	for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) {
		iosapic_intr_info[vector].low32 = IOSAPIC_MASK;
		INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes);	/* mark as unused */
	}

	pcat_compat = system_pcat_compat;
	if (pcat_compat) {
		/*
		 * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support
		 * enabled.
		 */
		printk(KERN_INFO "%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__);
		outb(0xff, 0xA1);
		outb(0xff, 0x21);
	}
}

static inline int
iosapic_alloc (void)
{
	int index;

	for (index = 0; index < NR_IOSAPICS; index++)
		if (!iosapic_lists[index].addr)
			return index;

	printk(KERN_WARNING "%s: failed to allocate iosapic\n", __FUNCTION__);
	return -1;
}

static inline void
iosapic_free (int index)
{
	memset(&iosapic_lists[index], 0, sizeof(iosapic_lists[0]));
}

static inline int
iosapic_check_gsi_range (unsigned int gsi_base, unsigned int ver)
{
	int index;
	unsigned int gsi_end, base, end;

	/* check gsi range */
	gsi_end = gsi_base + ((ver >> 16) & 0xff);
	for (index = 0; index < NR_IOSAPICS; index++) {
		if (!iosapic_lists[index].addr)
			continue;

		base = iosapic_lists[index].gsi_base;
		end  = base + iosapic_lists[index].num_rte - 1;

		if (gsi_base < base && gsi_end < base)
			continue;/* OK */

		if (gsi_base > end && gsi_end > end)
			continue; /* OK */

		return -EBUSY;
	}
	return 0;
}

int __devinit
iosapic_init (unsigned long phys_addr, unsigned int gsi_base)
{
	int num_rte, err, index;
	unsigned int isa_irq, ver;
	char __iomem *addr;
	unsigned long flags;

	spin_lock_irqsave(&iosapic_lock, flags);
	{
		addr = ioremap(phys_addr, 0);
		ver = iosapic_version(addr);

		if ((err = iosapic_check_gsi_range(gsi_base, ver))) {
			iounmap(addr);
			spin_unlock_irqrestore(&iosapic_lock, flags);
			return err;
		}

		/*
		 * The MAX_REDIR register holds the highest input pin
		 * number (starting from 0).
		 * We add 1 so that we can use it for number of pins (= RTEs)
		 */
		num_rte = ((ver >> 16) & 0xff) + 1;

		index = iosapic_alloc();
		iosapic_lists[index].addr = addr;
		iosapic_lists[index].gsi_base = gsi_base;
		iosapic_lists[index].num_rte = num_rte;
#ifdef CONFIG_NUMA
		iosapic_lists[index].node = MAX_NUMNODES;
#endif
	}
	spin_unlock_irqrestore(&iosapic_lock, flags);

	if ((gsi_base == 0) && pcat_compat) {
		/*
		 * Map the legacy ISA devices into the IOSAPIC data.  Some of these may
		 * get reprogrammed later on with data from the ACPI Interrupt Source
		 * Override table.
		 */
		for (isa_irq = 0; isa_irq < 16; ++isa_irq)
			iosapic_override_isa_irq(isa_irq, isa_irq, IOSAPIC_POL_HIGH, IOSAPIC_EDGE);
	}
	return 0;
}

#ifdef CONFIG_HOTPLUG
int
iosapic_remove (unsigned int gsi_base)
{
	int index, err = 0;
	unsigned long flags;

	spin_lock_irqsave(&iosapic_lock, flags);
	{
		index = find_iosapic(gsi_base);
		if (index < 0) {
			printk(KERN_WARNING "%s: No IOSAPIC for GSI base %u\n",
			       __FUNCTION__, gsi_base);
			goto out;
		}

		if (iosapic_lists[index].rtes_inuse) {
			err = -EBUSY;
			printk(KERN_WARNING "%s: IOSAPIC for GSI base %u is busy\n",
			       __FUNCTION__, gsi_base);
			goto out;
		}

		iounmap(iosapic_lists[index].addr);
		iosapic_free(index);
	}
 out:
	spin_unlock_irqrestore(&iosapic_lock, flags);
	return err;
}
#endif /* CONFIG_HOTPLUG */

#ifdef CONFIG_NUMA
void __devinit
map_iosapic_to_node(unsigned int gsi_base, int node)
{
	int index;

	index = find_iosapic(gsi_base);
	if (index < 0) {
		printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n",
		       __FUNCTION__, gsi_base);
		return;
	}
	iosapic_lists[index].node = node;
	return;
}
#endif

#ifndef XEN
static int __init iosapic_enable_kmalloc (void)
{
	iosapic_kmalloc_ok = 1;
	return 0;
}
core_initcall (iosapic_enable_kmalloc);
#endif

#ifdef XEN
/* nop for now */
void set_irq_affinity_info(unsigned int irq, int hwid, int redir) {}

static int iosapic_physbase_to_id(unsigned long physbase)
{
	int i;
	unsigned long addr = physbase | __IA64_UNCACHED_OFFSET;

	for (i = 0; i < NR_IOSAPICS; i++) {
	    if ((unsigned long)(iosapic_lists[i].addr) == addr)
		return i;
	}

	return -1;
}

int iosapic_guest_read(unsigned long physbase, unsigned int reg, u32 *pval)
{
	int id;
	unsigned long flags;

	if ((id = (iosapic_physbase_to_id(physbase))) < 0)
	    return id;

	spin_lock_irqsave(&iosapic_lock, flags);
	*pval = iosapic_read(iosapic_lists[id].addr, reg);
	spin_unlock_irqrestore(&iosapic_lock, flags);

	return 0;
}

int iosapic_guest_write(unsigned long physbase, unsigned int reg, u32 val)
{
	unsigned int id, gsi, vec, xen_vec, dest, high32;
	char rte_index;
	struct iosapic *ios;
	struct iosapic_intr_info *info;
	struct rte_entry rte;
	unsigned long flags;

	if ((id = (iosapic_physbase_to_id(physbase))) < 0)
	    return -EINVAL;
	ios = &iosapic_lists[id];

	/* Only handle first half of RTE update */
	if ((reg < 0x10) || (reg & 1))
	    return 0;

	rte.val = val;
	rte_index = IOSAPIC_RTEINDEX(reg);
	vec = rte.lo.vector;
#if 0
	/* Take PMI/NMI/INIT/EXTINT handled by xen */ 
	if (rte.delivery_mode > IOSAPIC_LOWEST_PRIORITY) {
	    printk("Attempt to write IOSAPIC dest mode owned by xen!\n");
	    printk("IOSAPIC/PIN = (%d/%d), lo = 0x%x\n",
		id, rte_index, val);
	    return -EINVAL;
	}
#endif

	/* Sanity check. Vector should be allocated before this update */
	if ((rte_index > ios->num_rte) ||
	    ((vec > IA64_FIRST_DEVICE_VECTOR) &&
	     (vec < IA64_LAST_DEVICE_VECTOR) &&
	     (!test_bit(vec - IA64_FIRST_DEVICE_VECTOR, ia64_vector_mask))))
	    return -EINVAL;

	gsi = ios->gsi_base + rte_index;
	xen_vec = gsi_to_vector(gsi);
	if (xen_vec >= 0 && test_bit(xen_vec, ia64_xen_vector)) {
		printk("WARN: GSI %d in use by Xen.\n", gsi);
		return -EINVAL;
	}
	info = &iosapic_intr_info[vec];
	spin_lock_irqsave(&irq_descp(vec)->lock, flags);
	spin_lock(&iosapic_lock);
	if (!gsi_vector_to_rte(gsi, vec)) {
	    register_intr(gsi, vec, IOSAPIC_LOWEST_PRIORITY,
		rte.lo.polarity, rte.lo.trigger);
	} else if (vector_is_shared(vec)) {
	    if ((info->trigger != rte.lo.trigger) ||
		(info->polarity != rte.lo.polarity)) {
		printk("WARN: can't override shared interrupt vec\n");
	        printk("IOSAPIC/PIN = (%d/%d), ori = 0x%x, new = 0x%x\n",
			id, rte_index, info->low32, rte.val);
		spin_unlock(&iosapic_lock);
		spin_unlock_irqrestore(&irq_descp(vec)->lock, flags);
		return -EINVAL;
	    }

	    /* If the vector is shared and already unmasked for other
	     * interrupt sources, don't mask it.
	     *
	     * Same check may also apply to single gsi pin, which may
	     * be shared by devices belonging to different domain. But
	     * let's see how to act later on demand.
	     */
	    if (!(info->low32 & IOSAPIC_MASK))
		rte.lo.mask = 0;
	}

	/* time to update physical RTE */
	dest = cpu_physical_id(smp_processor_id());
	high32 = (dest << IOSAPIC_DEST_SHIFT);
	iosapic_write(iosapic_lists[id].addr, reg + 1, high32);
	iosapic_write(iosapic_lists[id].addr, reg, rte.val);
	info->low32 = rte.val;
	info->dest = dest;
	spin_unlock(&iosapic_lock);
	spin_unlock_irqrestore(&irq_descp(vec)->lock, flags);
	return 0;
}
#endif /* XEN */