summaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorTomasz Nowicki <tn@semihalf.com>2016-09-12 14:54:20 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2016-09-12 15:32:30 -0400
commit88ef16d888a094587b2ac77de60927df5da5d56d (patch)
treea15c57a32a81fd41efa2b0be1ea9492035dcc74f /drivers/acpi
parent89c59cca48f04ede7e63944418af1794a8e31da6 (diff)
ACPI: I/O Remapping Table (IORT) initial support
IORT shows representation of IO topology for ARM based systems. It describes how various components are connected together on parent-child basis e.g. PCI RC -> SMMU -> ITS. Also see IORT spec. http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf Initial support allows to detect IORT table presence and save its root pointer obtained through acpi_get_table(). The pointer validity depends on acpi_gbl_permanent_mmap because if acpi_gbl_permanent_mmap is not set while using IORT nodes we would dereference unmapped pointers. For the aforementioned reason call acpi_iort_init() from acpi_init() which guarantees acpi_gbl_permanent_mmap to be set at that point. Add generic helpers which are helpful for scanning and retrieving information from IORT table content. List of the most important helpers: - iort_find_dev_node() finds IORT node for a given device - iort_node_map_rid() maps device RID and returns IORT node which provides final translation IORT support is placed under drivers/acpi/arm64/ new directory due to its ARM64 specific nature. The code there is considered only for ARM64. The long term plan is to keep all ARM64 specific tables support in this place e.g. GTDT table. Signed-off-by: Tomasz Nowicki <tn@semihalf.com> Acked-by: Rafael J. Wysocki <rjw@rjwysocki.net> Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org> Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/Kconfig4
-rw-r--r--drivers/acpi/Makefile2
-rw-r--r--drivers/acpi/arm64/Kconfig6
-rw-r--r--drivers/acpi/arm64/Makefile1
-rw-r--r--drivers/acpi/arm64/iort.c216
-rw-r--r--drivers/acpi/bus.c2
6 files changed, 231 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 445ce28475b3..d5c06145d07f 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -521,4 +521,8 @@ config ACPI_CONFIGFS
521 userspace. The configurable ACPI groups will be visible under 521 userspace. The configurable ACPI groups will be visible under
522 /config/acpi, assuming configfs is mounted under /config. 522 /config/acpi, assuming configfs is mounted under /config.
523 523
524if ARM64
525source "drivers/acpi/arm64/Kconfig"
526endif
527
524endif # ACPI 528endif # ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 5ae9d85c5159..e5ada7895697 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -105,3 +105,5 @@ obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o
105 105
106video-objs += acpi_video.o video_detect.o 106video-objs += acpi_video.o video_detect.o
107obj-y += dptf/ 107obj-y += dptf/
108
109obj-$(CONFIG_ARM64) += arm64/
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
new file mode 100644
index 000000000000..4616da4c15be
--- /dev/null
+++ b/drivers/acpi/arm64/Kconfig
@@ -0,0 +1,6 @@
1#
2# ACPI Configuration for ARM64
3#
4
5config ACPI_IORT
6 bool
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
new file mode 100644
index 000000000000..72331f2ce0e9
--- /dev/null
+++ b/drivers/acpi/arm64/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_ACPI_IORT) += iort.o
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
new file mode 100644
index 000000000000..5279a358924a
--- /dev/null
+++ b/drivers/acpi/arm64/iort.c
@@ -0,0 +1,216 @@
1/*
2 * Copyright (C) 2016, Semihalf
3 * Author: Tomasz Nowicki <tn@semihalf.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * This file implements early detection/parsing of I/O mapping
15 * reported to OS through firmware via I/O Remapping Table (IORT)
16 * IORT document number: ARM DEN 0049A
17 */
18
19#define pr_fmt(fmt) "ACPI: IORT: " fmt
20
21#include <linux/acpi_iort.h>
22#include <linux/kernel.h>
23#include <linux/pci.h>
24
25typedef acpi_status (*iort_find_node_callback)
26 (struct acpi_iort_node *node, void *context);
27
28/* Root pointer to the mapped IORT table */
29static struct acpi_table_header *iort_table;
30
31static LIST_HEAD(iort_msi_chip_list);
32static DEFINE_SPINLOCK(iort_msi_chip_lock);
33
34static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
35 iort_find_node_callback callback,
36 void *context)
37{
38 struct acpi_iort_node *iort_node, *iort_end;
39 struct acpi_table_iort *iort;
40 int i;
41
42 if (!iort_table)
43 return NULL;
44
45 /* Get the first IORT node */
46 iort = (struct acpi_table_iort *)iort_table;
47 iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
48 iort->node_offset);
49 iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
50 iort_table->length);
51
52 for (i = 0; i < iort->node_count; i++) {
53 if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
54 "IORT node pointer overflows, bad table!\n"))
55 return NULL;
56
57 if (iort_node->type == type &&
58 ACPI_SUCCESS(callback(iort_node, context)))
59 return iort_node;
60
61 iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
62 iort_node->length);
63 }
64
65 return NULL;
66}
67
68static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
69 void *context)
70{
71 struct device *dev = context;
72 acpi_status status;
73
74 if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
75 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
76 struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
77 struct acpi_iort_named_component *ncomp;
78
79 if (!adev) {
80 status = AE_NOT_FOUND;
81 goto out;
82 }
83
84 status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
85 if (ACPI_FAILURE(status)) {
86 dev_warn(dev, "Can't get device full path name\n");
87 goto out;
88 }
89
90 ncomp = (struct acpi_iort_named_component *)node->node_data;
91 status = !strcmp(ncomp->device_name, buf.pointer) ?
92 AE_OK : AE_NOT_FOUND;
93 acpi_os_free(buf.pointer);
94 } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
95 struct acpi_iort_root_complex *pci_rc;
96 struct pci_bus *bus;
97
98 bus = to_pci_bus(dev);
99 pci_rc = (struct acpi_iort_root_complex *)node->node_data;
100
101 /*
102 * It is assumed that PCI segment numbers maps one-to-one
103 * with root complexes. Each segment number can represent only
104 * one root complex.
105 */
106 status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
107 AE_OK : AE_NOT_FOUND;
108 } else {
109 status = AE_NOT_FOUND;
110 }
111out:
112 return status;
113}
114
115static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
116 u32 *rid_out)
117{
118 /* Single mapping does not care for input id */
119 if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
120 if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
121 type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
122 *rid_out = map->output_base;
123 return 0;
124 }
125
126 pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n",
127 map, type);
128 return -ENXIO;
129 }
130
131 if (rid_in < map->input_base ||
132 (rid_in >= map->input_base + map->id_count))
133 return -ENXIO;
134
135 *rid_out = map->output_base + (rid_in - map->input_base);
136 return 0;
137}
138
139static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
140 u32 rid_in, u32 *rid_out,
141 u8 type)
142{
143 u32 rid = rid_in;
144
145 /* Parse the ID mapping tree to find specified node type */
146 while (node) {
147 struct acpi_iort_id_mapping *map;
148 int i;
149
150 if (node->type == type) {
151 if (rid_out)
152 *rid_out = rid;
153 return node;
154 }
155
156 if (!node->mapping_offset || !node->mapping_count)
157 goto fail_map;
158
159 map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
160 node->mapping_offset);
161
162 /* Firmware bug! */
163 if (!map->output_reference) {
164 pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
165 node, node->type);
166 goto fail_map;
167 }
168
169 /* Do the RID translation */
170 for (i = 0; i < node->mapping_count; i++, map++) {
171 if (!iort_id_map(map, node->type, rid, &rid))
172 break;
173 }
174
175 if (i == node->mapping_count)
176 goto fail_map;
177
178 node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
179 map->output_reference);
180 }
181
182fail_map:
183 /* Map input RID to output RID unchanged on mapping failure*/
184 if (rid_out)
185 *rid_out = rid_in;
186
187 return NULL;
188}
189
190static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
191{
192 struct pci_bus *pbus;
193
194 if (!dev_is_pci(dev))
195 return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
196 iort_match_node_callback, dev);
197
198 /* Find a PCI root bus */
199 pbus = to_pci_dev(dev)->bus;
200 while (!pci_is_root_bus(pbus))
201 pbus = pbus->parent;
202
203 return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
204 iort_match_node_callback, &pbus->dev);
205}
206
207void __init acpi_iort_init(void)
208{
209 acpi_status status;
210
211 status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
212 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
213 const char *msg = acpi_format_exception(status);
214 pr_err("Failed to get table, %s\n", msg);
215 }
216}
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 85b7d07fe5c8..e56e6438515a 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -36,6 +36,7 @@
36#ifdef CONFIG_X86 36#ifdef CONFIG_X86
37#include <asm/mpspec.h> 37#include <asm/mpspec.h>
38#endif 38#endif
39#include <linux/acpi_iort.h>
39#include <linux/pci.h> 40#include <linux/pci.h>
40#include <acpi/apei.h> 41#include <acpi/apei.h>
41#include <linux/dmi.h> 42#include <linux/dmi.h>
@@ -1186,6 +1187,7 @@ static int __init acpi_init(void)
1186 } 1187 }
1187 1188
1188 pci_mmcfg_late_init(); 1189 pci_mmcfg_late_init();
1190 acpi_iort_init();
1189 acpi_scan_init(); 1191 acpi_scan_init();
1190 acpi_ec_init(); 1192 acpi_ec_init();
1191 acpi_debugfs_init(); 1193 acpi_debugfs_init();