diff options
author | Suresh Siddha <suresh.b.siddha@intel.com> | 2009-08-04 15:07:08 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2009-08-27 17:33:20 -0400 |
commit | 20f3097bfe5fb5ced0b14f9ea2620c4039bf1dde (patch) | |
tree | 437d792e28f9c64198d64730196abf0a3bb1e7c6 /drivers | |
parent | 5946fa3d5cdeb846a647a1900026af9f8b08c8b5 (diff) |
intr-remap: generic support for remapping HPET MSIs
Generic support for remapping HPET MSI's by parsing the HPET timer block
device scope in the ACPI DRHD tables. This is needed for platforms
supporting interrupt-remapping and MSI capable HPET timer block.
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Jesse Barnes <jbarnes@virtuousgeek.org>
Cc: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Cc: Jay Fenlason <fenlason@redhat.com>
LKML-Reference: <20090804190729.477649000@intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/intr_remapping.c | 89 | ||||
-rw-r--r-- | drivers/pci/intr_remapping.h | 7 |
2 files changed, 92 insertions, 4 deletions
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 4f5b8712931f..2cc3f70ad425 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <linux/dmar.h> | 2 | #include <linux/dmar.h> |
3 | #include <linux/spinlock.h> | 3 | #include <linux/spinlock.h> |
4 | #include <linux/jiffies.h> | 4 | #include <linux/jiffies.h> |
5 | #include <linux/hpet.h> | ||
5 | #include <linux/pci.h> | 6 | #include <linux/pci.h> |
6 | #include <linux/irq.h> | 7 | #include <linux/irq.h> |
7 | #include <asm/io_apic.h> | 8 | #include <asm/io_apic.h> |
@@ -14,7 +15,8 @@ | |||
14 | #include "pci.h" | 15 | #include "pci.h" |
15 | 16 | ||
16 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; | 17 | static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; |
17 | static int ir_ioapic_num; | 18 | static struct hpet_scope ir_hpet[MAX_HPET_TBS]; |
19 | static int ir_ioapic_num, ir_hpet_num; | ||
18 | int intr_remapping_enabled; | 20 | int intr_remapping_enabled; |
19 | 21 | ||
20 | static int disable_intremap; | 22 | static int disable_intremap; |
@@ -351,6 +353,16 @@ int flush_irte(int irq) | |||
351 | return rc; | 353 | return rc; |
352 | } | 354 | } |
353 | 355 | ||
356 | struct intel_iommu *map_hpet_to_ir(u8 hpet_id) | ||
357 | { | ||
358 | int i; | ||
359 | |||
360 | for (i = 0; i < MAX_HPET_TBS; i++) | ||
361 | if (ir_hpet[i].id == hpet_id) | ||
362 | return ir_hpet[i].iommu; | ||
363 | return NULL; | ||
364 | } | ||
365 | |||
354 | struct intel_iommu *map_ioapic_to_ir(int apic) | 366 | struct intel_iommu *map_ioapic_to_ir(int apic) |
355 | { | 367 | { |
356 | int i; | 368 | int i; |
@@ -478,6 +490,36 @@ int set_ioapic_sid(struct irte *irte, int apic) | |||
478 | return 0; | 490 | return 0; |
479 | } | 491 | } |
480 | 492 | ||
493 | int set_hpet_sid(struct irte *irte, u8 id) | ||
494 | { | ||
495 | int i; | ||
496 | u16 sid = 0; | ||
497 | |||
498 | if (!irte) | ||
499 | return -1; | ||
500 | |||
501 | for (i = 0; i < MAX_HPET_TBS; i++) { | ||
502 | if (ir_hpet[i].id == id) { | ||
503 | sid = (ir_hpet[i].bus << 8) | ir_hpet[i].devfn; | ||
504 | break; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | if (sid == 0) { | ||
509 | pr_warning("Failed to set source-id of HPET block (%d)\n", id); | ||
510 | return -1; | ||
511 | } | ||
512 | |||
513 | /* | ||
514 | * Should really use SQ_ALL_16. Some platforms are broken. | ||
515 | * While we figure out the right quirks for these broken platforms, use | ||
516 | * SQ_13_IGNORE_3 for now. | ||
517 | */ | ||
518 | set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_13_IGNORE_3, sid); | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
481 | int set_msi_sid(struct irte *irte, struct pci_dev *dev) | 523 | int set_msi_sid(struct irte *irte, struct pci_dev *dev) |
482 | { | 524 | { |
483 | struct pci_dev *bridge; | 525 | struct pci_dev *bridge; |
@@ -711,6 +753,34 @@ error: | |||
711 | return -1; | 753 | return -1; |
712 | } | 754 | } |
713 | 755 | ||
756 | static void ir_parse_one_hpet_scope(struct acpi_dmar_device_scope *scope, | ||
757 | struct intel_iommu *iommu) | ||
758 | { | ||
759 | struct acpi_dmar_pci_path *path; | ||
760 | u8 bus; | ||
761 | int count; | ||
762 | |||
763 | bus = scope->bus; | ||
764 | path = (struct acpi_dmar_pci_path *)(scope + 1); | ||
765 | count = (scope->length - sizeof(struct acpi_dmar_device_scope)) | ||
766 | / sizeof(struct acpi_dmar_pci_path); | ||
767 | |||
768 | while (--count > 0) { | ||
769 | /* | ||
770 | * Access PCI directly due to the PCI | ||
771 | * subsystem isn't initialized yet. | ||
772 | */ | ||
773 | bus = read_pci_config_byte(bus, path->dev, path->fn, | ||
774 | PCI_SECONDARY_BUS); | ||
775 | path++; | ||
776 | } | ||
777 | ir_hpet[ir_hpet_num].bus = bus; | ||
778 | ir_hpet[ir_hpet_num].devfn = PCI_DEVFN(path->dev, path->fn); | ||
779 | ir_hpet[ir_hpet_num].iommu = iommu; | ||
780 | ir_hpet[ir_hpet_num].id = scope->enumeration_id; | ||
781 | ir_hpet_num++; | ||
782 | } | ||
783 | |||
714 | static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope, | 784 | static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope, |
715 | struct intel_iommu *iommu) | 785 | struct intel_iommu *iommu) |
716 | { | 786 | { |
@@ -740,8 +810,8 @@ static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope, | |||
740 | ir_ioapic_num++; | 810 | ir_ioapic_num++; |
741 | } | 811 | } |
742 | 812 | ||
743 | static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, | 813 | static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header, |
744 | struct intel_iommu *iommu) | 814 | struct intel_iommu *iommu) |
745 | { | 815 | { |
746 | struct acpi_dmar_hardware_unit *drhd; | 816 | struct acpi_dmar_hardware_unit *drhd; |
747 | struct acpi_dmar_device_scope *scope; | 817 | struct acpi_dmar_device_scope *scope; |
@@ -765,6 +835,17 @@ static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, | |||
765 | drhd->address); | 835 | drhd->address); |
766 | 836 | ||
767 | ir_parse_one_ioapic_scope(scope, iommu); | 837 | ir_parse_one_ioapic_scope(scope, iommu); |
838 | } else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) { | ||
839 | if (ir_hpet_num == MAX_HPET_TBS) { | ||
840 | printk(KERN_WARNING "Exceeded Max HPET blocks\n"); | ||
841 | return -1; | ||
842 | } | ||
843 | |||
844 | printk(KERN_INFO "HPET id %d under DRHD base" | ||
845 | " 0x%Lx\n", scope->enumeration_id, | ||
846 | drhd->address); | ||
847 | |||
848 | ir_parse_one_hpet_scope(scope, iommu); | ||
768 | } | 849 | } |
769 | start += scope->length; | 850 | start += scope->length; |
770 | } | 851 | } |
@@ -785,7 +866,7 @@ int __init parse_ioapics_under_ir(void) | |||
785 | struct intel_iommu *iommu = drhd->iommu; | 866 | struct intel_iommu *iommu = drhd->iommu; |
786 | 867 | ||
787 | if (ecap_ir_support(iommu->ecap)) { | 868 | if (ecap_ir_support(iommu->ecap)) { |
788 | if (ir_parse_ioapic_scope(drhd->hdr, iommu)) | 869 | if (ir_parse_ioapic_hpet_scope(drhd->hdr, iommu)) |
789 | return -1; | 870 | return -1; |
790 | 871 | ||
791 | ir_supported = 1; | 872 | ir_supported = 1; |
diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h index 63a263c18415..5662fecfee60 100644 --- a/drivers/pci/intr_remapping.h +++ b/drivers/pci/intr_remapping.h | |||
@@ -7,4 +7,11 @@ struct ioapic_scope { | |||
7 | unsigned int devfn; /* PCI devfn number */ | 7 | unsigned int devfn; /* PCI devfn number */ |
8 | }; | 8 | }; |
9 | 9 | ||
10 | struct hpet_scope { | ||
11 | struct intel_iommu *iommu; | ||
12 | u8 id; | ||
13 | unsigned int bus; | ||
14 | unsigned int devfn; | ||
15 | }; | ||
16 | |||
10 | #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) | 17 | #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) |