aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/numa.txt272
-rw-r--r--Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt34
-rw-r--r--Documentation/kernel-parameters.txt3
-rw-r--r--Documentation/watchdog/watchdog-kernel-api.txt61
-rw-r--r--Documentation/watchdog/watchdog-parameters.txt6
-rw-r--r--arch/alpha/kernel/pci.c7
-rw-r--r--arch/alpha/kernel/sys_nautilus.c1
-rw-r--r--arch/arm/kernel/bios32.c9
-rw-r--r--arch/arm64/Kconfig45
-rw-r--r--arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi8
-rw-r--r--arch/arm64/boot/dts/arm/foundation-v8.dts7
-rw-r--r--arch/arm64/boot/dts/cavium/Makefile2
-rw-r--r--arch/arm64/boot/dts/cavium/thunder-88xx-2n.dts83
-rw-r--r--arch/arm64/boot/dts/cavium/thunder-88xx-2n.dtsi806
-rw-r--r--arch/arm64/include/asm/acpi.h15
-rw-r--r--arch/arm64/include/asm/mmzone.h18
-rw-r--r--arch/arm64/include/asm/numa.h53
-rw-r--r--arch/arm64/include/asm/topology.h7
-rw-r--r--arch/arm64/kernel/Makefile2
-rw-r--r--arch/arm64/kernel/acpi_numa.c204
-rw-r--r--arch/arm64/kernel/of_numa.c257
-rw-r--r--arch/arm64/kernel/pci.c26
-rw-r--r--arch/arm64/kernel/setup.c4
-rw-r--r--arch/arm64/kernel/smp.c7
-rw-r--r--arch/arm64/mm/Makefile1
-rw-r--r--arch/arm64/mm/init.c31
-rw-r--r--arch/arm64/mm/mmu.c1
-rw-r--r--arch/arm64/mm/numa.c394
-rw-r--r--arch/cris/arch-v32/drivers/pci/bios.c12
-rw-r--r--arch/frv/mb93090-mb00/pci-frv.h1
-rw-r--r--arch/frv/mb93090-mb00/pci-irq.c24
-rw-r--r--arch/frv/mb93090-mb00/pci-vdk.c1
-rw-r--r--arch/ia64/Kconfig1
-rw-r--r--arch/ia64/hp/common/sba_iommu.c2
-rw-r--r--arch/ia64/include/asm/pci.h1
-rw-r--r--arch/ia64/include/asm/topology.h4
-rw-r--r--arch/ia64/pci/pci.c52
-rw-r--r--arch/ia64/sn/kernel/io_acpi_init.c4
-rw-r--r--arch/m68k/coldfire/pci.c8
-rw-r--r--arch/metag/include/asm/topology.h3
-rw-r--r--arch/microblaze/pci/pci-common.c9
-rw-r--r--arch/mips/pci/pci.c9
-rw-r--r--arch/mn10300/unit-asb2305/pci-asb2305.h5
-rw-r--r--arch/mn10300/unit-asb2305/pci-irq.c24
-rw-r--r--arch/mn10300/unit-asb2305/pci.c19
-rw-r--r--arch/powerpc/include/asm/topology.h4
-rw-r--r--arch/powerpc/kernel/pci-common.c25
-rw-r--r--arch/s390/include/asm/pci.h2
-rw-r--r--arch/s390/include/asm/topology.h1
-rw-r--r--arch/sh/drivers/pci/fixups-cayman.c2
-rw-r--r--arch/sh/drivers/pci/fixups-dreamcast.c2
-rw-r--r--arch/sh/drivers/pci/fixups-r7780rp.c2
-rw-r--r--arch/sh/drivers/pci/fixups-rts7751r2d.c6
-rw-r--r--arch/sh/drivers/pci/fixups-sdk7780.c4
-rw-r--r--arch/sh/drivers/pci/fixups-se7751.c2
-rw-r--r--arch/sh/drivers/pci/fixups-sh03.c2
-rw-r--r--arch/sh/drivers/pci/fixups-snapgear.c2
-rw-r--r--arch/sh/drivers/pci/fixups-titan.c4
-rw-r--r--arch/sh/drivers/pci/pci.c10
-rw-r--r--arch/sh/drivers/pci/pcie-sh7786.c2
-rw-r--r--arch/sh/include/asm/topology.h3
-rw-r--r--arch/sparc/kernel/leon_pci.c12
-rw-r--r--arch/sparc/kernel/pci.c23
-rw-r--r--arch/tile/kernel/pci.c10
-rw-r--r--arch/tile/kernel/pci_gx.c10
-rw-r--r--arch/unicore32/kernel/pci.c11
-rw-r--r--arch/x86/Kconfig5
-rw-r--r--arch/x86/include/asm/pci.h5
-rw-r--r--arch/x86/include/asm/pci_x86.h35
-rw-r--r--arch/x86/include/asm/topology.h1
-rw-r--r--arch/x86/kernel/x86_init.c1
-rw-r--r--arch/x86/mm/srat.c29
-rw-r--r--arch/x86/pci/acpi.c56
-rw-r--r--arch/x86/pci/common.c10
-rw-r--r--arch/x86/pci/irq.c81
-rw-r--r--arch/x86/pci/mmconfig-shared.c250
-rw-r--r--arch/x86/pci/mmconfig_32.c11
-rw-r--r--arch/x86/pci/mmconfig_64.c67
-rw-r--r--arch/x86/pci/numachip.c1
-rw-r--r--drivers/acpi/Kconfig26
-rw-r--r--drivers/acpi/Makefile5
-rw-r--r--drivers/acpi/acpi_amba.c122
-rw-r--r--drivers/acpi/acpi_platform.c19
-rw-r--r--drivers/acpi/apei/Kconfig3
-rw-r--r--drivers/acpi/apei/Makefile2
-rw-r--r--drivers/acpi/apei/apei-internal.h2
-rw-r--r--drivers/acpi/apei/bert.c150
-rw-r--r--drivers/acpi/apei/hest.c19
-rw-r--r--drivers/acpi/bus.c33
-rw-r--r--drivers/acpi/gtdt.c367
-rw-r--r--drivers/acpi/internal.h6
-rw-r--r--drivers/acpi/iort.c326
-rw-r--r--drivers/acpi/mcfg.c201
-rw-r--r--drivers/acpi/numa.c144
-rw-r--r--drivers/acpi/pci_root.c138
-rw-r--r--drivers/acpi/resource.c12
-rw-r--r--drivers/acpi/scan.c4
-rw-r--r--drivers/acpi/spcr.c85
-rw-r--r--drivers/clocksource/arm_arch_timer.c176
-rw-r--r--drivers/irqchip/Kconfig1
-rw-r--r--drivers/irqchip/irq-gic-v2m.c160
-rw-r--r--drivers/irqchip/irq-gic-v3-its-pci-msi.c85
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c143
-rw-r--r--drivers/irqchip/irq-gic-v3.c348
-rw-r--r--drivers/irqchip/irq-gic.c8
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c10
-rw-r--r--drivers/of/of_pci_irq.c2
-rw-r--r--drivers/pci/Kconfig10
-rw-r--r--drivers/pci/Makefile15
-rw-r--r--drivers/pci/ecam.c234
-rw-r--r--drivers/pci/host/pci-host-generic.c14
-rw-r--r--drivers/pci/host/pci-versatile.c7
-rw-r--r--drivers/pci/host/pcie-iproc.c6
-rw-r--r--drivers/pci/msi.c3
-rw-r--r--drivers/pci/pci-acpi.c42
-rw-r--r--drivers/pci/pci.c31
-rw-r--r--drivers/pci/pci.h1
-rw-r--r--drivers/pci/probe.c7
-rw-r--r--drivers/pci/setup-irq.c34
-rw-r--r--drivers/tty/serial/amba-pl011.c14
-rw-r--r--drivers/watchdog/Kconfig14
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/sbsa_gwdt.c459
-rw-r--r--drivers/watchdog/watchdog_core.c127
-rw-r--r--drivers/watchdog/watchdog_dev.c55
-rw-r--r--drivers/xen/pci.c7
-rw-r--r--include/acpi/acpi_bus.h1
-rw-r--r--include/acpi/actbl2.h4
-rw-r--r--include/asm-generic/topology.h4
-rw-r--r--include/asm-generic/vmlinux.lds.h7
-rw-r--r--include/clocksource/arm_arch_timer.h19
-rw-r--r--include/kvm/arm_vgic.h36
-rw-r--r--include/linux/acpi.h41
-rw-r--r--include/linux/console.h13
-rw-r--r--include/linux/ecam.h62
-rw-r--r--include/linux/iort.h38
-rw-r--r--include/linux/irqchip/arm-gic-v3.h2
-rw-r--r--include/linux/irqchip/arm-gic.h3
-rw-r--r--include/linux/irqdomain.h10
-rw-r--r--include/linux/pci-acpi.h17
-rw-r--r--include/linux/pci.h15
-rw-r--r--include/linux/watchdog.h37
-rw-r--r--kernel/irq/irqdomain.c2
-rw-r--r--kernel/printk/printk.c94
-rw-r--r--virt/kvm/arm/arch_timer.c71
-rw-r--r--virt/kvm/arm/vgic-v2.c65
-rw-r--r--virt/kvm/arm/vgic-v3.c56
-rw-r--r--virt/kvm/arm/vgic.c140
148 files changed, 6598 insertions, 1051 deletions
diff --git a/Documentation/devicetree/bindings/arm/numa.txt b/Documentation/devicetree/bindings/arm/numa.txt
new file mode 100644
index 000000000000..734e5b423056
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/numa.txt
@@ -0,0 +1,272 @@
+==============================================================================
+NUMA binding description.
+==============================================================================
+
+==============================================================================
+1 - Introduction
+==============================================================================
+
+Systems employing a Non Uniform Memory Access (NUMA) architecture contain
+collections of hardware resources including processors, memory, and I/O buses,
+that comprise what is commonly known as a NUMA node.
+Processor accesses to memory within the local NUMA node is generally faster
+than processor accesses to memory outside of the local NUMA node.
+DT defines interfaces that allow the platform to convey NUMA node
+topology information to OS.
+
+==============================================================================
+2 - numa-node-id
+==============================================================================
+
+For the purpose of identification, each NUMA node is associated with a unique
+token known as a node id. For the purpose of this binding
+a node id is a 32-bit integer.
+
+A device node is associated with a NUMA node by the presence of a
+numa-node-id property which contains the node id of the device.
+
+Example:
+ /* numa node 0 */
+ numa-node-id = <0>;
+
+ /* numa node 1 */
+ numa-node-id = <1>;
+
+==============================================================================
+3 - distance-map
+==============================================================================
+
+The device tree node distance-map describes the relative
+distance (memory latency) between all numa nodes.
+
+- compatible : Should at least contain "numa-distance-map-v1".
+
+- distance-matrix
+ This property defines a matrix to describe the relative distances
+ between all numa nodes.
+ It is represented as a list of node pairs and their relative distance.
+
+ Note:
+ 1. Each entry represents distance from first node to second node.
+ The distances are equal in either direction.
+ 2. The distance from a node to self (local distance) is represented
+ with value 10 and all internode distance should be represented with
+ a value greater than 10.
+ 3. distance-matrix should have entries in lexicographical ascending
+ order of nodes.
+ 4. There must be only one device node distance-map which must reside in the root node.
+
+Example:
+ 4 nodes connected in mesh/ring topology as below,
+
+ 0_______20______1
+ | |
+ | |
+ 20 20
+ | |
+ | |
+ |_______________|
+ 3 20 2
+
+ if relative distance for each hop is 20,
+ then internode distance would be,
+ 0 -> 1 = 20
+ 1 -> 2 = 20
+ 2 -> 3 = 20
+ 3 -> 0 = 20
+ 0 -> 2 = 40
+ 1 -> 3 = 40
+
+ and dt presentation for this distance matrix is,
+
+ distance-map {
+ compatible = "numa-distance-map-v1";
+ distance-matrix = <0 0 10>,
+ <0 1 20>,
+ <0 2 40>,
+ <0 3 20>,
+ <1 0 20>,
+ <1 1 10>,
+ <1 2 20>,
+ <1 3 40>,
+ <2 0 40>,
+ <2 1 20>,
+ <2 2 10>,
+ <2 3 20>,
+ <3 0 20>,
+ <3 1 40>,
+ <3 2 20>,
+ <3 3 10>;
+ };
+
+==============================================================================
+4 - Example dts
+==============================================================================
+
+2 sockets system consists of 2 boards connected through ccn bus and
+each board having one socket/soc of 8 cpus, memory and pci bus.
+
+ memory@00c00000 {
+ device_type = "memory";
+ reg = <0x0 0x00c00000 0x0 0x80000000>;
+ /* node 0 */
+ numa-node-id = <0>;
+ };
+
+ memory@10000000000 {
+ device_type = "memory";
+ reg = <0x100 0x00000000 0x0 0x80000000>;
+ /* node 1 */
+ numa-node-id = <1>;
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@000 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x000>;
+ enable-method = "psci";
+ /* node 0 */
+ numa-node-id = <0>;
+ };
+ cpu@001 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x001>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@002 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x002>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@003 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x003>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@004 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x004>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@005 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x005>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@006 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x006>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@007 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x007>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@008 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x008>;
+ enable-method = "psci";
+ /* node 1 */
+ numa-node-id = <1>;
+ };
+ cpu@009 {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x009>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@00a {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x00a>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@00b {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x00b>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@00c {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x00c>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@00d {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x00d>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@00e {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x00e>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@00f {
+ device_type = "cpu";
+ compatible = "arm,armv8";
+ reg = <0x0 0x00f>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ };
+
+ pcie0: pcie0@0x8480,00000000 {
+ compatible = "arm,armv8";
+ device_type = "pci";
+ bus-range = <0 255>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0x8480 0x00000000 0 0x10000000>; /* Configuration space */
+ ranges = <0x03000000 0x8010 0x00000000 0x8010 0x00000000 0x70 0x00000000>;
+ /* node 0 */
+ numa-node-id = <0>;
+ };
+
+ pcie1: pcie1@0x9480,00000000 {
+ compatible = "arm,armv8";
+ device_type = "pci";
+ bus-range = <0 255>;
+ #size-cells = <2>;
+ #address-cells = <3>;
+ reg = <0x9480 0x00000000 0 0x10000000>; /* Configuration space */
+ ranges = <0x03000000 0x9010 0x00000000 0x9010 0x00000000 0x70 0x00000000>;
+ /* node 1 */
+ numa-node-id = <1>;
+ };
+
+ distance-map {
+ compatible = "numa-distance-map-v1";
+ distance-matrix = <0 0 10>,
+ <0 1 20>,
+ <1 1 10>;
+ };
diff --git a/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt b/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt
new file mode 100644
index 000000000000..2da63db29d64
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/sbsa-gwdt.txt
@@ -0,0 +1,34 @@
+* SBSA (Server Base System Architecture) Generic Watchdog
+
+The SBSA Generic Watchdog Timer is used to force a reset of the system
+after two stages of timeout have elapsed. A detailed definition of the
+watchdog timer can be found in the ARM document: ARM-DEN-0029 - Server
+Base System Architecture (SBSA)
+
+Required properties:
+- compatible: Should at least contain "arm,sbsa-gwdt".
+
+- reg: Each entry specifies the base physical address of a register frame
+ and the length of that frame; currently, two frames must be defined,
+ in this order:
+ 1: Watchdog control frame;
+ 2: Refresh frame.
+
+- interrupts: Should contain the Watchdog Signal 0 (WS0) SPI (Shared
+ Peripheral Interrupt) number of SBSA Generic Watchdog.
+
+Optional properties
+- timeout-sec: Watchdog timeout and pre-timeout values.
+ If used, at least first timeout value (in seconds) must be provided.
+ A second optional value (in seconds) may also be provided and
+ will be used as the pre-timeout value, if it is given.
+
+Example for FVP Foundation Model v8:
+
+watchdog@2a440000 {
+ compatible = "arm,sbsa-gwdt";
+ reg = <0x0 0x2a440000 0 0x1000>,
+ <0x0 0x2a450000 0 0x1000>;
+ interrupts = <0 27 4>;
+ timeout-sec = <60 30>;
+};
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 742f69d18fc8..198045ea651e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -555,6 +555,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
bootmem_debug [KNL] Enable bootmem allocator debug messages.
+ bert_disable [ACPI]
+ Disable BERT OS support on buggy BIOSes.
+
bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards)
bttv.radio= Most important insmod options are available as
kernel args too.
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt
index d8b0d3367706..c1237758617a 100644
--- a/Documentation/watchdog/watchdog-kernel-api.txt
+++ b/Documentation/watchdog/watchdog-kernel-api.txt
@@ -53,6 +53,9 @@ struct watchdog_device {
unsigned int timeout;
unsigned int min_timeout;
unsigned int max_timeout;
+ unsigned int pretimeout;
+ unsigned int min_pretimeout;
+ unsigned int max_pretimeout;
void *driver_data;
struct mutex lock;
unsigned long status;
@@ -75,6 +78,9 @@ It contains following fields:
* timeout: the watchdog timer's timeout value (in seconds).
* min_timeout: the watchdog timer's minimum timeout value (in seconds).
* max_timeout: the watchdog timer's maximum timeout value (in seconds).
+* pretimeout: the watchdog timer's pretimeout value (in seconds).
+* min_pretimeout: the watchdog timer's minimum pretimeout value (in seconds).
+* max_pretimeout: the watchdog timer's maximum pretimeout value (in seconds).
* bootstatus: status of the device after booting (reported with watchdog
WDIOF_* status bits).
* driver_data: a pointer to the drivers private data of a watchdog device.
@@ -99,6 +105,7 @@ struct watchdog_ops {
int (*ping)(struct watchdog_device *);
unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int);
+ int (*set_pretimeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *);
void (*ref)(struct watchdog_device *);
void (*unref)(struct watchdog_device *);
@@ -156,13 +163,24 @@ they are supported. These optional routines/operations are:
* status: this routine checks the status of the watchdog timer device. The
status of the device is reported with watchdog WDIOF_* status flags/bits.
* set_timeout: this routine checks and changes the timeout of the watchdog
- timer device. It returns 0 on success, -EINVAL for "parameter out of range"
- and -EIO for "could not write value to the watchdog". On success this
- routine should set the timeout value of the watchdog_device to the
- achieved timeout value (which may be different from the requested one
- because the watchdog does not necessarily has a 1 second resolution).
+ timer device. It returns 0 on success, -EINVAL for "parameter out of
+ range" (e.g., if the driver supports pretimeout, then the requested
+ timeout value must be greater than the pretimeout value) and -EIO for
+ "could not write value to the watchdog". On success this routine will
+ set the timeout value of the watchdog_device to an actual timeout value
+ (which may be different from the requested one because the watchdog does
+ not necessarily have a 1 second resolution).
(Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
watchdog's info structure).
+* set_pretimeout: this routine checks and changes the pretimeout of the
+ watchdog timer device. It returns 0 on success, -EINVAL for "parameter
+ out of range" and -EIO for "could not write value to the watchdog". On
+ success this routine will set the pretimeout value of the
+ watchdog_device to an actual pretimeout value (which may be different
+ from the requested one because the watchdog does not necessarily have a
+ 1 second resolution).
+ (Note: the WDIOF_PRETIMEOUT needs to be set in the options field of the
+ watchdog's info structure).
* get_timeleft: this routines returns the time that's left before a reset.
* ref: the operation that calls kref_get on the kref of a dynamically
allocated watchdog_device struct.
@@ -220,14 +238,39 @@ The watchdog_get_drvdata function allows you to retrieve driver specific data.
The argument of this function is the watchdog device where you want to retrieve
data from. The function returns the pointer to the driver specific data.
+There are three possible sources of driver default timeout values:
+(1) the driver contains hard-coded default values;
+(2) the timeout-sec from the device tree;
+(3) module parameters can be given when the module is loaded.
To initialize the timeout field, the following function can be used:
extern int watchdog_init_timeout(struct watchdog_device *wdd,
unsigned int timeout_parm, struct device *dev);
The watchdog_init_timeout function allows you to initialize the timeout field
-using the module timeout parameter or by retrieving the timeout-sec property from
-the device tree (if the module timeout parameter is invalid). Best practice is
-to set the default timeout value as timeout value in the watchdog_device and
-then use this function to set the user "preferred" timeout value.
+using the module timeout parameter or by retrieving the first element of
+the timeout-sec property from the device tree (if the module timeout
+parameter is invalid). Best practice is to set the default timeout value
+as the timeout value in the watchdog_device and then use this function to
+set the user preferred timeout value. By this way, if timeout values are
+provided when the module loads, they will take priority. Second priority
+will be the timeout-sec from DTB, and third the hard-coded driver values.
+This routine returns zero on success and a negative errno code for failure.
+
+Some watchdog timers have two stages of timeout (timeout and pretimeout).
+To initialize the timeout and pretimeout fields at the same time, the
+following function can be used:
+
+int watchdog_init_timeouts(struct watchdog_device *wdd,
+ unsigned int pretimeout_parm,
+ unsigned int timeout_parm,
+ struct device *dev);
+
+The watchdog_init_timeouts function allows you to initialize the
+pretimeout and timeout fields using the module pretimeout and timeout
+parameter or by retrieving the elements in the timeout-sec property from
+the device tree (if the module pretimeout and timeout parameter are
+invalid). Best practice is to set the default pretimeout and timeout
+values as pretimeout and timeout values in the watchdog_device and then
+use this function to set the user preferred pretimeout and timeout value.
This routine returns zero on success and a negative errno code for failure.
diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt
index 9f9ec9f76039..e62c8c4d1376 100644
--- a/Documentation/watchdog/watchdog-parameters.txt
+++ b/Documentation/watchdog/watchdog-parameters.txt
@@ -284,6 +284,12 @@ sbc_fitpc2_wdt:
margin: Watchdog margin in seconds (default 60s)
nowayout: Watchdog cannot be stopped once started
-------------------------------------------------
+sbsa_gwdt:
+timeout: Watchdog timeout in seconds. (default 60s)
+pretimeout: Watchdog pretimeout in seconds. (default 30s)
+nowayout: Watchdog cannot be stopped once started
+ (default=kernel config parameter)
+-------------------------------------------------
sc1200wdt:
isapnp: When set to 0 driver ISA PnP support will be disabled (default=1)
io: io port
diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c
index 5f387ee5b5c5..1c3c51392fad 100644
--- a/arch/alpha/kernel/pci.c
+++ b/arch/alpha/kernel/pci.c
@@ -354,7 +354,6 @@ common_init_pci(void)
pcibios_claim_console_setup();
pci_assign_unassigned_resources();
- pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq);
for (hose = hose_head; hose; hose = hose->next) {
bus = hose->bus;
if (bus)
@@ -362,6 +361,12 @@ common_init_pci(void)
}
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = alpha_mv.pci_swizzle;
+ bridge->map_irq = alpha_mv.pci_map_irq;
+ return 0;
+}
struct pci_controller * __init
alloc_pci_controller(void)
diff --git a/arch/alpha/kernel/sys_nautilus.c b/arch/alpha/kernel/sys_nautilus.c
index 2cfaa0e5c577..a3086dc8135c 100644
--- a/arch/alpha/kernel/sys_nautilus.c
+++ b/arch/alpha/kernel/sys_nautilus.c
@@ -254,7 +254,6 @@ nautilus_init_pci(void)
/* pci_common_swizzle() relies on bus->self being NULL
for the root bus, so just clear it. */
bus->self = NULL;
- pci_fixup_irqs(alpha_mv.pci_swizzle, alpha_mv.pci_map_irq);
pci_bus_add_devices(bus);
}
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index 066f7f9ba411..962d05bcef4f 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -410,6 +410,13 @@ static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
return irq;
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pcibios_swizzle;
+ bridge->map_irq = pcibios_map_irq;
+ return 0;
+}
+
static int pcibios_init_resources(int busnr, struct pci_sys_data *sys)
{
int ret;
@@ -510,8 +517,6 @@ void pci_common_init_dev(struct device *parent, struct hw_pci *hw)
if (hw->postinit)
hw->postinit();
- pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
-
list_for_each_entry(sys, &head, node) {
struct pci_bus *bus = sys->bus;
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 871f21783866..ca7125870722 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -2,7 +2,10 @@ config ARM64
def_bool y
select ACPI_CCA_REQUIRED if ACPI
select ACPI_GENERIC_GSI if ACPI
+ select ACPI_PCI_HOST_GENERIC if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
+ select ACPI_SPCR_TABLE if ACPI
+ select HAVE_ACPI_APEI if ACPI
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_GCOV_PROFILE_ALL
@@ -15,6 +18,7 @@ config ARM64
select ARCH_WANT_FRAME_POINTERS
select ARM_AMBA
select ARM_ARCH_TIMER
+ select ACPI_GTDT if ACPI
select ARM_GIC
select AUDIT_ARCH_COMPAT_GENERIC
select ARM_GIC_V2M if PCI_MSI
@@ -71,6 +75,7 @@ config ARM64
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if PERF_EVENTS
select HAVE_MEMBLOCK
+ select HAVE_MEMBLOCK_NODE_MAP if NUMA
select HAVE_PATA_PLATFORM
select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
@@ -92,6 +97,7 @@ config ARM64
select SPARSE_IRQ
select SYSCTL_EXCEPTION_TRACE
select HAVE_CONTEXT_TRACKING
+ select HAVE_PCI_ECAM
help
ARM 64-bit (AArch64) Linux support.
@@ -207,6 +213,11 @@ source "drivers/pci/Kconfig"
source "drivers/pci/pcie/Kconfig"
source "drivers/pci/hotplug/Kconfig"
+config PCI_MMCONFIG
+ def_bool y
+ select PCI_ECAM
+ depends on ACPI
+
endmenu
menu "Kernel Features"
@@ -503,6 +514,40 @@ config HOTPLUG_CPU
Say Y here to experiment with turning CPUs off and on. CPUs
can be controlled through /sys/devices/system/cpu.
+# Common NUMA Features
+config NUMA
+ bool "Numa Memory Allocation and Scheduler Support"
+ depends on SMP
+ help
+ Enable NUMA (Non Uniform Memory Access) support.
+
+ The kernel will try to allocate memory used by a CPU on the
+ local memory of the CPU and add some more
+ NUMA awareness to the kernel.
+
+config OF_NUMA
+ bool "Device Tree NUMA support"
+ depends on NUMA
+ depends on OF
+ default y
+ help
+ Enable Device Tree NUMA support.
+ This enables the numa mapping of cpu, memory, io and
+ inter node distances using dt bindings.
+
+config NODES_SHIFT
+ int "Maximum NUMA Nodes (as a power of 2)"
+ range 1 10
+ default "2"
+ depends on NEED_MULTIPLE_NODES
+ help
+ Specify the maximum number of NUMA Nodes available on the target
+ system. Increases memory reserved to accommodate various tables.
+
+config USE_PERCPU_NUMA_NODE_ID
+ def_bool y
+ depends on NUMA
+
source kernel/Kconfig.preempt
source kernel/Kconfig.hz
diff --git a/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi b/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi
index 2874d92881fd..f62d60c1a9ca 100644
--- a/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi
+++ b/arch/arm64/boot/dts/amd/amd-seattle-soc.dtsi
@@ -84,6 +84,14 @@
clock-names = "uartclk", "apb_pclk";
};
+ watchdog0: watchdog@e0bb0000 {
+ compatible = "arm,sbsa-gwdt";
+ reg = <0x0 0xe0bc0000 0 0x1000>,
+ <0x0 0xe0bb0000 0 0x1000>;
+ interrupts = <0 337 4>;
+ timeout-sec = <60 30>;
+ };
+
spi0: ssp@e1020000 {
status = "disabled";
compatible = "arm,pl022", "arm,primecell";
diff --git a/arch/arm64/boot/dts/arm/foundation-v8.dts b/arch/arm64/boot/dts/arm/foundation-v8.dts
index 4eac8dcea423..1a4fc40cbf0b 100644
--- a/arch/arm64/boot/dts/arm/foundation-v8.dts
+++ b/arch/arm64/boot/dts/arm/foundation-v8.dts
@@ -237,4 +237,11 @@
};
};
};
+ watchdog@2a440000 {
+ compatible = "arm,sbsa-gwdt";
+ reg = <0x0 0x2a440000 0 0x1000>,
+ <0x0 0x2a450000 0 0x1000>;
+ interrupts = <0 27 4>;
+ timeout-sec = <60 30>;
+ };
};
diff --git a/arch/arm64/boot/dts/cavium/Makefile b/arch/arm64/boot/dts/cavium/Makefile
index e34f89ddabb2..7fe70672d866 100644
--- a/arch/arm64/boot/dts/cavium/Makefile
+++ b/arch/arm64/boot/dts/cavium/Makefile
@@ -1,4 +1,4 @@
-dtb-$(CONFIG_ARCH_THUNDER) += thunder-88xx.dtb
+dtb-$(CONFIG_ARCH_THUNDER) += thunder-88xx.dtb thunder-88xx-2n.dtb
always := $(dtb-y)
subdir-y := $(dts-dirs)
diff --git a/arch/arm64/boot/dts/cavium/thunder-88xx-2n.dts b/arch/arm64/boot/dts/cavium/thunder-88xx-2n.dts
new file mode 100644
index 000000000000..5601e87102d8
--- /dev/null
+++ b/arch/arm64/boot/dts/cavium/thunder-88xx-2n.dts
@@ -0,0 +1,83 @@
+/*
+ * Cavium Thunder DTS file - Thunder board description
+ *
+ * Copyright (C) 2014, Cavium Inc.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+/include/ "thunder-88xx-2n.dtsi"
+
+/ {
+ model = "Cavium ThunderX CN88XX board";
+ compatible = "cavium,thunder-88xx";
+
+ aliases {
+ serial0 = &uaa0;
+ serial1 = &uaa1;
+ };
+
+ memory@00000000 {
+ device_type = "memory";
+ reg = <0x0 0x01400000 0x3 0xFEC00000>;
+ /* socket 0 */
+ numa-node-id = <0>;
+ };
+
+ memory@10000000000 {
+ device_type = "memory";
+ reg = <0x100 0x00400000 0x3 0xFFC00000>;
+ /* socket 1 */
+ numa-node-id = <1>;
+ };
+
+ distance-map {
+ compatible = "numa-distance-map-v1";
+ distance-matrix = <0 0 10>,
+ <0 1 20>,
+ <1 1 10>;
+ };
+};
diff --git a/arch/arm64/boot/dts/cavium/thunder-88xx-2n.dtsi b/arch/arm64/boot/dts/cavium/thunder-88xx-2n.dtsi
new file mode 100644
index 000000000000..b58e5c764d84
--- /dev/null
+++ b/arch/arm64/boot/dts/cavium/thunder-88xx-2n.dtsi
@@ -0,0 +1,806 @@
+/*
+ * Cavium Thunder DTS file - Thunder SoC description
+ *
+ * Copyright (C) 2014, Cavium Inc.
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/ {
+ compatible = "cavium,thunder-88xx";
+ interrupt-parent = <&gic0>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ psci {
+ compatible = "arm,psci-0.2";
+ method = "smc";
+ };
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cpu@000 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x000>;
+ enable-method = "psci";
+ /* socket 0 */
+ numa-node-id = <0>;
+ };
+ cpu@001 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x001>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@002 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x002>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@003 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x003>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@004 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x004>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@005 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x005>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@006 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x006>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@007 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x007>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@008 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x008>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@009 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x009>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@00a {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x00a>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@00b {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x00b>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@00c {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x00c>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@00d {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x00d>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@00e {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x00e>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@00f {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x00f>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@100 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@101 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@102 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x102>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@103 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x103>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@104 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x104>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@105 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x105>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@106 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x106>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@107 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x107>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@108 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x108>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@109 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x109>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@10a {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10a>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@10b {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10b>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@10c {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10c>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@10d {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10d>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@10e {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10e>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@10f {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10f>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@200 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x200>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@201 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x201>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@202 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x202>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@203 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x203>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@204 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x204>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@205 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x205>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@206 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x206>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@207 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x207>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@208 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x208>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@209 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x209>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@20a {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x20a>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@20b {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x20b>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@20c {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x20c>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@20d {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x20d>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@20e {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x20e>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@20f {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x20f>;
+ enable-method = "psci";
+ numa-node-id = <0>;
+ };
+ cpu@10000 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10000>;
+ enable-method = "psci";
+ /* socket 1 */
+ numa-node-id = <1>;
+ };
+ cpu@10001 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10001>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10002 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10002>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10003 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10003>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10004 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10004>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10005 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10005>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10006 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10006>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10007 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10007>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10008 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10008>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10009 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10009>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1000a {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1000a>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1000b {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1000b>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1000c {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1000c>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1000d {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1000d>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1000e {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1000e>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1000f {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1000f>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10100 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10100>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10101 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10101>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10102 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10102>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10103 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10103>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10104 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10104>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10105 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10105>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10106 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10106>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10107 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10107>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10108 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10108>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10109 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10109>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1010a {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1010a>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1010b {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1010b>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1010c {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1010c>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1010d {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1010d>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1010e {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1010e>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1010f {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1010f>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10200 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10200>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10201 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10201>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10202 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10202>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10203 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10203>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10204 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10204>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10205 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10205>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10206 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10206>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10207 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10207>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10208 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10208>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@10209 {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x10209>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1020a {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1020a>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1020b {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1020b>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1020c {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1020c>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1020d {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1020d>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1020e {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1020e>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ cpu@1020f {
+ device_type = "cpu";
+ compatible = "cavium,thunder", "arm,armv8";
+ reg = <0x0 0x1020f>;
+ enable-method = "psci";
+ numa-node-id = <1>;
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer";
+ interrupts = <1 13 0xff01>,
+ <1 14 0xff01>,
+ <1 11 0xff01>,
+ <1 10 0xff01>;
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ refclk50mhz: refclk50mhz {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <50000000>;
+ clock-output-names = "refclk50mhz";
+ };
+
+ gic0: interrupt-controller@8010,00000000 {
+ compatible = "arm,gic-v3";
+ #interrupt-cells = <3>;
+ #address-cells = <2>;
+ #size-cells = <2>;
+ #redistributor-regions = <2>;
+ ranges;
+ interrupt-controller;
+ reg = <0x8010 0x00000000 0x0 0x010000>, /* GICD */
+ <0x8010 0x80000000 0x0 0x600000>, /* GICR Node 0 */
+ <0x9010 0x80000000 0x0 0x600000>; /* GICR Node 1 */
+ interrupts = <1 9 0xf04>;
+
+ its: gic-its@8010,00020000 {
+ compatible = "arm,gic-v3-its";
+ msi-controller;
+ reg = <0x8010 0x20000 0x0 0x200000>;
+ numa-node-id = <0>;
+ };
+
+ its1: gic-its@9010,00020000 {
+ compatible = "arm,gic-v3-its";
+ msi-controller;
+ reg = <0x9010 0x20000 0x0 0x200000>;
+ numa-node-id = <1>;
+ };
+ };
+
+ uaa0: serial@87e0,24000000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x87e0 0x24000000 0x0 0x1000>;
+ interrupts = <1 21 4>;
+ clocks = <&refclk50mhz>;
+ clock-names = "apb_pclk";
+ };
+
+ uaa1: serial@87e0,25000000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x87e0 0x25000000 0x0 0x1000>;
+ interrupts = <1 22 4>;
+ clocks = <&refclk50mhz>;
+ clock-names = "apb_pclk";
+ };
+ };
+};
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index caafd63b8092..cc1705c59315 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -17,6 +17,7 @@
#include <asm/cputype.h>
#include <asm/smp_plat.h>
+#include <asm/tlbflush.h>
/* Macros for consistency checks of the GICC subtable of MADT */
#define ACPI_MADT_GICC_LENGTH \
@@ -94,6 +95,18 @@ static inline const char *acpi_get_enable_method(int cpu)
#ifdef CONFIG_ACPI_APEI
pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr);
-#endif
+static inline void arch_apei_flush_tlb_one(unsigned long addr)
+{
+ flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
+}
+#endif /* CONFIG_ACPI_APEI */
+
+#ifdef CONFIG_ACPI_NUMA
+int arm64_acpi_numa_init(void);
+void acpi_numa_set_node_info(unsigned int cpu, u64 hwid);
+#else
+static inline int arm64_acpi_numa_init(void) { return -ENODEV; }
+static inline void acpi_numa_set_node_info(unsigned int cpu, u64 hwid) { }
+#endif /* CONFIG_ACPI_NUMA */
#endif /*_ASM_ACPI_H*/
diff --git a/arch/arm64/include/asm/mmzone.h b/arch/arm64/include/asm/mmzone.h
new file mode 100644
index 000000000000..2cd804d8159e
--- /dev/null
+++ b/arch/arm64/include/asm/mmzone.h
@@ -0,0 +1,18 @@
+#ifndef __ASM_MMZONE_H
+#define __ASM_MMZONE_H
+
+#ifdef CONFIG_NUMA
+
+#include <linux/mmdebug.h>
+#include <linux/types.h>
+
+#include <asm/smp.h>
+#include <asm/numa.h>
+
+extern struct pglist_data *node_data[];
+
+#define NODE_DATA(nid) (node_data[(nid)])
+
+#endif /* CONFIG_NUMA */
+
+#endif /* __ASM_MMZONE_H */
diff --git a/arch/arm64/include/asm/numa.h b/arch/arm64/include/asm/numa.h
new file mode 100644
index 000000000000..1beb194ea136
--- /dev/null
+++ b/arch/arm64/include/asm/numa.h
@@ -0,0 +1,53 @@
+#ifndef __ASM_NUMA_H
+#define __ASM_NUMA_H
+
+#include <linux/nodemask.h>
+#include <asm/topology.h>
+
+#ifdef CONFIG_NUMA
+
+#define NR_NODE_MEMBLKS (MAX_NUMNODES * 2)
+
+/* currently, arm64 implements flat NUMA topology */
+#define parent_node(node) (node)
+
+extern int __node_distance(int from, int to);
+#define node_distance(a, b) __node_distance(a, b)
+
+extern int cpu_to_node_map[NR_CPUS];
+extern nodemask_t numa_nodes_parsed __initdata;
+
+/* Mappings between node number and cpus on that node. */
+extern cpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
+extern void numa_clear_node(unsigned int cpu);
+#ifdef CONFIG_DEBUG_PER_CPU_MAPS
+extern const struct cpumask *cpumask_of_node(int node);
+#else
+/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
+static inline const struct cpumask *cpumask_of_node(int node)
+{
+ return node_to_cpumask_map[node];
+}
+#endif
+
+void __init arm64_numa_init(void);
+int __init numa_add_memblk(int nodeid, u64 start, u64 end);
+void __init numa_set_distance(int from, int to, int distance);
+void __init numa_reset_distance(void);
+void numa_store_cpu_info(unsigned int cpu);
+#else /* CONFIG_NUMA */
+static inline void numa_store_cpu_info(unsigned int cpu) { }
+static inline void arm64_numa_init(void) { }
+#endif /* CONFIG_NUMA */
+
+struct device_node;
+#ifdef CONFIG_OF_NUMA
+int __init arm64_of_numa_init(void);
+void __init of_numa_set_node_info(unsigned int cpu, struct device_node *dn);
+#else
+static inline int arm64_of_numa_init(void) { return -ENODEV; }
+static inline void of_numa_set_node_info(unsigned int cpu,
+ struct device_node *dn) { }
+#endif
+
+#endif /* __ASM_NUMA_H */
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index a3e9d6fdbf21..6e1f62c0569f 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -22,6 +22,13 @@ void init_cpu_topology(void);
void store_cpu_topology(unsigned int cpuid);
const struct cpumask *cpu_coregroup_mask(int cpu);
+#ifdef CONFIG_NUMA
+
+struct pci_bus;
+int pcibus_to_node(struct pci_bus *bus);
+
+#endif /* CONFIG_NUMA */
+
#include <asm-generic/topology.h>
#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 474691f8b13a..555c4a584f40 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -41,6 +41,8 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o
arm64-obj-$(CONFIG_PCI) += pci.o
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
arm64-obj-$(CONFIG_ACPI) += acpi.o
+arm64-obj-$(CONFIG_OF_NUMA) += of_numa.o
+arm64-obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/acpi_numa.c b/arch/arm64/kernel/acpi_numa.c
new file mode 100644
index 000000000000..15fb935d9d91
--- /dev/null
+++ b/arch/arm64/kernel/acpi_numa.c
@@ -0,0 +1,204 @@
+/*
+ * ACPI 5.1 based NUMA setup for ARM64
+ * Lots of code was borrowed from arch/x86/mm/srat.c
+ *
+ * Copyright 2004 Andi Kleen, SuSE Labs.
+ * Copyright (C) 2013-2016, Linaro Ltd.
+ * Author: Hanjun Guo <hanjun.guo@linaro.org>
+ *
+ * Reads the ACPI SRAT table to figure out what memory belongs to which CPUs.
+ *
+ * Called from acpi_numa_init while reading the SRAT and SLIT tables.
+ * Assumes all memory regions belonging to a single proximity domain
+ * are in one chunk. Holes between them will be included in the node.
+ */
+
+#define pr_fmt(fmt) "ACPI: NUMA: " fmt
+
+#include <linux/acpi.h>
+#include <linux/bitmap.h>
+#include <linux/bootmem.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+#include <linux/mmzone.h>
+#include <linux/module.h>
+#include <linux/topology.h>
+
+#include <acpi/processor.h>
+#include <asm/numa.h>
+
+int acpi_numa __initdata;
+static int cpus_in_srat;
+
+struct __node_cpu_hwid {
+ u32 node_id; /* logical node containing this CPU */
+ u64 cpu_hwid; /* MPIDR for this CPU */
+};
+
+static struct __node_cpu_hwid early_node_cpu_hwid[NR_CPUS] = {
+[0 ... NR_CPUS - 1] = {NUMA_NO_NODE, PHYS_CPUID_INVALID} };
+
+static __init void bad_srat(void)
+{
+ pr_err("SRAT not used.\n");
+ acpi_numa = -1;
+}
+
+static __init inline int srat_disabled(void)
+{
+ return acpi_numa < 0;
+}
+
+void __init acpi_numa_set_node_info(unsigned int cpu, u64 hwid)
+{
+ int nid = 0, i;
+
+ for (i = 0; i < cpus_in_srat; i++) {
+ if (hwid == early_node_cpu_hwid[i].cpu_hwid) {
+ nid = early_node_cpu_hwid[i].node_id;
+ break;
+ }
+ }
+
+ cpu_to_node_map[cpu] = nid;
+}
+
+static int __init get_mpidr_in_madt(int acpi_id, u64 *mpidr)
+{
+ unsigned long madt_end, entry;
+ struct acpi_table_madt *madt;
+ acpi_size tbl_size;
+
+ if (ACPI_FAILURE(acpi_get_table_with_size(ACPI_SIG_MADT, 0,
+ (struct acpi_table_header **)&madt, &tbl_size)))
+ return -ENODEV;
+
+ entry = (unsigned long)madt;
+ madt_end = entry + madt->header.length;
+
+ /* Parse all entries looking for a match. */
+ entry += sizeof(struct acpi_table_madt);
+ while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
+ struct acpi_subtable_header *header =
+ (struct acpi_subtable_header *)entry;
+
+ if (header->type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
+ struct acpi_madt_generic_interrupt *gicc =
+ container_of(header,
+ struct acpi_madt_generic_interrupt, header);
+
+ if ((gicc->flags & ACPI_MADT_ENABLED) &&
+ (gicc->uid == acpi_id)) {
+ *mpidr = gicc->arm_mpidr;
+ early_acpi_os_unmap_memory(madt, tbl_size);
+ return 0;
+ }
+ }
+ entry += header->length;
+ }
+
+ early_acpi_os_unmap_memory(madt, tbl_size);
+ return -ENODEV;
+}
+
+/* Callback for Proximity Domain -> ACPI processor UID mapping */
+void __init acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa)
+{
+ int pxm, node;
+ u64 mpidr;
+
+ if (srat_disabled())
+ return;
+
+ if (pa->header.length < sizeof(struct acpi_srat_gicc_affinity)) {
+ bad_srat();
+ return;
+ }
+
+ if (!(pa->flags & ACPI_SRAT_GICC_ENABLED))
+ return;
+
+ if (cpus_in_srat >= NR_CPUS) {
+ pr_warn_once("SRAT: cpu_to_node_map[%d] is too small, may not be able to use all cpus\n",
+ NR_CPUS);
+ return;
+ }
+
+ pxm = pa->proximity_domain;
+ node = acpi_map_pxm_to_node(pxm);
+
+ if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
+ pr_err("SRAT: Too many proximity domains %d\n", pxm);
+ bad_srat();
+ return;
+ }
+
+ if (get_mpidr_in_madt(pa->acpi_processor_uid, &mpidr)) {
+ pr_warn("SRAT: PXM %d with ACPI ID %d has no valid MPIDR in MADT\n",
+ pxm, pa->acpi_processor_uid);
+ bad_srat();
+ return;
+ }
+
+ early_node_cpu_hwid[cpus_in_srat].node_id = node;
+ early_node_cpu_hwid[cpus_in_srat].cpu_hwid = mpidr;
+ node_set(node, numa_nodes_parsed);
+ acpi_numa = 1;
+ cpus_in_srat++;
+ pr_info("SRAT: PXM %d -> MPIDR 0x%Lx -> Node %d cpu %d\n",
+ pxm, mpidr, node, cpus_in_srat);
+}
+
+/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
+int __init acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
+{
+ u64 start, end;
+ int node, pxm;
+
+ if (srat_disabled())
+ return -EINVAL;
+
+ if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) {
+ bad_srat();
+ return -EINVAL;
+ }
+
+ /* Ignore disabled entries */
+ if (!(ma->flags & ACPI_SRAT_MEM_ENABLED))
+ return -EINVAL;
+
+ start = ma->base_address;
+ end = start + ma->length;
+ pxm = ma->proximity_domain;
+
+ node = acpi_map_pxm_to_node(pxm);
+
+ if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
+ pr_err("SRAT: Too many proximity domains.\n");
+ bad_srat();
+ return -EINVAL;
+ }
+
+ pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n",
+ node, pxm,
+ (unsigned long long) start, (unsigned long long) end - 1);
+
+ if (numa_add_memblk(node, start, (end - start)) < 0) {
+ bad_srat();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int __init arm64_acpi_numa_init(void)
+{
+ int ret;
+
+ ret = acpi_numa_init();
+ if (ret)
+ return ret;
+
+ return srat_disabled() ? -EINVAL : 0;
+}
diff --git a/arch/arm64/kernel/of_numa.c b/arch/arm64/kernel/of_numa.c
new file mode 100644
index 000000000000..2f9e34bea230
--- /dev/null
+++ b/arch/arm64/kernel/of_numa.c
@@ -0,0 +1,257 @@
+/*
+ * OF NUMA Parsing support.
+ *
+ * Copyright (C) 2015 Cavium Inc.
+ * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/ctype.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/nodemask.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+
+#include <asm/smp_plat.h>
+
+/* define default numa node to 0 */
+#define DEFAULT_NODE 0
+#define OF_NUMA_PROP "numa-node-id"
+
+/* Returns nid in the range [0..MAX_NUMNODES-1],
+ * or NUMA_NO_NODE if no valid numa-node-id entry found
+ * or DEFAULT_NODE if no numa-node-id entry exists
+ */
+static int of_numa_prop_to_nid(const __be32 *of_numa_prop, int length)
+{
+ int nid;
+
+ if (!of_numa_prop)
+ return DEFAULT_NODE;
+
+ if (length != sizeof(*of_numa_prop)) {
+ pr_warn("NUMA: Invalid of_numa_prop length %d found.\n",
+ length);
+ return NUMA_NO_NODE;
+ }
+
+ nid = of_read_number(of_numa_prop, 1);
+ if (nid >= MAX_NUMNODES) {
+ pr_warn("NUMA: Invalid numa node %d found.\n", nid);
+ return NUMA_NO_NODE;
+ }
+
+ return nid;
+}
+
+/* Must hold reference to node during call */
+static int of_get_numa_nid(struct device_node *device)
+{
+ int length;
+ const __be32 *of_numa_prop;
+
+ of_numa_prop = of_get_property(device, OF_NUMA_PROP, &length);
+
+ return of_numa_prop_to_nid(of_numa_prop, length);
+}
+
+static int __init early_init_of_get_numa_nid(unsigned long node)
+{
+ int length;
+ const __be32 *of_numa_prop;
+
+ of_numa_prop = of_get_flat_dt_prop(node, OF_NUMA_PROP, &length);
+
+ return of_numa_prop_to_nid(of_numa_prop, length);
+}
+
+/* Walk the device tree upwards, looking for a numa-node-id property */
+int of_node_to_nid(struct device_node *device)
+{
+ struct device_node *parent;
+ int nid = NUMA_NO_NODE;
+
+ of_node_get(device);
+ while (device) {
+ const __be32 *of_numa_prop;
+ int length;
+
+ of_numa_prop = of_get_property(device, OF_NUMA_PROP, &length);
+ if (of_numa_prop) {
+ nid = of_numa_prop_to_nid(of_numa_prop, length);
+ break;
+ }
+
+ parent = device;
+ device = of_get_parent(parent);
+ of_node_put(parent);
+ }
+ of_node_put(device);
+
+ return nid;
+}
+
+void __init of_numa_set_node_info(unsigned int cpu, struct device_node *device)
+{
+ int nid = DEFAULT_NODE;
+
+ if (device)
+ nid = of_get_numa_nid(device);
+
+ cpu_to_node_map[cpu] = nid;
+}
+
+/*
+ * Even though we connect cpus to numa domains later in SMP
+ * init, we need to know the node ids now for all cpus.
+*/
+static int __init early_init_parse_cpu_node(unsigned long node)
+{
+ int nid;
+
+ const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+
+ /* We are scanning "cpu" nodes only */
+ if (type == NULL)
+ return 0;
+ else if (strcmp(type, "cpu") != 0)
+ return 0;
+
+ nid = early_init_of_get_numa_nid(node);
+
+ if (nid == NUMA_NO_NODE)
+ return -EINVAL;
+
+ node_set(nid, numa_nodes_parsed);
+ return 0;
+}
+
+static int __init early_init_parse_memory_node(unsigned long node)
+{
+ const __be32 *reg, *endp;
+ int length;
+ int nid;
+
+ const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+
+ /* We are scanning "memory" nodes only */
+ if (type == NULL)
+ return 0;
+ else if (strcmp(type, "memory") != 0)
+ return 0;
+
+ nid = early_init_of_get_numa_nid(node);
+
+ if (nid == NUMA_NO_NODE)
+ return -EINVAL;
+
+ reg = of_get_flat_dt_prop(node, "reg", &length);
+ endp = reg + (length / sizeof(__be32));
+
+ while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
+ u64 base, size;
+
+ base = dt_mem_next_cell(dt_root_addr_cells, &reg);
+ size = dt_mem_next_cell(dt_root_size_cells, &reg);
+ pr_debug("NUMA-DT: base = %llx , node = %u\n",
+ base, nid);
+
+ if (numa_add_memblk(nid, base, size) < 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init early_init_parse_distance_map_v1(unsigned long node,
+ const char *uname)
+{
+
+ const __be32 *prop_dist_matrix;
+ int length = 0, i, matrix_count;
+ int nr_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
+
+ pr_info("NUMA: parsing numa-distance-map-v1\n");
+
+ prop_dist_matrix =
+ of_get_flat_dt_prop(node, "distance-matrix", &length);
+
+ if (!length) {
+ pr_err("NUMA: failed to parse distance-matrix\n");
+ return -ENODEV;
+ }
+
+ matrix_count = ((length / sizeof(__be32)) / (3 * nr_size_cells));
+
+ if ((matrix_count * sizeof(__be32) * 3 * nr_size_cells) != length) {
+ pr_warn("NUMA: invalid distance-matrix length %d\n", length);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < matrix_count; i++) {
+ u32 nodea, nodeb, distance;
+
+ nodea = dt_mem_next_cell(nr_size_cells, &prop_dist_matrix);
+ nodeb = dt_mem_next_cell(nr_size_cells, &prop_dist_matrix);
+ distance = dt_mem_next_cell(nr_size_cells, &prop_dist_matrix);
+ numa_set_distance(nodea, nodeb, distance);
+ pr_debug("NUMA-DT: distance[node%d -> node%d] = %d\n",
+ nodea, nodeb, distance);
+
+ /* Set default distance of node B->A same as A->B */
+ if (nodeb > nodea)
+ numa_set_distance(nodeb, nodea, distance);
+ }
+
+ return 0;
+}
+
+static int __init early_init_parse_distance_map(unsigned long node,
+ const char *uname)
+{
+
+ if (strcmp(uname, "distance-map") != 0)
+ return 0;
+
+ if (of_flat_dt_is_compatible(node, "numa-distance-map-v1"))
+ return early_init_parse_distance_map_v1(node, uname);
+
+ return -EINVAL;
+}
+
+/**
+ * early_init_of_scan_numa_map - parse memory node and map nid to memory range.
+ */
+int __init early_init_of_scan_numa_map(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ int ret;
+
+ ret = early_init_parse_cpu_node(node);
+
+ if (!ret)
+ ret = early_init_parse_memory_node(node);
+
+ if (!ret)
+ ret = early_init_parse_distance_map(node, uname);
+
+ return ret;
+}
+
+/* DT node mapping is done already early_init_of_scan_memory */
+int __init arm64_of_numa_init(void)
+{
+ return of_scan_flat_dt(early_init_of_scan_numa_map, NULL);
+}
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index b3d098bd34aa..329ca81242a1 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -10,7 +10,6 @@
*
*/
-#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -61,26 +60,11 @@ int pcibios_add_device(struct pci_dev *dev)
return 0;
}
-/*
- * raw_pci_read/write - Platform-specific PCI config space access.
- */
-int raw_pci_read(unsigned int domain, unsigned int bus,
- unsigned int devfn, int reg, int len, u32 *val)
-{
- return -ENXIO;
-}
-
-int raw_pci_write(unsigned int domain, unsigned int bus,
- unsigned int devfn, int reg, int len, u32 val)
+#ifdef CONFIG_NUMA
+int pcibus_to_node(struct pci_bus *bus)
{
- return -ENXIO;
-}
-
-#ifdef CONFIG_ACPI
-/* Root bridge scanning */
-struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
-{
- /* TODO: Should be revisited when implementing PCI on ACPI */
- return NULL;
+ return dev_to_node(&bus->dev);
}
+EXPORT_SYMBOL(pcibus_to_node);
#endif
+
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 8119479147db..d9b9761e9f60 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -53,6 +53,7 @@
#include <asm/cpufeature.h>
#include <asm/cpu_ops.h>
#include <asm/kasan.h>
+#include <asm/numa.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/smp_plat.h>
@@ -372,6 +373,9 @@ static int __init topology_init(void)
{
int i;
+ for_each_online_node(i)
+ register_one_node(i);
+
for_each_possible_cpu(i) {
struct cpu *cpu = &per_cpu(cpu_data.cpu, i);
cpu->hotpluggable = 1;
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index b1adc51b2c2e..112a892e30df 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -45,6 +45,7 @@
#include <asm/cputype.h>
#include <asm/cpu_ops.h>
#include <asm/mmu_context.h>
+#include <asm/numa.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/processor.h>
@@ -125,6 +126,7 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)
static void smp_store_cpu_info(unsigned int cpuid)
{
store_cpu_topology(cpuid);
+ numa_store_cpu_info(cpuid);
}
/*
@@ -445,6 +447,9 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
/* map the logical cpu id to cpu MPIDR */
cpu_logical_map(cpu_count) = hwid;
+ /* map logical cpu to node */
+ acpi_numa_set_node_info(cpu_count, hwid);
+
cpu_count++;
}
@@ -518,6 +523,8 @@ static void __init of_parse_and_init_cpus(void)
pr_debug("cpu logical map 0x%llx\n", hwid);
cpu_logical_map(cpu_count) = hwid;
+ /* map logical cpu to node */
+ of_numa_set_node_info(cpu_count, dn);
next:
cpu_count++;
}
diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile
index 57f57fde5722..54bb209cae8e 100644
--- a/arch/arm64/mm/Makefile
+++ b/arch/arm64/mm/Makefile
@@ -4,6 +4,7 @@ obj-y := dma-mapping.o extable.o fault.o init.o \
context.o proc.o pageattr.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_ARM64_PTDUMP) += dump.o
+obj-$(CONFIG_NUMA) += numa.o
obj-$(CONFIG_KASAN) += kasan_init.o
KASAN_SANITIZE_kasan_init.o := n
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 17bf39ac83ba..295459976416 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -37,6 +37,7 @@
#include <asm/fixmap.h>
#include <asm/memory.h>
+#include <asm/numa.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/sizes.h>
@@ -77,6 +78,19 @@ static phys_addr_t max_zone_dma_phys(void)
return min(offset + (1ULL << 32), memblock_end_of_DRAM());
}
+#ifdef CONFIG_NUMA
+static void __init zone_sizes_init(unsigned long min, unsigned long max)
+{
+ unsigned long max_zone_pfns[MAX_NR_ZONES] = {0};
+
+ if (IS_ENABLED(CONFIG_ZONE_DMA))
+ max_zone_pfns[ZONE_DMA] = PFN_DOWN(max_zone_dma_phys());
+ max_zone_pfns[ZONE_NORMAL] = max;
+
+ free_area_init_nodes(max_zone_pfns);
+}
+
+#else
static void __init zone_sizes_init(unsigned long min, unsigned long max)
{
struct memblock_region *reg;
@@ -116,6 +130,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
free_area_init_node(0, zone_size, min, zhole_size);
}
+#endif /* CONFIG_NUMA */
#ifdef CONFIG_HAVE_ARCH_PFN_VALID
int pfn_valid(unsigned long pfn)
@@ -133,10 +148,15 @@ static void arm64_memory_present(void)
static void arm64_memory_present(void)
{
struct memblock_region *reg;
+ int nid = 0;
- for_each_memblock(memory, reg)
- memory_present(0, memblock_region_memory_base_pfn(reg),
- memblock_region_memory_end_pfn(reg));
+ for_each_memblock(memory, reg) {
+#ifdef CONFIG_NUMA
+ nid = reg->nid;
+#endif
+ memory_present(nid, memblock_region_memory_base_pfn(reg),
+ memblock_region_memory_end_pfn(reg));
+ }
}
#endif
@@ -181,7 +201,6 @@ void __init arm64_memblock_init(void)
dma_contiguous_reserve(arm64_dma_phys_limit);
memblock_allow_resize();
- memblock_dump_all();
}
void __init bootmem_init(void)
@@ -193,6 +212,9 @@ void __init bootmem_init(void)
early_memtest(min << PAGE_SHIFT, max << PAGE_SHIFT);
+ max_pfn = max_low_pfn = max;
+
+ arm64_numa_init();
/*
* Sparsemem tries to allocate bootmem in memory_present(), so must be
* done after the fixed reservations.
@@ -203,7 +225,6 @@ void __init bootmem_init(void)
zone_sizes_init(min, max);
high_memory = __va((max << PAGE_SHIFT) - 1) + 1;
- max_pfn = max_low_pfn = max;
}
#ifndef CONFIG_SPARSEMEM_VMEMMAP
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 873e363048c6..6f6807517eb5 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -453,6 +453,7 @@ void __init paging_init(void)
zero_page = early_alloc(PAGE_SIZE);
bootmem_init();
+ memblock_dump_all();
empty_zero_page = virt_to_page(zero_page);
diff --git a/arch/arm64/mm/numa.c b/arch/arm64/mm/numa.c
new file mode 100644
index 000000000000..e9749953dcce
--- /dev/null
+++ b/arch/arm64/mm/numa.c
@@ -0,0 +1,394 @@
+/*
+ * NUMA support, based on the x86 implementation.
+ *
+ * Copyright (C) 2015 Cavium Inc.
+ * Author: Ganapatrao Kulkarni <gkulkarni@cavium.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bootmem.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+#include <linux/module.h>
+#include <linux/mmzone.h>
+#include <linux/nodemask.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/topology.h>
+
+#include <asm/smp_plat.h>
+
+struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
+EXPORT_SYMBOL(node_data);
+nodemask_t numa_nodes_parsed __initdata;
+int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
+
+static int numa_off;
+static int numa_distance_cnt;
+static u8 *numa_distance;
+
+static __init int numa_parse_early_param(char *opt)
+{
+ if (!opt)
+ return -EINVAL;
+ if (!strncmp(opt, "off", 3)) {
+ pr_info("%s\n", "NUMA turned off");
+ numa_off = 1;
+ }
+ return 0;
+}
+early_param("numa", numa_parse_early_param);
+
+cpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
+EXPORT_SYMBOL(node_to_cpumask_map);
+
+#ifdef CONFIG_DEBUG_PER_CPU_MAPS
+/*
+ * Returns a pointer to the bitmask of CPUs on Node 'node'.
+ */
+const struct cpumask *cpumask_of_node(int node)
+{
+
+ if (WARN_ON(node >= nr_node_ids))
+ return cpu_none_mask;
+
+ if (WARN_ON(node_to_cpumask_map[node] == NULL))
+ return cpu_online_mask;
+
+ return node_to_cpumask_map[node];
+}
+EXPORT_SYMBOL(cpumask_of_node);
+#endif
+
+static void map_cpu_to_node(unsigned int cpu, int nid)
+{
+ set_cpu_numa_node(cpu, nid);
+ if (nid >= 0)
+ cpumask_set_cpu(cpu, node_to_cpumask_map[nid]);
+}
+
+static void unmap_cpu_to_node(unsigned int cpu)
+{
+ int nid = cpu_to_node(cpu);
+
+ if (nid >= 0)
+ cpumask_clear_cpu(cpu, node_to_cpumask_map[nid]);
+ set_cpu_numa_node(cpu, NUMA_NO_NODE);
+}
+
+void numa_clear_node(unsigned int cpu)
+{
+ unmap_cpu_to_node(cpu);
+}
+
+/*
+ * Allocate node_to_cpumask_map based on number of available nodes
+ * Requires node_possible_map to be valid.
+ *
+ * Note: cpumask_of_node() is not valid until after this is done.
+ * (Use CONFIG_DEBUG_PER_CPU_MAPS to check this.)
+ */
+static void __init setup_node_to_cpumask_map(void)
+{
+ unsigned int cpu;
+ int node;
+
+ /* setup nr_node_ids if not done yet */
+ if (nr_node_ids == MAX_NUMNODES)
+ setup_nr_node_ids();
+
+ /* allocate and clear the mapping */
+ for (node = 0; node < nr_node_ids; node++) {
+ alloc_bootmem_cpumask_var(&node_to_cpumask_map[node]);
+ cpumask_clear(node_to_cpumask_map[node]);
+ }
+
+ for_each_possible_cpu(cpu)
+ set_cpu_numa_node(cpu, NUMA_NO_NODE);
+
+ /* cpumask_of_node() will now work */
+ pr_debug("Node to cpumask map for %d nodes\n", nr_node_ids);
+}
+
+/*
+ * Set the cpu to node and mem mapping
+ */
+void numa_store_cpu_info(unsigned int cpu)
+{
+ map_cpu_to_node(cpu, numa_off ? 0 : cpu_to_node_map[cpu]);
+}
+
+/**
+ * numa_add_memblk - Set node id to memblk
+ * @nid: NUMA node ID of the new memblk
+ * @start: Start address of the new memblk
+ * @size: Size of the new memblk
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int __init numa_add_memblk(int nid, u64 start, u64 size)
+{
+ int ret;
+
+ ret = memblock_set_node(start, size, &memblock.memory, nid);
+ if (ret < 0) {
+ pr_err("NUMA: memblock [0x%llx - 0x%llx] failed to add on node %d\n",
+ start, (start + size - 1), nid);
+ return ret;
+ }
+
+ node_set(nid, numa_nodes_parsed);
+ pr_info("NUMA: Adding memblock [0x%llx - 0x%llx] on node %d\n",
+ start, (start + size - 1), nid);
+ return ret;
+}
+EXPORT_SYMBOL(numa_add_memblk);
+
+/* Initialize NODE_DATA for a node on the local memory */
+static void __init setup_node_data(int nid, u64 start_pfn, u64 end_pfn)
+{
+ const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES);
+ u64 nd_pa;
+ void *nd;
+ int tnid;
+
+ pr_info("Initmem setup node %d [mem %#010Lx-%#010Lx]\n",
+ nid, start_pfn << PAGE_SHIFT,
+ (end_pfn << PAGE_SHIFT) - 1);
+
+ nd_pa = memblock_alloc_try_nid(nd_size, SMP_CACHE_BYTES, nid);
+ nd = __va(nd_pa);
+
+ /* report and initialize */
+ pr_info(" NODE_DATA [mem %#010Lx-%#010Lx]\n",
+ nd_pa, nd_pa + nd_size - 1);
+ tnid = early_pfn_to_nid(nd_pa >> PAGE_SHIFT);
+ if (tnid != nid)
+ pr_info(" NODE_DATA(%d) on node %d\n", nid, tnid);
+
+ node_data[nid] = nd;
+ memset(NODE_DATA(nid), 0, sizeof(pg_data_t));
+ NODE_DATA(nid)->node_id = nid;
+ NODE_DATA(nid)->node_start_pfn = start_pfn;
+ NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
+}
+
+/**
+ * numa_reset_distance - Reset NUMA distance table
+ *
+ * The current table is freed.
+ * The next numa_set_distance() call will create a new one.
+ */
+void __init numa_reset_distance(void)
+{
+ size_t size;
+
+ if (!numa_distance)
+ return;
+
+ size = numa_distance_cnt * numa_distance_cnt *
+ sizeof(numa_distance[0]);
+
+ memblock_free(__pa(numa_distance), size);
+ numa_distance_cnt = 0;
+ numa_distance = NULL;
+}
+
+static int __init numa_alloc_distance(void)
+{
+ size_t size;
+ u64 phys;
+ int i, j;
+
+ size = nr_node_ids * nr_node_ids * sizeof(numa_distance[0]);
+ phys = memblock_find_in_range(0, PFN_PHYS(max_pfn),
+ size, PAGE_SIZE);
+ if (WARN_ON(!phys))
+ return -ENOMEM;
+
+ memblock_reserve(phys, size);
+
+ numa_distance = __va(phys);
+ numa_distance_cnt = nr_node_ids;
+
+ /* fill with the default distances */
+ for (i = 0; i < numa_distance_cnt; i++)
+ for (j = 0; j < numa_distance_cnt; j++)
+ numa_distance[i * numa_distance_cnt + j] = i == j ?
+ LOCAL_DISTANCE : REMOTE_DISTANCE;
+
+ pr_debug("NUMA: Initialized distance table, cnt=%d\n",
+ numa_distance_cnt);
+
+ return 0;
+}
+
+/**
+ * numa_set_distance - Set NUMA distance from one NUMA to another
+ * @from: the 'from' node to set distance
+ * @to: the 'to' node to set distance
+ * @distance: NUMA distance
+ *
+ * Set the distance from node @from to @to to @distance. If distance table
+ * doesn't exist, one which is large enough to accommodate all the currently
+ * known nodes will be created.
+ *
+ * If such table cannot be allocated, a warning is printed and further
+ * calls are ignored until the distance table is reset with
+ * numa_reset_distance().
+ *
+ * If @from or @to is higher than the highest known node or lower than zero
+ * at the time of table creation or @distance doesn't make sense, the call
+ * is ignored.
+ * This is to allow simplification of specific NUMA config implementations.
+ */
+void __init numa_set_distance(int from, int to, int distance)
+{
+ if (!numa_distance)
+ return;
+
+ if (from >= numa_distance_cnt || to >= numa_distance_cnt ||
+ from < 0 || to < 0) {
+ pr_warn_once("NUMA: Warning: node ids are out of bound, from=%d to=%d distance=%d\n",
+ from, to, distance);
+ return;
+ }
+
+ if ((u8)distance != distance ||
+ (from == to && distance != LOCAL_DISTANCE)) {
+ pr_warn_once("NUMA: Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
+ from, to, distance);
+ return;
+ }
+
+ numa_distance[from * numa_distance_cnt + to] = distance;
+}
+EXPORT_SYMBOL(numa_set_distance);
+
+int __node_distance(int from, int to)
+{
+ if (from >= numa_distance_cnt || to >= numa_distance_cnt)
+ return from == to ? LOCAL_DISTANCE : REMOTE_DISTANCE;
+ return numa_distance[from * numa_distance_cnt + to];
+}
+EXPORT_SYMBOL(__node_distance);
+
+static int __init numa_register_nodes(void)
+{
+ int nid;
+ struct memblock_region *mblk;
+
+ /* Check that valid nid is set to memblks */
+ for_each_memblock(memory, mblk)
+ if (mblk->nid == NUMA_NO_NODE || mblk->nid >= MAX_NUMNODES)
+ return -EINVAL;
+
+ /* Finally register nodes. */
+ for_each_node_mask(nid, numa_nodes_parsed) {
+ unsigned long start_pfn, end_pfn;
+
+ get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
+ setup_node_data(nid, start_pfn, end_pfn);
+ node_set_online(nid);
+ }
+
+ /* Setup online nodes to actual nodes*/
+ node_possible_map = numa_nodes_parsed;
+
+ return 0;
+}
+
+static int __init numa_init(int (*init_func)(void))
+{
+ int ret;
+
+ nodes_clear(numa_nodes_parsed);
+ nodes_clear(node_possible_map);
+ nodes_clear(node_online_map);
+ numa_reset_distance();
+
+ ret = init_func();
+ if (ret < 0)
+ return ret;
+
+ if (nodes_empty(numa_nodes_parsed))
+ return -EINVAL;
+
+ ret = numa_register_nodes();
+ if (ret < 0)
+ return ret;
+
+ ret = numa_alloc_distance();
+ if (ret < 0)
+ return ret;
+
+ setup_node_to_cpumask_map();
+
+ /* init boot processor */
+ cpu_to_node_map[0] = 0;
+ map_cpu_to_node(0, 0);
+
+ return 0;
+}
+
+/**
+ * dummy_numa_init - Fallback dummy NUMA init
+ *
+ * Used if there's no underlying NUMA architecture, NUMA initialization
+ * fails, or NUMA is disabled on the command line.
+ *
+ * Must online at least one node (node 0) and add memory blocks that cover all
+ * allowed memory. It is unlikely that this function fails.
+ */
+static int __init dummy_numa_init(void)
+{
+ int ret;
+ struct memblock_region *mblk;
+
+ pr_info("%s\n", "No NUMA configuration found");
+ pr_info("Faking a node at [mem %#018Lx-%#018Lx]\n",
+ 0LLU, PFN_PHYS(max_pfn) - 1);
+
+ for_each_memblock(memory, mblk) {
+ ret = numa_add_memblk(0, mblk->base, mblk->size);
+ if (unlikely(ret < 0))
+ return ret;
+ }
+
+ numa_off = 1;
+ return 0;
+}
+
+/**
+ * arm64_numa_init - Initialize NUMA
+ *
+ * Try each configured NUMA initialization method until one succeeds. The
+ * last fallback is dummy single node config encomapssing whole memory and
+ * never fails.
+ */
+void __init arm64_numa_init(void)
+{
+ int ret = -ENODEV;
+
+ if (!numa_off)
+ ret = numa_init(acpi_disabled ? arm64_of_numa_init : arm64_acpi_numa_init);
+
+ if (ret)
+ numa_init(dummy_numa_init);
+}
diff --git a/arch/cris/arch-v32/drivers/pci/bios.c b/arch/cris/arch-v32/drivers/pci/bios.c
index 64a5fb93767d..2b7b79f7447e 100644
--- a/arch/cris/arch-v32/drivers/pci/bios.c
+++ b/arch/cris/arch-v32/drivers/pci/bios.c
@@ -80,9 +80,10 @@ int pcibios_enable_resources(struct pci_dev *dev, int mask)
return 0;
}
-int pcibios_enable_irq(struct pci_dev *dev)
+int pcibios_enable_irq(struct pci_dev *dev, u8 slot, u8 pin)
{
- dev->irq = EXT_INTR_VECT;
+ if (!dev->msi_enabled)
+ dev->irq = EXT_INTR_VECT;
return 0;
}
@@ -92,8 +93,11 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
if ((err = pcibios_enable_resources(dev, mask)) < 0)
return err;
+ return 0;
+}
- if (!dev->msi_enabled)
- pcibios_enable_irq(dev);
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->map_irq = pcibios_enable_irq;
return 0;
}
diff --git a/arch/frv/mb93090-mb00/pci-frv.h b/arch/frv/mb93090-mb00/pci-frv.h
index d51992ff5a61..1537700c04b0 100644
--- a/arch/frv/mb93090-mb00/pci-frv.h
+++ b/arch/frv/mb93090-mb00/pci-frv.h
@@ -28,5 +28,4 @@ extern struct pci_ops *__nongpreldata pci_root_ops;
extern unsigned int pcibios_irq_mask;
void pcibios_irq_init(void);
-void pcibios_fixup_irqs(void);
void pcibios_enable_irq(struct pci_dev *dev);
diff --git a/arch/frv/mb93090-mb00/pci-irq.c b/arch/frv/mb93090-mb00/pci-irq.c
index 1c35c93f942b..2fde211499ac 100644
--- a/arch/frv/mb93090-mb00/pci-irq.c
+++ b/arch/frv/mb93090-mb00/pci-irq.c
@@ -40,19 +40,19 @@ void __init pcibios_irq_init(void)
{
}
-void __init pcibios_fixup_irqs(void)
+int pcibios_map_irq(struct pci_dev *dev, uint8_t slot, uint8_t pin)
{
- struct pci_dev *dev = NULL;
- uint8_t line, pin;
-
- for_each_pci_dev(dev) {
- pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
- if (pin) {
- dev->irq = pci_bus0_irq_routing[PCI_SLOT(dev->devfn)][pin - 1];
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
- }
- pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
- }
+ int irq;
+
+ irq = pci_bus0_irq_routing[PCI_SLOT(dev->devfn)][pin - 1];
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+ return irq;
+}
+
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->map_irq = pcibios_map_irq;
+ return 0;
}
void pcibios_enable_irq(struct pci_dev *dev)
diff --git a/arch/frv/mb93090-mb00/pci-vdk.c b/arch/frv/mb93090-mb00/pci-vdk.c
index f211839e2cae..22d59ed70e73 100644
--- a/arch/frv/mb93090-mb00/pci-vdk.c
+++ b/arch/frv/mb93090-mb00/pci-vdk.c
@@ -387,7 +387,6 @@ int __init pcibios_init(void)
bus = pci_scan_root_bus(NULL, 0, pci_root_ops, NULL, &resources);
pcibios_irq_init();
- pcibios_fixup_irqs();
pcibios_resource_survey();
if (!bus)
return 0;
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index eb0249e37981..1285cef47211 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -17,6 +17,7 @@ config IA64
select ACPI if (!IA64_HP_SIM)
select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI
select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
+ select ACPI_HAS_NUMA_ARCH_FIXUP if ACPI
select HAVE_UNSTABLE_SCHED_CLOCK
select HAVE_IDE
select HAVE_OPROFILE
diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c
index a6d6190c9d24..78e4444dea5c 100644
--- a/arch/ia64/hp/common/sba_iommu.c
+++ b/arch/ia64/hp/common/sba_iommu.c
@@ -1981,7 +1981,7 @@ sba_connect_bus(struct pci_bus *bus)
if (PCI_CONTROLLER(bus)->iommu)
return;
- handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion);
+ handle = acpi_device_handle(ACPI_COMPANION(bus->bridge));
if (!handle)
return;
diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h
index 07039d168f37..50507481d599 100644
--- a/arch/ia64/include/asm/pci.h
+++ b/arch/ia64/include/asm/pci.h
@@ -65,7 +65,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
#define pci_legacy_write platform_pci_legacy_write
struct pci_controller {
- struct acpi_device *companion;
void *iommu;
int segment;
int node; /* nearest node with memory or NUMA_NO_NODE for global allocation */
diff --git a/arch/ia64/include/asm/topology.h b/arch/ia64/include/asm/topology.h
index 3ad8f6988363..2778eb68b615 100644
--- a/arch/ia64/include/asm/topology.h
+++ b/arch/ia64/include/asm/topology.h
@@ -58,10 +58,6 @@ void build_cpu_to_node_map(void);
extern void arch_fix_phys_package_id(int num, u32 slot);
-#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
- cpu_all_mask : \
- cpumask_of_node(pcibus_to_node(bus)))
-
#include <asm-generic/topology.h>
#endif /* _ASM_IA64_TOPOLOGY_H */
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c
index 8f6ac2f8ae4c..c75356bd319d 100644
--- a/arch/ia64/pci/pci.c
+++ b/arch/ia64/pci/pci.c
@@ -154,7 +154,7 @@ static int add_io_space(struct device *dev, struct pci_root_info *info,
struct resource_entry *iospace;
struct resource *resource, *res = entry->res;
char *name;
- unsigned long base, min, max, base_port;
+ unsigned long base_mmio, base_port;
unsigned int sparse = 0, space_nr, len;
len = strlen(info->common.name) + 32;
@@ -172,12 +172,10 @@ static int add_io_space(struct device *dev, struct pci_root_info *info,
goto free_resource;
name = (char *)(iospace + 1);
- min = res->start - entry->offset;
- max = res->end - entry->offset;
- base = __pa(io_space[space_nr].mmio_base);
+ base_mmio = __pa(io_space[space_nr].mmio_base);
base_port = IO_SPACE_BASE(space_nr);
snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->common.name,
- base_port + min, base_port + max);
+ base_port + res->start, base_port + res->end);
/*
* The SDM guarantees the legacy 0-64K space is sparse, but if the
@@ -190,19 +188,27 @@ static int add_io_space(struct device *dev, struct pci_root_info *info,
resource = iospace->res;
resource->name = name;
resource->flags = IORESOURCE_MEM;
- resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min);
- resource->end = base + (sparse ? IO_SPACE_SPARSE_ENCODING(max) : max);
+ resource->start = base_mmio;
+ resource->end = base_mmio;
+ if (sparse) {
+ resource->start += IO_SPACE_SPARSE_ENCODING(res->start);
+ resource->end += IO_SPACE_SPARSE_ENCODING(res->end);
+ } else {
+ resource->start += res->start;
+ resource->end += res->end;
+ }
if (insert_resource(&iomem_resource, resource)) {
dev_err(dev,
"can't allocate host bridge io space resource %pR\n",
resource);
goto free_resource;
}
+ resource_list_add_tail(iospace, &info->io_resources);
+ /* Adjust base of original IO port resource descriptor */
entry->offset = base_port;
- res->start = min + base_port;
- res->end = max + base_port;
- resource_list_add_tail(iospace, &info->io_resources);
+ res->start += base_port;
+ res->end += base_port;
return 0;
@@ -301,28 +307,12 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
}
info->controller.segment = root->segment;
- info->controller.companion = device;
info->controller.node = acpi_get_node(device->handle);
INIT_LIST_HEAD(&info->io_resources);
return acpi_pci_root_create(root, &pci_acpi_root_ops,
&info->common, &info->controller);
}
-int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
-{
- /*
- * We pass NULL as parent to pci_create_root_bus(), so if it is not NULL
- * here, pci_create_root_bus() has been called by someone else and
- * sysdata is likely to be different from what we expect. Let it go in
- * that case.
- */
- if (!bridge->dev.parent) {
- struct pci_controller *controller = bridge->bus->sysdata;
- ACPI_COMPANION_SET(&bridge->dev, controller->companion);
- }
- return 0;
-}
-
void pcibios_fixup_device_resources(struct pci_dev *dev)
{
int idx;
@@ -374,16 +364,6 @@ void pcibios_fixup_bus(struct pci_bus *b)
platform_pci_fixup_bus(b);
}
-void pcibios_add_bus(struct pci_bus *bus)
-{
- acpi_pci_add_bus(bus);
-}
-
-void pcibios_remove_bus(struct pci_bus *bus)
-{
- acpi_pci_remove_bus(bus);
-}
-
void pcibios_set_master (struct pci_dev *dev)
{
/* No special bus mastering setup handling */
diff --git a/arch/ia64/sn/kernel/io_acpi_init.c b/arch/ia64/sn/kernel/io_acpi_init.c
index 0640739cc20c..bcfddc26eb35 100644
--- a/arch/ia64/sn/kernel/io_acpi_init.c
+++ b/arch/ia64/sn/kernel/io_acpi_init.c
@@ -132,7 +132,7 @@ sn_get_bussoft_ptr(struct pci_bus *bus)
struct acpi_resource_vendor_typed *vendor;
- handle = acpi_device_handle(PCI_CONTROLLER(bus)->companion);
+ handle = acpi_device_handle(ACPI_COMPANION(bus->bridge));
status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
&sn_uuid, &buffer);
if (ACPI_FAILURE(status)) {
@@ -360,7 +360,7 @@ sn_acpi_get_pcidev_info(struct pci_dev *dev, struct pcidev_info **pcidev_info,
acpi_status status;
struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- rootbus_handle = acpi_device_handle(PCI_CONTROLLER(dev)->companion);
+ rootbus_handle = acpi_device_handle(ACPI_COMPANION(dev->bus->bridge));
status = acpi_evaluate_integer(rootbus_handle, METHOD_NAME__SEG, NULL,
&segment);
if (ACPI_SUCCESS(status)) {
diff --git a/arch/m68k/coldfire/pci.c b/arch/m68k/coldfire/pci.c
index 821de928dc3f..86661cda7b0a 100644
--- a/arch/m68k/coldfire/pci.c
+++ b/arch/m68k/coldfire/pci.c
@@ -319,11 +319,17 @@ static int __init mcf_pci_init(void)
rootbus->resource[0] = &mcf_pci_io;
rootbus->resource[1] = &mcf_pci_mem;
- pci_fixup_irqs(pci_common_swizzle, mcf_pci_map_irq);
pci_bus_size_bridges(rootbus);
pci_bus_assign_resources(rootbus);
pci_bus_add_devices(rootbus);
return 0;
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = mcf_pci_map_irq;
+ return 0;
+}
+
subsys_initcall(mcf_pci_init);
diff --git a/arch/metag/include/asm/topology.h b/arch/metag/include/asm/topology.h
index e95f874ded1b..b2851963d207 100644
--- a/arch/metag/include/asm/topology.h
+++ b/arch/metag/include/asm/topology.h
@@ -9,9 +9,6 @@
#define cpumask_of_node(node) ((void)node, cpu_online_mask)
#define pcibus_to_node(bus) ((void)(bus), -1)
-#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
- cpu_all_mask : \
- cpumask_of_node(pcibus_to_node(bus)))
#endif
diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c
index ae838ed5fcf2..99ea108514ec 100644
--- a/arch/microblaze/pci/pci-common.c
+++ b/arch/microblaze/pci/pci-common.c
@@ -855,12 +855,15 @@ void pcibios_setup_bus_devices(struct pci_bus *bus)
* code and is needed by the DMA init
*/
set_dev_node(&dev->dev, pcibus_to_node(dev->bus));
-
- /* Read default IRQs and fixup if necessary */
- dev->irq = of_irq_parse_and_map_pci(dev, 0, 0);
}
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->map_irq = of_irq_parse_and_map_pci;
+ return 0;
+}
+
void pcibios_fixup_bus(struct pci_bus *bus)
{
/* When called from the generic PCI probe, read PCI<->PCI bridge
diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c
index b8a0bf5766f2..137d3fea5b9c 100644
--- a/arch/mips/pci/pci.c
+++ b/arch/mips/pci/pci.c
@@ -247,13 +247,18 @@ static int __init pcibios_init(void)
for (hose = hose_head; hose; hose = hose->next)
pcibios_scanbus(hose);
- pci_fixup_irqs(pci_common_swizzle, pcibios_map_irq);
-
pci_initialized = 1;
return 0;
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = pcibios_map_irq;
+ return 0;
+}
+
subsys_initcall(pcibios_init);
static int pcibios_enable_resources(struct pci_dev *dev, int mask)
diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.h b/arch/mn10300/unit-asb2305/pci-asb2305.h
index 96c484b12226..b7d55267a1b6 100644
--- a/arch/mn10300/unit-asb2305/pci-asb2305.h
+++ b/arch/mn10300/unit-asb2305/pci-asb2305.h
@@ -60,9 +60,6 @@ struct irq_routing_table {
} __attribute__((packed));
extern unsigned int pcibios_irq_mask;
-
-extern void pcibios_irq_init(void);
-extern void pcibios_fixup_irqs(void);
-extern void pcibios_enable_irq(struct pci_dev *dev);
+extern int pci_map_irq(struct pci_dev *, u8 slot, u8 pin);
#endif /* PCI_ASB2305_H */
diff --git a/arch/mn10300/unit-asb2305/pci-irq.c b/arch/mn10300/unit-asb2305/pci-irq.c
index fcb28ceb824d..d7d2ce92caf7 100644
--- a/arch/mn10300/unit-asb2305/pci-irq.c
+++ b/arch/mn10300/unit-asb2305/pci-irq.c
@@ -20,27 +20,11 @@
#include <asm/smp.h>
#include "pci-asb2305.h"
-void __init pcibios_irq_init(void)
+int pci_map_irq(struct pci_dev *, u8 slot, u8 pin)
{
-}
-
-void __init pcibios_fixup_irqs(void)
-{
- struct pci_dev *dev = NULL;
- u8 line, pin;
+ u8 line;
- for_each_pci_dev(dev) {
- pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
- if (pin) {
- dev->irq = XIRQ1;
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
- dev->irq);
- }
- pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
- }
-}
-
-void pcibios_enable_irq(struct pci_dev *dev)
-{
+ dev->irq = XIRQ1;
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
}
diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c
index 3dfe2d31c67b..c9fc19dba9a9 100644
--- a/arch/mn10300/unit-asb2305/pci.c
+++ b/arch/mn10300/unit-asb2305/pci.c
@@ -375,14 +375,17 @@ static int __init pcibios_init(void)
bus = pci_scan_root_bus(NULL, 0, &pci_direct_ampci, NULL, &resources);
if (!bus)
return 0;
-
- pcibios_irq_init();
- pcibios_fixup_irqs();
pcibios_resource_survey();
pci_bus_add_devices(bus);
return 0;
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->map_irq = pci_map_irq;
+ return 0;
+}
+
arch_initcall(pcibios_init);
char *__init pcibios_setup(char *str)
@@ -395,16 +398,6 @@ char *__init pcibios_setup(char *str)
return str;
}
-int pcibios_enable_device(struct pci_dev *dev, int mask)
-{
- int err;
-
- err = pci_enable_resources(dev, mask);
- if (err == 0)
- pcibios_enable_irq(dev);
- return err;
-}
-
/*
* disable the ethernet chipset
*/
diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h
index 8b3b46b7b0f2..eee025d23b36 100644
--- a/arch/powerpc/include/asm/topology.h
+++ b/arch/powerpc/include/asm/topology.h
@@ -32,10 +32,6 @@ static inline int pcibus_to_node(struct pci_bus *bus)
}
#endif
-#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
- cpu_all_mask : \
- cpumask_of_node(pcibus_to_node(bus)))
-
extern int __node_distance(int, int);
#define node_distance(a, b) __node_distance(a, b)
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 0f7a60f1e9f6..9fe033af98ad 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -232,7 +232,7 @@ struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node)
* If the interrupt is used, then gets the interrupt line from the
* openfirmware and sets it in the pci_dev and pci_config line.
*/
-static int pci_read_irq_line(struct pci_dev *pci_dev)
+static int pci_read_irq_line(struct pci_dev *pci_dev, u8 pin)
{
struct of_phandle_args oirq;
unsigned int virq;
@@ -244,7 +244,7 @@ static int pci_read_irq_line(struct pci_dev *pci_dev)
#endif
/* Try to get a mapping from the device-tree */
if (of_irq_parse_pci(pci_dev, &oirq)) {
- u8 line, pin;
+ u8 line;
/* If that fails, lets fallback to what is in the config
* space and map that through the default controller. We
@@ -253,10 +253,6 @@ static int pci_read_irq_line(struct pci_dev *pci_dev)
* either provide a proper interrupt tree or don't use this
* function.
*/
- if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &pin))
- return -1;
- if (pin == 0)
- return -1;
if (pci_read_config_byte(pci_dev, PCI_INTERRUPT_LINE, &line) ||
line == 0xff || line == 0) {
return -1;
@@ -281,9 +277,16 @@ static int pci_read_irq_line(struct pci_dev *pci_dev)
pr_debug(" Mapped to linux irq %d\n", virq);
- pci_dev->irq = virq;
+ return virq;
+}
- return 0;
+static int pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ /* Read default IRQs and fixup if necessary */
+ dev->irq = pci_read_irq_line(dev, pin);
+ if (ppc_md.pci_irq_fixup)
+ ppc_md.pci_irq_fixup(dev);
+ return dev->irq;
}
/*
@@ -785,6 +788,7 @@ int pci_proc_domain(struct pci_bus *bus)
int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
{
+ bridge->map_irq = pci_map_irq;
if (ppc_md.pcibios_root_bridge_prepare)
return ppc_md.pcibios_root_bridge_prepare(bridge);
@@ -984,11 +988,6 @@ static void pcibios_setup_device(struct pci_dev *dev)
phb = pci_bus_to_host(dev->bus);
if (phb->controller_ops.dma_dev_setup)
phb->controller_ops.dma_dev_setup(dev);
-
- /* Read default IRQs and fixup if necessary */
- pci_read_irq_line(dev);
- if (ppc_md.pci_irq_fixup)
- ppc_md.pci_irq_fixup(dev);
}
int pcibios_add_device(struct pci_dev *dev)
diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index c873e682b67f..539fb2dbd694 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -205,7 +205,7 @@ static inline int __pcibus_to_node(const struct pci_bus *bus)
}
static inline const struct cpumask *
-cpumask_of_pcibus(const struct pci_bus *bus)
+__cpumask_of_pcibus(const struct pci_bus *bus)
{
return cpu_online_mask;
}
diff --git a/arch/s390/include/asm/topology.h b/arch/s390/include/asm/topology.h
index 94fc55fc72ce..280eafc6ab7b 100644
--- a/arch/s390/include/asm/topology.h
+++ b/arch/s390/include/asm/topology.h
@@ -78,6 +78,7 @@ static inline const struct cpumask *cpumask_of_node(int node)
#define parent_node(node) (node)
#define pcibus_to_node(bus) __pcibus_to_node(bus)
+#define cpumask_of_pcibus(bus) __cpumask_of_pcibus(bus)
#define node_distance(a, b) __node_distance(a, b)
diff --git a/arch/sh/drivers/pci/fixups-cayman.c b/arch/sh/drivers/pci/fixups-cayman.c
index edc2fb7a5bb2..32467884d6f7 100644
--- a/arch/sh/drivers/pci/fixups-cayman.c
+++ b/arch/sh/drivers/pci/fixups-cayman.c
@@ -5,7 +5,7 @@
#include <cpu/irq.h>
#include "pci-sh5.h"
-int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int result = -1;
diff --git a/arch/sh/drivers/pci/fixups-dreamcast.c b/arch/sh/drivers/pci/fixups-dreamcast.c
index 1d1c5a227e50..9d597f7ab8dd 100644
--- a/arch/sh/drivers/pci/fixups-dreamcast.c
+++ b/arch/sh/drivers/pci/fixups-dreamcast.c
@@ -76,7 +76,7 @@ static void gapspci_fixup_resources(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, gapspci_fixup_resources);
-int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
/*
* The interrupt routing semantics here are quite trivial.
diff --git a/arch/sh/drivers/pci/fixups-r7780rp.c b/arch/sh/drivers/pci/fixups-r7780rp.c
index 57ed3f09d0c2..2c9b58f848dd 100644
--- a/arch/sh/drivers/pci/fixups-r7780rp.c
+++ b/arch/sh/drivers/pci/fixups-r7780rp.c
@@ -15,7 +15,7 @@
#include <linux/sh_intc.h>
#include "pci-sh4.h"
-int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
return evt2irq(0xa20) + slot;
}
diff --git a/arch/sh/drivers/pci/fixups-rts7751r2d.c b/arch/sh/drivers/pci/fixups-rts7751r2d.c
index eaddb56c45c6..358ac104f08c 100644
--- a/arch/sh/drivers/pci/fixups-rts7751r2d.c
+++ b/arch/sh/drivers/pci/fixups-rts7751r2d.c
@@ -20,18 +20,18 @@
#define PCIMCR_MRSET_OFF 0xBFFFFFFF
#define PCIMCR_RFSH_OFF 0xFFFFFFFB
-static u8 rts7751r2d_irq_tab[] __initdata = {
+static u8 rts7751r2d_irq_tab[] = {
IRQ_PCI_INTA,
IRQ_PCI_INTB,
IRQ_PCI_INTC,
IRQ_PCI_INTD,
};
-static char lboxre2_irq_tab[] __initdata = {
+static char lboxre2_irq_tab[] = {
IRQ_ETH0, IRQ_ETH1, IRQ_INTA, IRQ_INTD,
};
-int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
if (mach_is_lboxre2())
return lboxre2_irq_tab[slot];
diff --git a/arch/sh/drivers/pci/fixups-sdk7780.c b/arch/sh/drivers/pci/fixups-sdk7780.c
index c0a015ae6ecf..24e96dfbdb22 100644
--- a/arch/sh/drivers/pci/fixups-sdk7780.c
+++ b/arch/sh/drivers/pci/fixups-sdk7780.c
@@ -22,7 +22,7 @@
#define IRQ_INTD evt2irq(0xa80)
/* IDSEL [16][17][18][19][20][21][22][23][24][25][26][27][28][29][30][31] */
-static char sdk7780_irq_tab[4][16] __initdata = {
+static char sdk7780_irq_tab[4][16] = {
/* INTA */
{ IRQ_INTA, IRQ_INTD, IRQ_INTC, IRQ_INTD, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1 },
@@ -37,7 +37,7 @@ static char sdk7780_irq_tab[4][16] __initdata = {
-1, -1, -1 },
};
-int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
return sdk7780_irq_tab[pin-1][slot];
}
diff --git a/arch/sh/drivers/pci/fixups-se7751.c b/arch/sh/drivers/pci/fixups-se7751.c
index 84a88ca92008..1cb8d0ac4fdb 100644
--- a/arch/sh/drivers/pci/fixups-se7751.c
+++ b/arch/sh/drivers/pci/fixups-se7751.c
@@ -7,7 +7,7 @@
#include <linux/sh_intc.h>
#include "pci-sh4.h"
-int __init pcibios_map_platform_irq(const struct pci_dev *, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *, u8 slot, u8 pin)
{
switch (slot) {
case 0: return evt2irq(0x3a0);
diff --git a/arch/sh/drivers/pci/fixups-sh03.c b/arch/sh/drivers/pci/fixups-sh03.c
index 16207bef9f52..55ac1ba2c74f 100644
--- a/arch/sh/drivers/pci/fixups-sh03.c
+++ b/arch/sh/drivers/pci/fixups-sh03.c
@@ -4,7 +4,7 @@
#include <linux/pci.h>
#include <linux/sh_intc.h>
-int __init pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int irq;
diff --git a/arch/sh/drivers/pci/fixups-snapgear.c b/arch/sh/drivers/pci/fixups-snapgear.c
index 6e33ba4cd076..a931e5928f58 100644
--- a/arch/sh/drivers/pci/fixups-snapgear.c
+++ b/arch/sh/drivers/pci/fixups-snapgear.c
@@ -19,7 +19,7 @@
#include <linux/sh_intc.h>
#include "pci-sh4.h"
-int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
int irq = -1;
diff --git a/arch/sh/drivers/pci/fixups-titan.c b/arch/sh/drivers/pci/fixups-titan.c
index bd1addb1b8be..a9d563e479d5 100644
--- a/arch/sh/drivers/pci/fixups-titan.c
+++ b/arch/sh/drivers/pci/fixups-titan.c
@@ -19,7 +19,7 @@
#include <mach/titan.h>
#include "pci-sh4.h"
-static char titan_irq_tab[] __initdata = {
+static char titan_irq_tab[] = {
TITAN_IRQ_WAN,
TITAN_IRQ_LAN,
TITAN_IRQ_MPCIA,
@@ -27,7 +27,7 @@ static char titan_irq_tab[] __initdata = {
TITAN_IRQ_USB,
};
-int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
int irq = titan_irq_tab[slot];
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
index d5462b7bc514..28f8c9083130 100644
--- a/arch/sh/drivers/pci/pci.c
+++ b/arch/sh/drivers/pci/pci.c
@@ -144,16 +144,20 @@ static int __init pcibios_init(void)
for (hose = hose_head; hose; hose = hose->next)
pcibios_scanbus(hose);
- pci_fixup_irqs(pci_common_swizzle, pcibios_map_platform_irq);
-
dma_debug_add_bus(&pci_bus_type);
-
pci_initialized = 1;
return 0;
}
subsys_initcall(pcibios_init);
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = pcibios_map_platform_irq;
+ return 0;
+}
+
/*
* Called after each bus is probed, but before its children
* are examined.
diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c
index a162a7f86b2e..0167a7352719 100644
--- a/arch/sh/drivers/pci/pcie-sh7786.c
+++ b/arch/sh/drivers/pci/pcie-sh7786.c
@@ -467,7 +467,7 @@ static int __init pcie_init(struct sh7786_pcie_port *port)
return 0;
}
-int __init pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
{
return evt2irq(0xae0);
}
diff --git a/arch/sh/include/asm/topology.h b/arch/sh/include/asm/topology.h
index b0a282d65f6a..357983d51f5f 100644
--- a/arch/sh/include/asm/topology.h
+++ b/arch/sh/include/asm/topology.h
@@ -9,9 +9,6 @@
#define cpumask_of_node(node) ((void)node, cpu_online_mask)
#define pcibus_to_node(bus) ((void)(bus), -1)
-#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
- cpu_all_mask : \
- cpumask_of_node(pcibus_to_node(bus)))
#endif
diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c
index 4371f72ff025..ca11fb8e861a 100644
--- a/arch/sparc/kernel/leon_pci.c
+++ b/arch/sparc/kernel/leon_pci.c
@@ -13,6 +13,8 @@
#include <asm/leon.h>
#include <asm/leon_pci.h>
+int (*leon_pci_map_irq)(const struct pci_dev *dev, u8 slot, u8 pin);
+
/* The LEON architecture does not rely on a BIOS or bootloader to setup
* PCI for us. The Linux generic routines are used to setup resources,
* reset values of configuration-space register settings are preserved.
@@ -39,14 +41,20 @@ void leon_pci_init(struct platform_device *ofdev, struct leon_pci_info *info)
return;
}
- /* Setup IRQs of all devices using custom routines */
- pci_fixup_irqs(pci_common_swizzle, info->map_irq);
+ leon_pci_map_irq = info->map_irq;
/* Assign devices with resources */
pci_assign_unassigned_resources();
pci_bus_add_devices(root_bus);
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = leon_pci_map_irq;
+ return 0;
+}
+
void pcibios_fixup_bus(struct pci_bus *pbus)
{
struct pci_dev *dev;
diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c
index badf0951d73c..f29f65993af0 100644
--- a/arch/sparc/kernel/pci.c
+++ b/arch/sparc/kernel/pci.c
@@ -337,10 +337,6 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
} else {
dev->hdr_type = PCI_HEADER_TYPE_NORMAL;
dev->rom_base_reg = PCI_ROM_ADDRESS;
-
- dev->irq = sd->op->archdata.irqs[0];
- if (dev->irq == 0xffffffff)
- dev->irq = PCI_IRQ_NONE;
}
pci_parse_of_addrs(sd->op, node, dev);
@@ -353,6 +349,25 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
return dev;
}
+int pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ int irq;
+ struct platform_device *op = of_find_device_by_node(dev->sysdata);
+
+ irq = op->archdata.irqs[0];
+ if (irq == 0xffffffff)
+ irq = PCI_IRQ_NONE;
+ return irq;
+}
+
+#ifndef CONFIG_LEON_PCI
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->map_irq = pci_map_irq;
+ return 0;
+}
+#endif
+
static void apb_calc_first_last(u8 map, u32 *first_p, u32 *last_p)
{
u32 idx, first, last;
diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c
index 9475a74cd53a..e151d80ab178 100644
--- a/arch/tile/kernel/pci.c
+++ b/arch/tile/kernel/pci.c
@@ -313,9 +313,6 @@ int __init pcibios_init(void)
}
}
- /* Do machine dependent PCI interrupt routing */
- pci_fixup_irqs(pci_common_swizzle, tile_map_irq);
-
/*
* This comes from the generic Linux PCI driver.
*
@@ -369,6 +366,13 @@ int __init pcibios_init(void)
}
subsys_initcall(pcibios_init);
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = tile_map_irq;
+ return 0;
+}
+
/*
* No bus fixups needed.
*/
diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c
index 4c017d0d2de8..7eb3e2bd2687 100644
--- a/arch/tile/kernel/pci_gx.c
+++ b/arch/tile/kernel/pci_gx.c
@@ -887,9 +887,6 @@ int __init pcibios_init(void)
next_busno = bus->busn_res.end + 1;
}
- /* Do machine dependent PCI interrupt routing */
- pci_fixup_irqs(pci_common_swizzle, tile_map_irq);
-
/*
* This comes from the generic Linux PCI driver.
*
@@ -1038,6 +1035,13 @@ alloc_mem_map_failed:
}
subsys_initcall(pcibios_init);
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = tile_map_irq;
+ return 0;
+}
+
/* No bus fixups needed. */
void pcibios_fixup_bus(struct pci_bus *bus)
{
diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c
index d45fa5f3e9c4..42295b20d9e2 100644
--- a/arch/unicore32/kernel/pci.c
+++ b/arch/unicore32/kernel/pci.c
@@ -101,7 +101,7 @@ void pci_puv3_preinit(void)
writel(readl(PCIBRI_CMD) | PCIBRI_CMD_IO | PCIBRI_CMD_MEM, PCIBRI_CMD);
}
-static int __init pci_puv3_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+static int pci_puv3_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (dev->bus->number == 0) {
#ifdef CONFIG_ARCH_FPGA /* 4 pci slots */
@@ -263,8 +263,6 @@ static int __init pci_common_init(void)
if (!puv3_bus)
panic("PCI: unable to scan bus!");
- pci_fixup_irqs(pci_common_swizzle, pci_puv3_map_irq);
-
if (!pci_has_flag(PCI_PROBE_ONLY)) {
pci_bus_size_bridges(puv3_bus);
pci_bus_assign_resources(puv3_bus);
@@ -274,6 +272,13 @@ static int __init pci_common_init(void)
}
subsys_initcall(pci_common_init);
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = pci_puv3_map_irq;
+ return 0;
+}
+
char * __init pcibios_setup(char *str)
{
if (!strcmp(str, "debug")) {
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index db3622f22b61..0338cdd9d962 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -30,6 +30,7 @@ config X86
select ARCH_HAS_PMEM_API if X86_64
select ARCH_HAS_MMIO_FLUSH
select ARCH_HAS_SG_CHAIN
+ select ARCH_HAS_CUSTOM_PCI_ECAM if X86_32
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
select ARCH_MIGHT_HAVE_PC_PARPORT
@@ -74,6 +75,7 @@ config X86
select GENERIC_TIME_VSYSCALL
select HAVE_ACPI_APEI if ACPI
select HAVE_ACPI_APEI_NMI if ACPI
+ select HAVE_ACPI_APEI_HEST_IA32 if ACPI
select HAVE_ALIGNED_STRUCT_PAGE if SLUB
select HAVE_AOUT if X86_32
select HAVE_ARCH_AUDITSYSCALL
@@ -128,6 +130,7 @@ config X86
select HAVE_MIXED_BREAKPOINTS_REGS
select HAVE_OPROFILE
select HAVE_OPTPROBES
+ select HAVE_PCI_ECAM
select HAVE_PCSPKR_PLATFORM
select HAVE_PERF_EVENTS
select HAVE_PERF_EVENTS_NMI
@@ -2365,6 +2368,7 @@ config PCI_DIRECT
config PCI_MMCONFIG
def_bool y
+ select PCI_ECAM
depends on X86_32 && PCI && (ACPI || SFI) && (PCI_GOMMCONFIG || PCI_GOANY)
config PCI_OLPC
@@ -2382,6 +2386,7 @@ config PCI_DOMAINS
config PCI_MMCONFIG
bool "Support mmconfig PCI config space access"
+ select PCI_ECAM
depends on X86_64 && PCI && ACPI
config PCI_CNB20LE_QUIRK
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
index 462594320d39..fcbce830cfe1 100644
--- a/arch/x86/include/asm/pci.h
+++ b/arch/x86/include/asm/pci.h
@@ -14,9 +14,6 @@
struct pci_sysdata {
int domain; /* PCI domain */
int node; /* NUMA node */
-#ifdef CONFIG_ACPI
- struct acpi_device *companion; /* ACPI companion device */
-#endif
#ifdef CONFIG_X86_64
void *iommu; /* IOMMU private data */
#endif
@@ -121,7 +118,7 @@ static inline int __pcibus_to_node(const struct pci_bus *bus)
}
static inline const struct cpumask *
-cpumask_of_pcibus(const struct pci_bus *bus)
+__cpumask_of_pcibus(const struct pci_bus *bus)
{
int node;
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index fa1195dae425..be091b47ad95 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -90,6 +90,7 @@ extern unsigned int pcibios_irq_mask;
extern raw_spinlock_t pci_config_lock;
+extern int pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin);
extern int (*pcibios_enable_irq)(struct pci_dev *dev);
extern void (*pcibios_disable_irq)(struct pci_dev *dev);
@@ -119,36 +120,16 @@ extern int __init pci_acpi_init(void);
extern void __init pcibios_irq_init(void);
extern int __init pcibios_init(void);
extern int pci_legacy_init(void);
-extern void pcibios_fixup_irqs(void);
+extern int pcibios_fixup_irq(struct pci_dev *dev, u8 pin);
/* pci-mmconfig.c */
-
-/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
-#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
-
-struct pci_mmcfg_region {
- struct list_head list;
- struct resource res;
- u64 address;
- char __iomem *virt;
- u16 segment;
- u8 start_bus;
- u8 end_bus;
- char name[PCI_MMCFG_RESOURCE_NAME_LEN];
-};
-
-extern int __init pci_mmcfg_arch_init(void);
-extern void __init pci_mmcfg_arch_free(void);
-extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
-extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
phys_addr_t addr);
-extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
-extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
-
-extern struct list_head pci_mmcfg_list;
-#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
+int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn,
+ int reg, int len, u32 *value);
+int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn,
+ int reg, int len, u32 value);
/*
* AMD Fam10h CPUs are buggy, and cannot access MMIO config space
@@ -200,9 +181,9 @@ static inline void mmio_config_writel(void __iomem *pos, u32 val)
# define x86_default_pci_init pci_legacy_init
# endif
# define x86_default_pci_init_irq pcibios_irq_init
-# define x86_default_pci_fixup_irqs pcibios_fixup_irqs
+# define x86_default_pci_fixup_irq pcibios_fixup_irq
#else
# define x86_default_pci_init NULL
# define x86_default_pci_init_irq NULL
-# define x86_default_pci_fixup_irqs NULL
+# define x86_default_pci_fixup_irq NULL
#endif
diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h
index 0fb46482dfde..980f6eea5bbf 100644
--- a/arch/x86/include/asm/topology.h
+++ b/arch/x86/include/asm/topology.h
@@ -91,6 +91,7 @@ extern void setup_node_to_cpumask_map(void);
#define parent_node(node) (node)
#define pcibus_to_node(bus) __pcibus_to_node(bus)
+#define cpumask_of_pcibus(bus) __cpumask_of_pcibus(bus)
extern int __node_distance(int, int);
#define node_distance(a, b) __node_distance(a, b)
diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
index 3839628d962e..810f1dd37d92 100644
--- a/arch/x86/kernel/x86_init.c
+++ b/arch/x86/kernel/x86_init.c
@@ -80,7 +80,6 @@ struct x86_init_ops x86_init __initdata = {
.pci = {
.init = x86_default_pci_init,
.init_irq = x86_default_pci_init_irq,
- .fixup_irqs = x86_default_pci_fixup_irqs,
},
};
diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c
index c2aea63bee20..92a640a3d259 100644
--- a/arch/x86/mm/srat.c
+++ b/arch/x86/mm/srat.c
@@ -42,33 +42,6 @@ static __init inline int srat_disabled(void)
return acpi_numa < 0;
}
-/*
- * Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for
- * I/O localities since SRAT does not list them. I/O localities are
- * not supported at this point.
- */
-void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
-{
- int i, j;
-
- for (i = 0; i < slit->locality_count; i++) {
- const int from_node = pxm_to_node(i);
-
- if (from_node == NUMA_NO_NODE)
- continue;
-
- for (j = 0; j < slit->locality_count; j++) {
- const int to_node = pxm_to_node(j);
-
- if (to_node == NUMA_NO_NODE)
- continue;
-
- numa_set_distance(from_node, to_node,
- slit->entry[slit->locality_count * i + j]);
- }
- }
-}
-
/* Callback for Proximity Domain -> x2APIC mapping */
void __init
acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
@@ -210,8 +183,6 @@ out_err:
return -1;
}
-void __init acpi_numa_arch_fixup(void) {}
-
int __init x86_acpi_numa_init(void)
{
int ret;
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 3cd69832d7f4..c4d79d2e35f5 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -5,17 +5,13 @@
#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/pci-acpi.h>
+#include <linux/ecam.h>
#include <asm/numa.h>
#include <asm/pci_x86.h>
struct pci_root_info {
struct acpi_pci_root_info common;
struct pci_sysdata sd;
-#ifdef CONFIG_PCI_MMCONFIG
- bool mcfg_added;
- u8 start_bus;
- u8 end_bus;
-#endif
};
static bool pci_use_crs = true;
@@ -179,16 +175,13 @@ static int check_segment(u16 seg, struct device *dev, char *estr)
static int setup_mcfg_map(struct acpi_pci_root_info *ci)
{
- int result, seg;
- struct pci_root_info *info;
+ int result, seg, start, end;
struct acpi_pci_root *root = ci->root;
struct device *dev = &ci->bridge->dev;
- info = container_of(ci, struct pci_root_info, common);
- info->start_bus = (u8)root->secondary.start;
- info->end_bus = (u8)root->secondary.end;
- info->mcfg_added = false;
- seg = info->sd.domain;
+ seg = root->segment;
+ start = root->secondary.start;
+ end = root->secondary.end;
/* return success if MMCFG is not in use */
if (raw_pci_ext_ops && raw_pci_ext_ops != &pci_mmcfg)
@@ -197,40 +190,22 @@ static int setup_mcfg_map(struct acpi_pci_root_info *ci)
if (!(pci_probe & PCI_PROBE_MMCONF))
return check_segment(seg, dev, "MMCONFIG is disabled,");
- result = pci_mmconfig_insert(dev, seg, info->start_bus, info->end_bus,
- root->mcfg_addr);
+ result = pci_mmconfig_insert(dev, seg, start, end, root->mcfg_addr);
if (result == 0) {
/* enable MMCFG if it hasn't been enabled yet */
if (raw_pci_ext_ops == NULL)
raw_pci_ext_ops = &pci_mmcfg;
- info->mcfg_added = true;
} else if (result != -EEXIST)
return check_segment(seg, dev,
"fail to add MMCONFIG information,");
return 0;
}
-
-static void teardown_mcfg_map(struct acpi_pci_root_info *ci)
-{
- struct pci_root_info *info;
-
- info = container_of(ci, struct pci_root_info, common);
- if (info->mcfg_added) {
- pci_mmconfig_delete(info->sd.domain,
- info->start_bus, info->end_bus);
- info->mcfg_added = false;
- }
-}
#else
static int setup_mcfg_map(struct acpi_pci_root_info *ci)
{
return 0;
}
-
-static void teardown_mcfg_map(struct acpi_pci_root_info *ci)
-{
-}
#endif
static int pci_acpi_root_get_node(struct acpi_pci_root *root)
@@ -258,7 +233,7 @@ static int pci_acpi_root_init_info(struct acpi_pci_root_info *ci)
static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
{
- teardown_mcfg_map(ci);
+ pci_mmcfg_teardown_map(ci);
kfree(container_of(ci, struct pci_root_info, common));
}
@@ -340,7 +315,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
struct pci_sysdata sd = {
.domain = domain,
.node = node,
- .companion = root->device
};
memcpy(bus->sysdata, &sd, sizeof(sd));
@@ -355,7 +329,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
else {
info->sd.domain = domain;
info->sd.node = node;
- info->sd.companion = root->device;
bus = acpi_pci_root_create(root, &acpi_pci_root_ops,
&info->common, &info->sd);
}
@@ -373,21 +346,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
return bus;
}
-int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
-{
- /*
- * We pass NULL as parent to pci_create_root_bus(), so if it is not NULL
- * here, pci_create_root_bus() has been called by someone else and
- * sysdata is likely to be different from what we expect. Let it go in
- * that case.
- */
- if (!bridge->dev.parent) {
- struct pci_sysdata *sd = bridge->bus->sysdata;
- ACPI_COMPANION_SET(&bridge->dev, sd->companion);
- }
- return 0;
-}
-
int __init pci_acpi_init(void)
{
struct pci_dev *dev = NULL;
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index eccd4d99e6a4..ed3236dfcb43 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -171,16 +171,6 @@ void pcibios_fixup_bus(struct pci_bus *b)
pcibios_fixup_device_resources(dev);
}
-void pcibios_add_bus(struct pci_bus *bus)
-{
- acpi_pci_add_bus(bus);
-}
-
-void pcibios_remove_bus(struct pci_bus *bus)
-{
- acpi_pci_remove_bus(bus);
-}
-
/*
* Only use DMI information to set this if nothing was passed
* on the kernel command line (which was parsed earlier).
diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c
index 32e70343e6fd..cc62226529d8 100644
--- a/arch/x86/pci/irq.c
+++ b/arch/x86/pci/irq.c
@@ -876,9 +876,8 @@ static struct irq_info *pirq_get_info(struct pci_dev *dev)
return NULL;
}
-static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
+static int pcibios_lookup_irq(struct pci_dev *dev, u8 pin, int assign)
{
- u8 pin;
struct irq_info *info;
int i, pirq, newirq;
int irq = 0;
@@ -888,7 +887,6 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
char *msg = NULL;
/* Find IRQ pin */
- pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (!pin) {
dev_dbg(&dev->dev, "no interrupt pin\n");
return 0;
@@ -1021,47 +1019,38 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
return 1;
}
-void __init pcibios_fixup_irqs(void)
+int pcibios_fixup_irq(struct pci_dev *dev, u8 pin)
{
- struct pci_dev *dev = NULL;
- u8 pin;
-
+ int irq = dev->irq;
DBG(KERN_DEBUG "PCI: IRQ fixup\n");
- for_each_pci_dev(dev) {
- /*
- * If the BIOS has set an out of range IRQ number, just
- * ignore it. Also keep track of which IRQ's are
- * already in use.
- */
- if (dev->irq >= 16) {
- dev_dbg(&dev->dev, "ignoring bogus IRQ %d\n", dev->irq);
- dev->irq = 0;
- }
- /*
- * If the IRQ is already assigned to a PCI device,
- * ignore its ISA use penalty
- */
- if (pirq_penalty[dev->irq] >= 100 &&
- pirq_penalty[dev->irq] < 100000)
- pirq_penalty[dev->irq] = 0;
- pirq_penalty[dev->irq]++;
+ /*
+ * If the BIOS has set an out of range IRQ number, just
+ * ignore it. Also keep track of which IRQ's are
+ * already in use.
+ */
+ if (irq >= 16) {
+ dev_dbg(&dev->dev, "ignoring bogus IRQ %d\n", irq);
+ irq = 0;
}
+ /*
+ * If the IRQ is already assigned to a PCI device,
+ * ignore its ISA use penalty
+ */
+ if (pirq_penalty[irq] >= 100 &&
+ pirq_penalty[irq] < 100000)
+ pirq_penalty[irq] = 0;
+ pirq_penalty[irq]++;
- if (io_apic_assign_pci_irqs)
- return;
+ if (io_apic_assign_pci_irqs || !pin)
+ return irq;
- dev = NULL;
- for_each_pci_dev(dev) {
- pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
- if (!pin)
- continue;
+ /*
+ * Still no IRQ? Try to lookup one...
+ */
+ if (!irq && pcibios_lookup_irq(dev, pin, 0))
+ irq = dev->irq;
- /*
- * Still no IRQ? Try to lookup one...
- */
- if (!dev->irq)
- pcibios_lookup_irq(dev, 0);
- }
+ return irq;
}
/*
@@ -1162,6 +1151,12 @@ void __init pcibios_irq_init(void)
}
}
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+ bridge->map_irq = pci_map_irq;
+ return 0;
+}
+
static void pirq_penalize_isa_irq(int irq, int active)
{
/*
@@ -1186,12 +1181,20 @@ void pcibios_penalize_isa_irq(int irq, int active)
pirq_penalize_isa_irq(irq, active);
}
+int pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ dev->irq = pcibios_fixup_irq(dev, pin);
+ if (pcibios_enable_irq(dev))
+ return -1;
+ return dev->irq;
+}
+
static int pirq_enable_irq(struct pci_dev *dev)
{
u8 pin = 0;
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
- if (pin && !pcibios_lookup_irq(dev, 1)) {
+ if (pin && !pcibios_lookup_irq(dev, pin, 1)) {
char *msg = "";
if (!io_apic_assign_pci_irqs && dev->irq)
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index dd30b7e08bc2..980f3040530e 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/rculist.h>
+#include <linux/ecam.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
#include <asm/acpi.h>
@@ -27,103 +28,11 @@
/* Indicate if the mmcfg resources have been placed into the resource table. */
static bool pci_mmcfg_running_state;
static bool pci_mmcfg_arch_init_failed;
-static DEFINE_MUTEX(pci_mmcfg_lock);
-LIST_HEAD(pci_mmcfg_list);
-
-static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
-{
- if (cfg->res.parent)
- release_resource(&cfg->res);
- list_del(&cfg->list);
- kfree(cfg);
-}
-
-static void __init free_all_mmcfg(void)
-{
- struct pci_mmcfg_region *cfg, *tmp;
-
- pci_mmcfg_arch_free();
- list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
- pci_mmconfig_remove(cfg);
-}
-
-static void list_add_sorted(struct pci_mmcfg_region *new)
-{
- struct pci_mmcfg_region *cfg;
-
- /* keep list sorted by segment and starting bus number */
- list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
- if (cfg->segment > new->segment ||
- (cfg->segment == new->segment &&
- cfg->start_bus >= new->start_bus)) {
- list_add_tail_rcu(&new->list, &cfg->list);
- return;
- }
- }
- list_add_tail_rcu(&new->list, &pci_mmcfg_list);
-}
-
-static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
- int end, u64 addr)
-{
- struct pci_mmcfg_region *new;
- struct resource *res;
-
- if (addr == 0)
- return NULL;
-
- new = kzalloc(sizeof(*new), GFP_KERNEL);
- if (!new)
- return NULL;
-
- new->address = addr;
- new->segment = segment;
- new->start_bus = start;
- new->end_bus = end;
-
- res = &new->res;
- res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
- res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
- res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
- snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
- "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
- res->name = new->name;
-
- return new;
-}
-
-static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
- int end, u64 addr)
-{
- struct pci_mmcfg_region *new;
-
- new = pci_mmconfig_alloc(segment, start, end, addr);
- if (new) {
- mutex_lock(&pci_mmcfg_lock);
- list_add_sorted(new);
- mutex_unlock(&pci_mmcfg_lock);
-
- pr_info(PREFIX
- "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
- "(base %#lx)\n",
- segment, start, end, &new->res, (unsigned long)addr);
- }
-
- return new;
-}
-
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
-{
- struct pci_mmcfg_region *cfg;
-
- list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
- if (cfg->segment == segment &&
- cfg->start_bus <= bus && bus <= cfg->end_bus)
- return cfg;
-
- return NULL;
-}
+const struct pci_raw_ops pci_mmcfg = {
+ .read = pci_mmcfg_read,
+ .write = pci_mmcfg_write,
+};
static const char *__init pci_mmcfg_e7520(void)
{
@@ -543,8 +452,8 @@ static void __init pci_mmcfg_reject_broken(int early)
}
}
-static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
- struct acpi_mcfg_allocation *cfg)
+int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+ struct acpi_mcfg_allocation *cfg)
{
int year;
@@ -566,50 +475,6 @@ static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
return -EINVAL;
}
-static int __init pci_parse_mcfg(struct acpi_table_header *header)
-{
- struct acpi_table_mcfg *mcfg;
- struct acpi_mcfg_allocation *cfg_table, *cfg;
- unsigned long i;
- int entries;
-
- if (!header)
- return -EINVAL;
-
- mcfg = (struct acpi_table_mcfg *)header;
-
- /* how many config structures do we have */
- free_all_mmcfg();
- entries = 0;
- i = header->length - sizeof(struct acpi_table_mcfg);
- while (i >= sizeof(struct acpi_mcfg_allocation)) {
- entries++;
- i -= sizeof(struct acpi_mcfg_allocation);
- }
- if (entries == 0) {
- pr_err(PREFIX "MMCONFIG has no entries\n");
- return -ENODEV;
- }
-
- cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
- for (i = 0; i < entries; i++) {
- cfg = &cfg_table[i];
- if (acpi_mcfg_check_entry(mcfg, cfg)) {
- free_all_mmcfg();
- return -ENODEV;
- }
-
- if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
- cfg->end_bus_number, cfg->address) == NULL) {
- pr_warn(PREFIX "no memory for MCFG entries\n");
- free_all_mmcfg();
- return -ENOMEM;
- }
- }
-
- return 0;
-}
-
#ifdef CONFIG_ACPI_APEI
extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
void *data), void *data);
@@ -652,9 +517,10 @@ static void __init __pci_mmcfg_init(int early)
}
}
- if (pci_mmcfg_arch_init())
+ if (pci_mmcfg_arch_init()) {
+ raw_pci_ext_ops = &pci_mmcfg;
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
- else {
+ } else {
free_all_mmcfg();
pci_mmcfg_arch_init_failed = true;
}
@@ -668,7 +534,7 @@ void __init pci_mmcfg_early_init(void)
if (pci_mmcfg_check_hostbridge())
known_bridge = 1;
else
- acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+ acpi_sfi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
__pci_mmcfg_init(1);
set_apei_filter();
@@ -686,7 +552,7 @@ void __init pci_mmcfg_late_init(void)
/* MMCONFIG hasn't been enabled yet, try again */
if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
- acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+ acpi_sfi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
__pci_mmcfg_init(0);
}
}
@@ -734,85 +600,45 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
if (start > end)
return -EINVAL;
- mutex_lock(&pci_mmcfg_lock);
+ rcu_read_lock();
cfg = pci_mmconfig_lookup(seg, start);
- if (cfg) {
- if (cfg->end_bus < end)
- dev_info(dev, FW_INFO
- "MMCONFIG for "
- "domain %04x [bus %02x-%02x] "
- "only partially covers this bridge\n",
- cfg->segment, cfg->start_bus, cfg->end_bus);
- mutex_unlock(&pci_mmcfg_lock);
+ rcu_read_unlock();
+ if (cfg)
return -EEXIST;
- }
- if (!addr) {
- mutex_unlock(&pci_mmcfg_lock);
+ if (!addr)
return -EINVAL;
- }
- rc = -EBUSY;
cfg = pci_mmconfig_alloc(seg, start, end, addr);
- if (cfg == NULL) {
+ if (!cfg) {
dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
- rc = -ENOMEM;
- } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
+ return -ENOMEM;
+ }
+
+ rc = -EBUSY;
+ if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
&cfg->res);
- } else {
- /* Insert resource if it's not in boot stage */
- if (pci_mmcfg_running_state)
- tmp = insert_resource_conflict(&iomem_resource,
- &cfg->res);
-
- if (tmp) {
- dev_warn(dev,
- "MMCONFIG %pR conflicts with "
- "%s %pR\n",
- &cfg->res, tmp->name, tmp);
- } else if (pci_mmcfg_arch_map(cfg)) {
- dev_warn(dev, "fail to map MMCONFIG %pR.\n",
- &cfg->res);
- } else {
- list_add_sorted(cfg);
- dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
- &cfg->res, (unsigned long)addr);
- cfg = NULL;
- rc = 0;
- }
+ goto error;
}
- if (cfg) {
- if (cfg->res.parent)
- release_resource(&cfg->res);
- kfree(cfg);
+ /* Insert resource if it's not in boot stage */
+ if (pci_mmcfg_running_state)
+ tmp = insert_resource_conflict(&iomem_resource, &cfg->res);
+
+ if (tmp) {
+ dev_warn(dev, "MMCONFIG %pR conflicts with %s %pR\n",
+ &cfg->res, tmp->name, tmp);
+ goto error;
}
- mutex_unlock(&pci_mmcfg_lock);
+ rc = pci_mmconfig_inject(cfg);
+ if (!rc)
+ return 0;
+error:
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ kfree(cfg);
return rc;
}
-
-/* Delete MMCFG information for host bridges */
-int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
-{
- struct pci_mmcfg_region *cfg;
-
- mutex_lock(&pci_mmcfg_lock);
- list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
- if (cfg->segment == seg && cfg->start_bus == start &&
- cfg->end_bus == end) {
- list_del_rcu(&cfg->list);
- synchronize_rcu();
- pci_mmcfg_arch_unmap(cfg);
- if (cfg->res.parent)
- release_resource(&cfg->res);
- mutex_unlock(&pci_mmcfg_lock);
- kfree(cfg);
- return 0;
- }
- mutex_unlock(&pci_mmcfg_lock);
-
- return -ENOENT;
-}
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index 43984bc1665a..2ded56f091a7 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -12,6 +12,7 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/rcupdate.h>
+#include <linux/ecam.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
@@ -49,7 +50,7 @@ static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
}
}
-static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
+int pci_mmcfg_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
@@ -88,7 +89,7 @@ err: *value = -1;
return 0;
}
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
+int pci_mmcfg_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value)
{
unsigned long flags;
@@ -125,15 +126,9 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
return 0;
}
-const struct pci_raw_ops pci_mmcfg = {
- .read = pci_mmcfg_read,
- .write = pci_mmcfg_write,
-};
-
int __init pci_mmcfg_arch_init(void)
{
printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n");
- raw_pci_ext_ops = &pci_mmcfg;
return 1;
}
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index bea52496aea6..fd356cc0a43c 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -10,6 +10,7 @@
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
+#include <linux/ecam.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
@@ -24,7 +25,7 @@ static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned i
return NULL;
}
-static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
+int pci_mmcfg_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
char __iomem *addr;
@@ -58,7 +59,7 @@ err: *value = -1;
return 0;
}
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
+int pci_mmcfg_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value)
{
char __iomem *addr;
@@ -89,65 +90,3 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
return 0;
}
-
-const struct pci_raw_ops pci_mmcfg = {
- .read = pci_mmcfg_read,
- .write = pci_mmcfg_write,
-};
-
-static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
-{
- void __iomem *addr;
- u64 start, size;
- int num_buses;
-
- start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
- num_buses = cfg->end_bus - cfg->start_bus + 1;
- size = PCI_MMCFG_BUS_OFFSET(num_buses);
- addr = ioremap_nocache(start, size);
- if (addr)
- addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
- return addr;
-}
-
-int __init pci_mmcfg_arch_init(void)
-{
- struct pci_mmcfg_region *cfg;
-
- list_for_each_entry(cfg, &pci_mmcfg_list, list)
- if (pci_mmcfg_arch_map(cfg)) {
- pci_mmcfg_arch_free();
- return 0;
- }
-
- raw_pci_ext_ops = &pci_mmcfg;
-
- return 1;
-}
-
-void __init pci_mmcfg_arch_free(void)
-{
- struct pci_mmcfg_region *cfg;
-
- list_for_each_entry(cfg, &pci_mmcfg_list, list)
- pci_mmcfg_arch_unmap(cfg);
-}
-
-int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
-{
- cfg->virt = mcfg_ioremap(cfg);
- if (!cfg->virt) {
- pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
-{
- if (cfg && cfg->virt) {
- iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
- cfg->virt = NULL;
- }
-}
diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
index 2e565e65c893..55fbd18e22e5 100644
--- a/arch/x86/pci/numachip.c
+++ b/arch/x86/pci/numachip.c
@@ -13,6 +13,7 @@
*
*/
+#include <linux/ecam.h>
#include <linux/pci.h>
#include <asm/pci_x86.h>
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5eef4cb4f70e..3cb1d1c78116 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -57,6 +57,16 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT
config ACPI_CCA_REQUIRED
bool
+
+config IORT_TABLE
+ bool
+
+config ACPI_HAS_NUMA_ARCH_FIXUP
+ bool
+
+config ACPI_SPCR_TABLE
+ bool
+
config ACPI_DEBUGGER
bool "AML debugger interface (EXPERIMENTAL)"
select ACPI_DEBUG
@@ -280,7 +290,7 @@ config ACPI_THERMAL
config ACPI_NUMA
bool "NUMA support"
depends on NUMA
- depends on (X86 || IA64)
+ depends on (X86 || IA64 || ARM64)
default y if IA64_GENERIC || IA64_SGI_SN2
config ACPI_CUSTOM_DSDT_FILE
@@ -332,6 +342,11 @@ config ACPI_PCI_SLOT
i.e., segment/bus/device/function tuples, with physical slots in
the system. If you are unsure, say N.
+config ACPI_PCI_HOST_GENERIC
+ bool "Generic ACPI PCI host controller"
+ help
+ Say Y here if you want to support generic ACPI PCI host controller.
+
config X86_PM_TIMER
bool "Power Management Timer Support" if EXPERT
depends on X86
@@ -510,4 +525,13 @@ config XPOWER_PMIC_OPREGION
endif
+config ACPI_GTDT
+ bool "ACPI GTDT Support"
+ depends on ARM64
+ help
+ GTDT (Generic Timer Description Table) provides information
+ for per-processor timers and Platform (memory-mapped) timers
+ for ARM platforms. Select this option to provide information
+ needed for the timers init.
+
endif # ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf337178..af8d25d1b032 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -43,6 +43,7 @@ acpi-y += pci_root.o pci_link.o pci_irq.o
acpi-y += acpi_lpss.o acpi_apd.o
acpi-y += acpi_platform.o
acpi-y += acpi_pnp.o
+acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
acpi-y += int340x_thermal.o
acpi-y += power.o
acpi-y += event.o
@@ -65,6 +66,7 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o
obj-$(CONFIG_ACPI_FAN) += fan.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
+obj-$(CONFIG_PCI_MMCONFIG) += mcfg.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-y += container.o
obj-$(CONFIG_ACPI_THERMAL) += thermal.o
@@ -79,6 +81,8 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
+obj-$(CONFIG_IORT_TABLE) += iort.o
+obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o
# processor has its own "processor." module_param namespace
processor-y := processor_driver.o
@@ -96,5 +100,6 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o
obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o
obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
+obj-$(CONFIG_ACPI_GTDT) += gtdt.o
video-objs += acpi_video.o video_detect.o
diff --git a/drivers/acpi/acpi_amba.c b/drivers/acpi/acpi_amba.c
new file mode 100644
index 000000000000..2a61b54ab968
--- /dev/null
+++ b/drivers/acpi/acpi_amba.c
@@ -0,0 +1,122 @@
+
+/*
+ * ACPI support for platform bus type.
+ *
+ * Copyright (C) 2015, Linaro Ltd
+ * Author: Graeme Gregory <graeme.gregory@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/amba/bus.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "internal.h"
+
+static const struct acpi_device_id amba_id_list[] = {
+ {"ARMH0061", 0}, /* PL061 GPIO Device */
+ {"", 0},
+};
+
+static void amba_register_dummy_clk(void)
+{
+ static struct clk *amba_dummy_clk;
+
+ /* If clock already registered */
+ if (amba_dummy_clk)
+ return;
+
+ amba_dummy_clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL,
+ CLK_IS_ROOT, 0);
+ clk_register_clkdev(amba_dummy_clk, "apb_pclk", NULL);
+}
+
+static int amba_handler_attach(struct acpi_device *adev,
+ const struct acpi_device_id *id)
+{
+ struct amba_device *dev;
+ struct resource_entry *rentry;
+ struct list_head resource_list;
+ bool address_found = false;
+ int irq_no = 0;
+ int ret;
+
+ /* If the ACPI node already has a physical device attached, skip it. */
+ if (adev->physical_node_count)
+ return 0;
+
+ dev = amba_device_alloc(dev_name(&adev->dev), 0, 0);
+ if (!dev) {
+ dev_err(&adev->dev, "%s(): amba_device_alloc() failed\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&resource_list);
+ ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+ if (ret < 0)
+ goto err_free;
+
+ list_for_each_entry(rentry, &resource_list, node) {
+ switch (resource_type(rentry->res)) {
+ case IORESOURCE_MEM:
+ if (!address_found) {
+ dev->res = *rentry->res;
+ address_found = true;
+ }
+ break;
+ case IORESOURCE_IRQ:
+ if (irq_no < AMBA_NR_IRQS)
+ dev->irq[irq_no++] = rentry->res->start;
+ break;
+ default:
+ dev_warn(&adev->dev, "Invalid resource\n");
+ break;
+ }
+ }
+
+ acpi_dev_free_resource_list(&resource_list);
+
+ /*
+ * If the ACPI node has a parent and that parent has a physical device
+ * attached to it, that physical device should be the parent of
+ * the amba device we are about to create.
+ */
+ if (adev->parent)
+ dev->dev.parent = acpi_get_first_physical_node(adev->parent);
+
+ ACPI_COMPANION_SET(&dev->dev, adev);
+
+ ret = amba_device_add(dev, &iomem_resource);
+ if (ret) {
+ dev_err(&adev->dev, "%s(): amba_device_add() failed (%d)\n",
+ __func__, ret);
+ goto err_free;
+ }
+
+ return 1;
+
+err_free:
+ amba_device_put(dev);
+ return ret;
+}
+
+static struct acpi_scan_handler amba_handler = {
+ .ids = amba_id_list,
+ .attach = amba_handler_attach,
+};
+
+void __init acpi_amba_init(void)
+{
+ amba_register_dummy_clk();
+ acpi_scan_add_handler(&amba_handler);
+}
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 296b7a14893a..c3af1088bf6b 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -43,7 +43,6 @@ static const struct acpi_device_id forbidden_id_list[] = {
struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
{
struct platform_device *pdev = NULL;
- struct acpi_device *acpi_parent;
struct platform_device_info pdevinfo;
struct resource_entry *rentry;
struct list_head resource_list;
@@ -82,22 +81,8 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
* attached to it, that physical device should be the parent of the
* platform device we are about to create.
*/
- pdevinfo.parent = NULL;
- acpi_parent = adev->parent;
- if (acpi_parent) {
- struct acpi_device_physical_node *entry;
- struct list_head *list;
-
- mutex_lock(&acpi_parent->physical_node_lock);
- list = &acpi_parent->physical_node_list;
- if (!list_empty(list)) {
- entry = list_first_entry(list,
- struct acpi_device_physical_node,
- node);
- pdevinfo.parent = entry->dev;
- }
- mutex_unlock(&acpi_parent->physical_node_lock);
- }
+ pdevinfo.parent = adev->parent ?
+ acpi_get_first_physical_node(adev->parent) : NULL;
pdevinfo.name = dev_name(&adev->dev);
pdevinfo.id = -1;
pdevinfo.res = resources;
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index b0140c8fc733..a60bb000a15d 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -4,6 +4,9 @@ config HAVE_ACPI_APEI
config HAVE_ACPI_APEI_NMI
bool
+config HAVE_ACPI_APEI_HEST_IA32
+ bool
+
config ACPI_APEI
bool "ACPI Platform Error Interface (APEI)"
select MISC_FILESYSTEMS
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
index 5d575a955940..e50573de25f1 100644
--- a/drivers/acpi/apei/Makefile
+++ b/drivers/acpi/apei/Makefile
@@ -3,4 +3,4 @@ obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
-apei-y := apei-base.o hest.o erst.o
+apei-y := apei-base.o hest.o erst.o bert.o
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h
index 16129c78b489..6e9f14c0a71b 100644
--- a/drivers/acpi/apei/apei-internal.h
+++ b/drivers/acpi/apei/apei-internal.h
@@ -1,6 +1,6 @@
/*
* apei-internal.h - ACPI Platform Error Interface internal
- * definations.
+ * definitions.
*/
#ifndef APEI_INTERNAL_H
diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c
new file mode 100644
index 000000000000..a05b5c0cf181
--- /dev/null
+++ b/drivers/acpi/apei/bert.c
@@ -0,0 +1,150 @@
+/*
+ * APEI Boot Error Record Table (BERT) support
+ *
+ * Copyright 2011 Intel Corp.
+ * Author: Huang Ying <ying.huang@intel.com>
+ *
+ * Under normal circumstances, when a hardware error occurs, the error
+ * handler receives control and processes the error. This gives OSPM a
+ * chance to process the error condition, report it, and optionally attempt
+ * recovery. In some cases, the system is unable to process an error.
+ * For example, system firmware or a management controller may choose to
+ * reset the system or the system might experience an uncontrolled crash
+ * or reset.The boot error source is used to report unhandled errors that
+ * occurred in a previous boot. This mechanism is described in the BERT
+ * table.
+ *
+ * For more information about BERT, please refer to ACPI Specification
+ * version 4.0, section 17.3.1
+ *
+ * This file is licensed under GPLv2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+#include "apei-internal.h"
+
+#undef pr_fmt
+#define pr_fmt(fmt) "BERT: " fmt
+
+static int bert_disable;
+
+static void __init bert_print_all(struct acpi_bert_region *region,
+ unsigned int region_len)
+{
+ struct acpi_hest_generic_status *estatus =
+ (struct acpi_hest_generic_status *)region;
+ int remain = region_len;
+ u32 estatus_len;
+
+ if (!estatus->block_status)
+ return;
+
+ while (remain > sizeof(struct acpi_bert_region)) {
+ if (cper_estatus_check(estatus)) {
+ pr_err(FW_BUG "Invalid error record.\n");
+ return;
+ }
+
+ estatus_len = cper_estatus_len(estatus);
+ if (remain < estatus_len) {
+ pr_err(FW_BUG "Truncated status block (length: %u).\n",
+ estatus_len);
+ return;
+ }
+
+ pr_info_once("Error records from previous boot:\n");
+
+ cper_estatus_print(KERN_INFO HW_ERR, estatus);
+
+ /*
+ * Because the boot error source is "one-time polled" type,
+ * clear Block Status of current Generic Error Status Block,
+ * once it's printed.
+ */
+ estatus->block_status = 0;
+
+ estatus = (void *)estatus + estatus_len;
+ /* No more error records. */
+ if (!estatus->block_status)
+ return;
+
+ remain -= estatus_len;
+ }
+}
+
+static int __init setup_bert_disable(char *str)
+{
+ bert_disable = 1;
+
+ return 0;
+}
+__setup("bert_disable", setup_bert_disable);
+
+static int __init bert_check_table(struct acpi_table_bert *bert_tab)
+{
+ if (bert_tab->header.length < sizeof(struct acpi_table_bert) ||
+ bert_tab->region_length < sizeof(struct acpi_bert_region))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __init bert_init(void)
+{
+ struct acpi_bert_region *boot_error_region;
+ struct acpi_table_bert *bert_tab;
+ unsigned int region_len;
+ acpi_status status;
+ int rc = 0;
+
+ if (acpi_disabled)
+ return 0;
+
+ if (bert_disable) {
+ pr_info("Boot Error Record Table support is disabled.\n");
+ return 0;
+ }
+
+ status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab);
+ if (status == AE_NOT_FOUND)
+ return 0;
+
+ if (ACPI_FAILURE(status)) {
+ pr_err("get table failed, %s.\n", acpi_format_exception(status));
+ return -EINVAL;
+ }
+
+ rc = bert_check_table(bert_tab);
+ if (rc) {
+ pr_err(FW_BUG "table invalid.\n");
+ return rc;
+ }
+
+ region_len = bert_tab->region_length;
+ if (!request_mem_region(bert_tab->address, region_len, "APEI BERT")) {
+ pr_err("Can't request iomem region <%016llx-%016llx>.\n",
+ (unsigned long long)bert_tab->address,
+ (unsigned long long)bert_tab->address + region_len - 1);
+ return -EIO;
+ }
+
+ boot_error_region = ioremap_cache(bert_tab->address, region_len);
+ if (boot_error_region) {
+ bert_print_all(boot_error_region, region_len);
+ iounmap(boot_error_region);
+ } else {
+ rc = -ENOMEM;
+ }
+
+ release_mem_region(bert_tab->address, region_len);
+
+ return rc;
+}
+
+late_initcall(bert_init);
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index 20b3fcf4007c..c708c954c79a 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -117,15 +117,27 @@ int apei_hest_parse(apei_hest_func_t func, void *data)
}
EXPORT_SYMBOL_GPL(apei_hest_parse);
+#ifdef CONFIG_HAVE_ACPI_APEI_HEST_IA32
/*
* Check if firmware advertises firmware first mode. We need FF bit to be set
* along with a set of MC banks which work in FF mode.
*/
static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data)
{
- return arch_apei_enable_cmcff(hest_hdr, data);
+ if (!acpi_disable_cmcff)
+ return !arch_apei_enable_cmcff(hest_hdr, data);
+
+ return 0;
}
+static inline int __init hest_ia_init(void)
+{
+ return apei_hest_parse(hest_parse_cmc, NULL);
+}
+#else
+static inline int __init hest_ia_init(void) { return 0; }
+#endif
+
struct ghes_arr {
struct platform_device **ghes_devs;
unsigned int count;
@@ -232,8 +244,9 @@ void __init acpi_hest_init(void)
goto err;
}
- if (!acpi_disable_cmcff)
- apei_hest_parse(hest_parse_cmc, NULL);
+ rc = hest_ia_init();
+ if (rc)
+ goto err;
if (!ghes_disable) {
rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a212cefae524..832b26dfc73c 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -478,24 +478,35 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device)
Device Matching
-------------------------------------------------------------------------- */
-static struct acpi_device *acpi_primary_dev_companion(struct acpi_device *adev,
- const struct device *dev)
+/**
+ * acpi_device_fix_parent - Get first physical node of an ACPI device
+ * @adev: ACPI device in question
+ */
+struct device *acpi_get_first_physical_node(struct acpi_device *adev)
{
struct mutex *physical_node_lock = &adev->physical_node_lock;
+ struct device *node = NULL;
mutex_lock(physical_node_lock);
- if (list_empty(&adev->physical_node_list)) {
- adev = NULL;
- } else {
- const struct acpi_device_physical_node *node;
+ if (!list_empty(&adev->physical_node_list))
node = list_first_entry(&adev->physical_node_list,
- struct acpi_device_physical_node, node);
- if (node->dev != dev)
- adev = NULL;
- }
+ struct acpi_device_physical_node, node)->dev;
+
mutex_unlock(physical_node_lock);
- return adev;
+
+ return node;
+}
+
+static struct acpi_device *acpi_primary_dev_companion(struct acpi_device *adev,
+ const struct device *dev)
+{
+ const struct device *node = acpi_get_first_physical_node(adev);
+
+ if (node && node == dev)
+ return adev;
+
+ return NULL;
}
/**
diff --git a/drivers/acpi/gtdt.c b/drivers/acpi/gtdt.c
new file mode 100644
index 000000000000..d15ea919edec
--- /dev/null
+++ b/drivers/acpi/gtdt.c
@@ -0,0 +1,367 @@
+/*
+ * ARM Specific GTDT table Support
+ *
+ * Copyright (C) 2015, Linaro Ltd.
+ * Author: Fu Wei <fu.wei@linaro.org>
+ * Hanjun Guo <hanjun.guo@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <clocksource/arm_arch_timer.h>
+
+static u32 platform_timer_count __initdata;
+static int gtdt_timers_existence __initdata;
+
+/*
+ * Get some basic info from GTDT table, and init the global variables above
+ * for all timers initialization of Generic Timer.
+ * This function does some validation on GTDT table, and will be run only once.
+ */
+static void __init *platform_timer_info_init(struct acpi_table_header *table)
+{
+ void *gtdt_end, *platform_timer_struct, *platform_timer;
+ struct acpi_gtdt_header *header;
+ struct acpi_table_gtdt *gtdt;
+ u32 i;
+
+ gtdt = container_of(table, struct acpi_table_gtdt, header);
+ if (!gtdt) {
+ pr_err("GTDT: table pointer error, bad table.\n");
+ return NULL;
+ }
+ gtdt_end = (void *)table + table->length;
+ gtdt_timers_existence |= ARCH_CP15_TIMER;
+
+ if (table->revision < 2) {
+ pr_info("GTDT: Revision:%d doesn't support Platform Timers.\n",
+ table->revision);
+ return NULL;
+ }
+
+ platform_timer_count = gtdt->platform_timer_count;
+ if (!platform_timer_count) {
+ pr_info("GTDT: No Platform Timer structures.\n");
+ return NULL;
+ }
+
+ platform_timer_struct = (void *)gtdt + gtdt->platform_timer_offset;
+ if (platform_timer_struct < (void *)table +
+ sizeof(struct acpi_table_gtdt)) {
+ pr_err("GTDT: Platform Timer pointer error, bad table.\n");
+ return NULL;
+ }
+
+ platform_timer = platform_timer_struct;
+ for (i = 0; i < platform_timer_count; i++) {
+ if (platform_timer > gtdt_end) {
+ pr_warn("GTDT: subtable pointer overflows, bad table\n");
+ platform_timer_count = i;
+ break;
+ }
+ header = (struct acpi_gtdt_header *)platform_timer;
+ if (header->type == ACPI_GTDT_TYPE_TIMER_BLOCK)
+ gtdt_timers_existence |= ARCH_MEM_TIMER;
+ else if (header->type == ACPI_GTDT_TYPE_WATCHDOG)
+ gtdt_timers_existence |= ARCH_WD_TIMER;
+ platform_timer += header->length;
+ }
+
+ return platform_timer_struct;
+}
+
+static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
+{
+ int trigger, polarity;
+
+ if (!interrupt)
+ return 0;
+
+ trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
+ : ACPI_LEVEL_SENSITIVE;
+
+ polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
+ : ACPI_ACTIVE_HIGH;
+
+ return acpi_register_gsi(NULL, interrupt, trigger, polarity);
+}
+
+/*
+ * Get the necessary info of arch_timer from GTDT table.
+ */
+int __init gtdt_arch_timer_data_init(struct acpi_table_header *table,
+ struct arch_timer_data *data)
+{
+ struct acpi_table_gtdt *gtdt;
+
+ if (acpi_disabled)
+ return -EINVAL;
+
+ if (!gtdt_timers_existence)
+ platform_timer_info_init(table);
+
+ gtdt = container_of(table, struct acpi_table_gtdt, header);
+ if (!gtdt || !data)
+ return -EINVAL;
+
+ data->phys_secure_ppi =
+ map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
+ gtdt->secure_el1_flags);
+
+ data->phys_nonsecure_ppi =
+ map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
+ gtdt->non_secure_el1_flags);
+
+ data->virt_ppi =
+ map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
+ gtdt->virtual_timer_flags);
+
+ data->hyp_ppi =
+ map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
+ gtdt->non_secure_el2_flags);
+
+ data->c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
+
+ return 0;
+}
+
+bool __init gtdt_timer_is_available(int type)
+{
+ return gtdt_timers_existence | type;
+}
+
+/*
+ * Helper function for getting the pointer of platform_timer_struct.
+ */
+static void __init *get_platform_timer_struct(struct acpi_table_header *table)
+{
+ struct acpi_table_gtdt *gtdt;
+
+ gtdt = container_of(table, struct acpi_table_gtdt, header);
+ if (gtdt)
+ return (void *)gtdt + gtdt->platform_timer_offset;
+
+ pr_err("GTDT: table pointer error, bad table.\n");
+ return NULL;
+}
+
+ /*
+ * Get the pointer of GT Block Structure in GTDT table
+ */
+void __init *gtdt_gt_block(struct acpi_table_header *table, int index)
+{
+ struct acpi_gtdt_header *header;
+ void *platform_timer;
+ int i, j;
+
+ if (!gtdt_timers_existence)
+ platform_timer = platform_timer_info_init(table);
+ else
+ platform_timer = get_platform_timer_struct(table);
+
+ if (!gtdt_timer_is_available(ARCH_MEM_TIMER))
+ return NULL;
+
+ for (i = 0, j = 0; i < platform_timer_count; i++) {
+ header = (struct acpi_gtdt_header *)platform_timer;
+ if (header->type == ACPI_GTDT_TYPE_TIMER_BLOCK && j++ == index)
+ return platform_timer;
+ platform_timer += header->length;
+ }
+
+ return NULL;
+}
+
+/*
+ * Get the timer_count(the number of timer frame) of a GT Block in GTDT table
+ */
+u32 __init gtdt_gt_timer_count(struct acpi_gtdt_timer_block *gt_block)
+{
+ if (!gt_block) {
+ pr_err("GTDT: invalid GT Block baseaddr.\n");
+ return 0;
+ }
+
+ return gt_block->timer_count;
+}
+
+/*
+ * Get the physical address of GT Block in GTDT table
+ */
+void __init *gtdt_gt_cntctlbase(struct acpi_gtdt_timer_block *gt_block)
+{
+ if (!gt_block) {
+ pr_err("GTDT: invalid GT Block baseaddr.\n");
+ return NULL;
+ }
+
+ return (void *)gt_block->block_address;
+}
+
+/*
+ * Helper function for getting the pointer of a timer frame in GT block.
+ */
+static void __init *gtdt_gt_timer_frame(struct acpi_gtdt_timer_block *gt_block,
+ int index)
+{
+ void *timer_frame;
+
+ if (!(gt_block && gt_block->timer_count))
+ return NULL;
+
+ timer_frame = (void *)gt_block + gt_block->timer_offset +
+ sizeof(struct acpi_gtdt_timer_entry) * index;
+
+ if (timer_frame <= (void *)gt_block + gt_block->header.length -
+ sizeof(struct acpi_gtdt_timer_entry))
+ return timer_frame;
+
+ pr_err("GTDT: invalid GT Block timer frame entry addr.\n");
+ return NULL;
+}
+
+/*
+ * Get the GT timer Frame Number(ID) in a GT Block Timer Structure.
+ * The maximum Frame Number(ID) is (ARCH_TIMER_MEM_MAX_FRAME - 1),
+ * so returning ARCH_TIMER_MEM_MAX_FRAME means error.
+ */
+u32 __init gtdt_gt_frame_number(struct acpi_gtdt_timer_block *gt_block,
+ int index)
+{
+ struct acpi_gtdt_timer_entry *frame;
+
+ frame = (struct acpi_gtdt_timer_entry *)gtdt_gt_timer_frame(gt_block,
+ index);
+ if (frame)
+ return frame->frame_number;
+
+ return ARCH_TIMER_MEM_MAX_FRAME;
+}
+
+/*
+ * Get the GT timer Frame data in a GT Block Timer Structure
+ */
+int __init gtdt_gt_timer_data(struct acpi_gtdt_timer_block *gt_block,
+ int index, bool virt,
+ struct arch_timer_mem_data *data)
+{
+ struct acpi_gtdt_timer_entry *frame;
+
+ frame = (struct acpi_gtdt_timer_entry *)gtdt_gt_timer_frame(gt_block,
+ index);
+ if (!frame)
+ return -EINVAL;
+
+ data->cntbase_phy = (phys_addr_t)frame->base_address;
+ if (virt)
+ data->irq = map_generic_timer_interrupt(
+ frame->virtual_timer_interrupt,
+ frame->virtual_timer_flags);
+ else
+ data->irq = map_generic_timer_interrupt(frame->timer_interrupt,
+ frame->timer_flags);
+
+ return 0;
+}
+
+/*
+ * Initialize a SBSA generic Watchdog platform device info from GTDT
+ */
+static int __init gtdt_import_sbsa_gwdt(struct acpi_gtdt_watchdog *wd,
+ int index)
+{
+ struct platform_device *pdev;
+ int irq = map_generic_timer_interrupt(wd->timer_interrupt,
+ wd->timer_flags);
+ /*
+ * According to SBSA specification the size of refresh and control
+ * frames of SBSA Generic Watchdog is SZ_4K(Offset 0x000 – 0xFFF).
+ */
+ struct resource res[] = {
+ DEFINE_RES_IRQ(irq),
+ DEFINE_RES_MEM(wd->control_frame_address, SZ_4K),
+ DEFINE_RES_MEM(wd->refresh_frame_address, SZ_4K),
+ };
+
+ pr_debug("GTDT: a Watchdog GT(0x%llx/0x%llx gsi:%u flags:0x%x)\n",
+ wd->refresh_frame_address, wd->control_frame_address,
+ wd->timer_interrupt, wd->timer_flags);
+
+ if (!(wd->refresh_frame_address &&
+ wd->control_frame_address &&
+ wd->timer_interrupt)) {
+ pr_err("GTDT: failed geting the device info.\n");
+ return -EINVAL;
+ }
+
+ if (irq < 0) {
+ pr_err("GTDT: failed to register GSI of the Watchdog GT.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Add a platform device named "sbsa-gwdt" to match the platform driver.
+ * "sbsa-gwdt": SBSA(Server Base System Architecture) Generic Watchdog
+ * The platform driver (like drivers/watchdog/sbsa_gwdt.c)can get device
+ * info below by matching this name.
+ */
+ pdev = platform_device_register_simple("sbsa-gwdt", index, res,
+ ARRAY_SIZE(res));
+ if (IS_ERR(pdev)) {
+ acpi_unregister_gsi(wd->timer_interrupt);
+ return PTR_ERR(pdev);
+ }
+
+ return 0;
+}
+
+static int __init gtdt_sbsa_gwdt_parse(struct acpi_table_header *table)
+{
+ struct acpi_gtdt_header *header;
+ void *platform_timer;
+ int i, gwdt_index;
+ int ret = 0;
+
+ /*
+ * At this proint, we have got and validate some info about platform
+ * timer in platform_timer_info_init.
+ */
+ if (!gtdt_timer_is_available(ARCH_WD_TIMER))
+ return -EINVAL;
+
+ platform_timer = get_platform_timer_struct(table);
+
+ for (i = 0, gwdt_index = 0; i < platform_timer_count; i++) {
+ header = (struct acpi_gtdt_header *)platform_timer;
+ if (header->type == ACPI_GTDT_TYPE_WATCHDOG) {
+ ret = gtdt_import_sbsa_gwdt(platform_timer, gwdt_index);
+ if (ret)
+ pr_err("GTDT: failed to import subtable %d\n",
+ i);
+ else
+ gwdt_index++;
+ }
+ platform_timer += header->length;
+ }
+
+ return ret;
+}
+
+static int __init gtdt_sbsa_gwdt_init(void)
+{
+ if (acpi_disabled)
+ return 0;
+
+ return acpi_table_parse(ACPI_SIG_GTDT, gtdt_sbsa_gwdt_parse);
+}
+
+device_initcall(gtdt_sbsa_gwdt_init);
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 11d87bf67e73..6086f66e8f25 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -29,6 +29,11 @@ void acpi_processor_init(void);
void acpi_platform_init(void);
void acpi_pnp_init(void);
void acpi_int340x_thermal_init(void);
+#ifdef CONFIG_ARM_AMBA
+void acpi_amba_init(void);
+#else
+static inline void acpi_amba_init(void) {}
+#endif
int acpi_sysfs_init(void);
void acpi_container_init(void);
void acpi_memory_hotplug_init(void);
@@ -98,6 +103,7 @@ bool acpi_device_is_present(struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev,
const struct device *dev);
+struct device *acpi_get_first_physical_node(struct acpi_device *adev);
/* --------------------------------------------------------------------------
Device Matching and Notification
diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
new file mode 100644
index 000000000000..13efc5fdb50e
--- /dev/null
+++ b/drivers/acpi/iort.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2015, Linaro Ltd.
+ * Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * This file implements early detection/parsing of I/O mapping
+ * reported to OS through firmware via I/O Remapping Table (IORT)
+ * IORT document number: ARM DEN 0049A
+ */
+
+#define pr_fmt(fmt) "ACPI: IORT: " fmt
+
+#include <linux/export.h>
+#include <linux/iort.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+struct iort_its_msi_chip {
+ struct list_head list;
+ struct fwnode_handle *fw_node;
+ u32 translation_id;
+};
+
+typedef acpi_status (*iort_find_node_callback)
+ (struct acpi_iort_node *node, void *context);
+
+/* Root pointer to the mapped IORT table */
+static struct acpi_table_header *iort_table;
+
+static LIST_HEAD(iort_msi_chip_list);
+
+/**
+ * iort_register_domain_token() - register domain token and related ITS ID
+ * to the list from where we can get it back
+ * later on.
+ * @translation_id: ITS ID
+ * @token: domain token
+ *
+ * Returns: 0 on success, -ENOMEM if not memory when allocating list element.
+ */
+int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
+{
+ struct iort_its_msi_chip *its_msi_chip;
+
+ its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
+ if (!its_msi_chip)
+ return -ENOMEM;
+
+ its_msi_chip->fw_node = fw_node;
+ its_msi_chip->translation_id = trans_id;
+
+ list_add(&its_msi_chip->list, &iort_msi_chip_list);
+ return 0;
+}
+
+/**
+ * iort_find_its_domain_token() - find domain token based on given ITS ID.
+ * @translation_id: ITS ID
+ *
+ * Returns: domain token when find on the list, NULL otherwise.
+ */
+struct fwnode_handle *iort_find_its_domain_token(int trans_id)
+{
+ struct iort_its_msi_chip *its_msi_chip;
+
+ list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+ if (its_msi_chip->translation_id == trans_id)
+ return its_msi_chip->fw_node;
+ }
+
+ return NULL;
+}
+
+static struct acpi_iort_node *
+iort_scan_node(enum acpi_iort_node_type type,
+ iort_find_node_callback callback, void *context)
+{
+ struct acpi_iort_node *iort_node, *iort_end;
+ struct acpi_table_iort *iort;
+ int i;
+
+ if (!iort_table)
+ return NULL;
+
+ /*
+ * iort_table and iort both point to the start of IORT table, but
+ * have different struct types
+ */
+ iort = (struct acpi_table_iort *)iort_table;
+
+ /* Get the first IORT node */
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+ iort->node_offset);
+ iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ iort_table->length);
+
+ for (i = 0; i < iort->node_count; i++) {
+ if (iort_node >= iort_end) {
+ pr_err("iort node pointer overflows, bad table\n");
+ return NULL;
+ }
+
+ if (iort_node->type == type) {
+ if (ACPI_SUCCESS(callback(iort_node, context)))
+ return iort_node;
+ }
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+ iort_node->length);
+ }
+
+ return NULL;
+}
+
+static struct acpi_iort_node *iort_find_parent_node(struct acpi_iort_node *node)
+{
+ struct acpi_iort_id_mapping *id;
+
+ if (!node || !node->mapping_offset || !node->mapping_count)
+ return NULL;
+
+ id = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+ node->mapping_offset);
+ /* Firmware bug! */
+ if (!id->output_reference) {
+ pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+ node, node->type);
+ return NULL;
+ }
+
+ node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+ id->output_reference);
+ return node;
+}
+
+static acpi_status
+iort_find_dev_callback(struct acpi_iort_node *node, void *context)
+{
+ struct acpi_iort_root_complex *pci_rc;
+ struct device *dev = context;
+ struct pci_bus *bus;
+
+ switch (node->type) {
+ case ACPI_IORT_NODE_PCI_ROOT_COMPLEX:
+ bus = to_pci_bus(dev);
+ pci_rc = (struct acpi_iort_root_complex *)node->node_data;
+
+ /*
+ * It is assumed that PCI segment numbers have a one-to-one
+ * mapping with root complexes. Each segment number can
+ * represent only one root complex.
+ */
+ if (pci_rc->pci_segment_number == pci_domain_nr(bus))
+ return AE_OK;
+
+ break;
+ }
+
+ return AE_NOT_FOUND;
+}
+
+/**
+ * iort_pci_find_its_id() - find the ITS identifier based on specified device.
+ * @dev: device
+ * @idx: index of the ITS identifier list
+ * @its_id: ITS identifier
+ *
+ * Returns: 0 on success, appropriate error value otherwise
+ */
+static int
+iort_pci_find_its_id(struct device *dev, unsigned int idx, int *its_id)
+{
+ struct acpi_iort_its_group *its;
+ struct acpi_iort_node *node;
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_find_dev_callback, dev);
+ if (!node) {
+ pr_err("can't find node related to %s device\n", dev_name(dev));
+ return -ENXIO;
+ }
+
+ /* Go upstream until find its parent ITS node */
+ while (node->type != ACPI_IORT_NODE_ITS_GROUP) {
+ node = iort_find_parent_node(node);
+ if (!node)
+ return -ENXIO;
+ }
+
+ /* Move to ITS specific data */
+ its = (struct acpi_iort_its_group *)node->node_data;
+ if (idx > its->its_count) {
+ pr_err("requested ITS ID index [%d] is greater than available ITS count [%d]\n",
+ idx, its->its_count);
+ return -ENXIO;
+ }
+
+ *its_id = its->identifiers[idx];
+ return 0;
+}
+
+
+/**
+ * iort_find_pci_domain_token() - find registered domain token related to
+ * PCI device
+ * @dev: device
+ *
+ * Returns: domain token on success, NULL otherwise
+ */
+struct fwnode_handle *iort_find_pci_domain_token(struct device *dev)
+{
+ static struct fwnode_handle *domain_handle;
+ int its_id;
+
+ if (iort_pci_find_its_id(dev, 0, &its_id))
+ return NULL;
+
+ domain_handle = iort_find_its_domain_token(its_id);
+ if (!domain_handle)
+ return NULL;
+
+ return domain_handle;
+}
+
+static int
+iort_translate_dev_to_devid(struct acpi_iort_node *node, u32 req_id,
+ u32 *dev_id)
+{
+ u32 curr_id = req_id;
+
+ if (!node)
+ return -EINVAL;
+
+ /* Go upstream */
+ while (node->type != ACPI_IORT_NODE_ITS_GROUP) {
+ struct acpi_iort_id_mapping *id;
+ int i, found = 0;
+
+ /* Exit when no mapping array */
+ if (!node->mapping_offset || !node->mapping_count)
+ return -EINVAL;
+
+ id = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+ node->mapping_offset);
+
+ for (i = 0, found = 0; i < node->mapping_count; i++, id++) {
+ /*
+ * Single mapping is not translation rule,
+ * lets move on for this case
+ */
+ if (id->flags & ACPI_IORT_ID_SINGLE_MAPPING)
+ continue;
+
+ if (curr_id < id->input_base ||
+ (curr_id > id->input_base + id->id_count))
+ continue;
+
+ curr_id = id->output_base + (curr_id - id->input_base);
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ return -ENXIO;
+
+ node = iort_find_parent_node(node);
+ if (!node)
+ return -ENXIO;
+ }
+
+ *dev_id = curr_id;
+ return 0;
+}
+
+/**
+ * iort_find_pci_id() - find PCI device ID based on requester ID
+ * @dev: device
+ * @req_id: requester ID
+ * @dev_id: device ID
+ *
+ * Returns: 0 on success, appropriate error value otherwise
+ */
+int iort_find_pci_id(struct pci_dev *pdev, u32 req_id, u32 *dev_id)
+{
+ struct pci_bus *bus = pdev->bus;
+ struct acpi_iort_node *node;
+ int err;
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_find_dev_callback, &bus->dev);
+ if (!node) {
+ pr_err("can't find node related to %s device\n",
+ dev_name(&pdev->dev));
+ return -ENXIO;
+ }
+
+ err = iort_translate_dev_to_devid(node, req_id, dev_id);
+ return err;
+}
+
+static int __init iort_table_detect(void)
+{
+ acpi_status status;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
+ if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+ pr_err("Failed to get table, %s\n", msg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+arch_initcall(iort_table_detect);
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c
new file mode 100644
index 000000000000..ec5fe7b2de54
--- /dev/null
+++ b/drivers/acpi/mcfg.c
@@ -0,0 +1,201 @@
+/*
+ * MCFG ACPI table parser.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/ecam.h>
+#include <linux/pci.h>
+#include <linux/pci-acpi.h>
+
+#define PREFIX "MCFG: "
+
+/*
+ * raw_pci_read/write - raw ACPI PCI config space accessors.
+ *
+ * By defauly (__weak) these accessors are empty and should be overwritten
+ * by architectures which support operations on ACPI PCI_Config regions,
+ * see osl.c file.
+ */
+
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 *val)
+{
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 val)
+{
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+extern struct pci_mcfg_fixup __start_acpi_mcfg_fixups[];
+extern struct pci_mcfg_fixup __end_acpi_mcfg_fixups[];
+
+static struct pci_ops *pci_mcfg_check_quirks(struct acpi_pci_root *root)
+{
+ struct pci_mcfg_fixup *f;
+ int bus_num = root->secondary.start;
+ int domain = root->segment;
+
+ /*
+ * First match against PCI topology <domain:bus> then use DMI or
+ * custom match handler.
+ */
+ for (f = __start_acpi_mcfg_fixups; f < __end_acpi_mcfg_fixups; f++) {
+ if ((f->domain == domain || f->domain == PCI_MCFG_DOMAIN_ANY) &&
+ (f->bus_num == bus_num || f->bus_num == PCI_MCFG_BUS_ANY) &&
+ (f->system ? dmi_check_system(f->system) : 0 ||
+ f->match ? f->match(f, root) : 0))
+ return f->ops;
+ }
+ return NULL;
+}
+
+void __iomem *
+pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset)
+{
+ struct pci_mmcfg_region *cfg;
+
+ cfg = pci_mmconfig_lookup(pci_domain_nr(bus), bus->number);
+ if (cfg && cfg->virt)
+ return cfg->virt +
+ (PCI_MMCFG_BUS_OFFSET(bus->number) | (devfn << 12)) +
+ offset;
+ return NULL;
+}
+
+/* Default generic PCI config accessors */
+static struct pci_ops default_pci_mcfg_ops = {
+ .map_bus = pci_mcfg_dev_base,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
+};
+
+struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root)
+{
+ struct pci_ops *pci_mcfg_ops_quirk;
+
+ /*
+ * Match against platform specific quirks and return corresponding
+ * PCI config space accessor set.
+ */
+ pci_mcfg_ops_quirk = pci_mcfg_check_quirks(root);
+ if (pci_mcfg_ops_quirk)
+ return pci_mcfg_ops_quirk;
+
+ return &default_pci_mcfg_ops;
+}
+
+int __init acpi_parse_mcfg(struct acpi_table_header *header)
+{
+ struct acpi_table_mcfg *mcfg;
+ struct acpi_mcfg_allocation *cfg_table, *cfg;
+ unsigned long i;
+ int entries;
+
+ if (!header)
+ return -EINVAL;
+
+ mcfg = (struct acpi_table_mcfg *)header;
+
+ /* how many config structures do we have */
+ free_all_mmcfg();
+ entries = 0;
+ i = header->length - sizeof(struct acpi_table_mcfg);
+ while (i >= sizeof(struct acpi_mcfg_allocation)) {
+ entries++;
+ i -= sizeof(struct acpi_mcfg_allocation);
+ }
+ if (entries == 0) {
+ pr_err(PREFIX "MCFG table has no entries\n");
+ return -ENODEV;
+ }
+
+ cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+ for (i = 0; i < entries; i++) {
+ cfg = &cfg_table[i];
+ if (acpi_mcfg_check_entry(mcfg, cfg)) {
+ free_all_mmcfg();
+ return -ENODEV;
+ }
+
+ if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
+ cfg->end_bus_number, cfg->address) == NULL) {
+ pr_warn(PREFIX "no memory for MCFG entries\n");
+ free_all_mmcfg();
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci)
+{
+ struct pci_mmcfg_region *cfg;
+ struct acpi_pci_root *root;
+ int seg, start, end, err;
+
+ root = ci->root;
+ seg = root->segment;
+ start = root->secondary.start;
+ end = root->secondary.end;
+
+ cfg = pci_mmconfig_lookup(seg, start);
+ if (cfg)
+ return 0;
+
+ cfg = pci_mmconfig_alloc(seg, start, end, root->mcfg_addr);
+ if (!cfg)
+ return -ENOMEM;
+
+ err = pci_mmconfig_inject(cfg);
+ return err;
+}
+
+void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci)
+{
+ struct acpi_pci_root *root = ci->root;
+ struct pci_mmcfg_region *cfg;
+
+ cfg = pci_mmconfig_lookup(root->segment, root->secondary.start);
+ if (!cfg)
+ return;
+
+ if (cfg->hot_added)
+ pci_mmconfig_delete(root->segment, root->secondary.start,
+ root->secondary.end);
+}
+
+int __init __weak acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+ struct acpi_mcfg_allocation *cfg)
+{
+ return 0;
+}
+
+void __init __weak pci_mmcfg_early_init(void)
+{
+
+}
+
+void __init __weak pci_mmcfg_late_init(void)
+{
+ struct pci_mmcfg_region *cfg;
+
+ acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
+
+ if (list_empty(&pci_mmcfg_list))
+ return;
+ if (!pci_mmcfg_arch_init())
+ free_all_mmcfg();
+
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
+ insert_resource(&iomem_resource, &cfg->res);
+}
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index 72b6e9ef0ae9..b33915e15761 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -18,6 +18,9 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
+
+#define pr_fmt(fmt) "ACPI: " fmt
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -28,12 +31,6 @@
#include <linux/nodemask.h>
#include <linux/topology.h>
-#define PREFIX "ACPI: "
-
-#define ACPI_NUMA 0x80000000
-#define _COMPONENT ACPI_NUMA
-ACPI_MODULE_NAME("numa");
-
static nodemask_t nodes_found_map = NODE_MASK_NONE;
/* maps to convert between proximity domain and logical node ID */
@@ -128,68 +125,63 @@ EXPORT_SYMBOL(acpi_map_pxm_to_online_node);
static void __init
acpi_table_print_srat_entry(struct acpi_subtable_header *header)
{
-
- ACPI_FUNCTION_NAME("acpi_table_print_srat_entry");
-
- if (!header)
- return;
-
switch (header->type) {
-
case ACPI_SRAT_TYPE_CPU_AFFINITY:
-#ifdef ACPI_DEBUG_OUTPUT
{
struct acpi_srat_cpu_affinity *p =
(struct acpi_srat_cpu_affinity *)header;
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
- p->apic_id, p->local_sapic_eid,
- p->proximity_domain_lo,
- (p->flags & ACPI_SRAT_CPU_ENABLED)?
- "enabled" : "disabled"));
+ pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
+ p->apic_id, p->local_sapic_eid,
+ p->proximity_domain_lo,
+ (p->flags & ACPI_SRAT_CPU_ENABLED) ?
+ "enabled" : "disabled");
}
-#endif /* ACPI_DEBUG_OUTPUT */
break;
case ACPI_SRAT_TYPE_MEMORY_AFFINITY:
-#ifdef ACPI_DEBUG_OUTPUT
{
struct acpi_srat_mem_affinity *p =
(struct acpi_srat_mem_affinity *)header;
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n",
- (unsigned long)p->base_address,
- (unsigned long)p->length,
- p->proximity_domain,
- (p->flags & ACPI_SRAT_MEM_ENABLED)?
- "enabled" : "disabled",
- (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)?
- " hot-pluggable" : "",
- (p->flags & ACPI_SRAT_MEM_NON_VOLATILE)?
- " non-volatile" : ""));
+ pr_debug("SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n",
+ (unsigned long)p->base_address,
+ (unsigned long)p->length,
+ p->proximity_domain,
+ (p->flags & ACPI_SRAT_MEM_ENABLED) ?
+ "enabled" : "disabled",
+ (p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ?
+ " hot-pluggable" : "",
+ (p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ?
+ " non-volatile" : "");
}
-#endif /* ACPI_DEBUG_OUTPUT */
break;
case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY:
-#ifdef ACPI_DEBUG_OUTPUT
{
struct acpi_srat_x2apic_cpu_affinity *p =
(struct acpi_srat_x2apic_cpu_affinity *)header;
- ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "SRAT Processor (x2apicid[0x%08x]) in"
- " proximity domain %d %s\n",
- p->apic_id,
- p->proximity_domain,
- (p->flags & ACPI_SRAT_CPU_ENABLED) ?
- "enabled" : "disabled"));
+ pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s\n",
+ p->apic_id,
+ p->proximity_domain,
+ (p->flags & ACPI_SRAT_CPU_ENABLED) ?
+ "enabled" : "disabled");
+ }
+ break;
+
+ case ACPI_SRAT_TYPE_GICC_AFFINITY:
+ {
+ struct acpi_srat_gicc_affinity *p =
+ (struct acpi_srat_gicc_affinity *)header;
+ pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n",
+ p->acpi_processor_uid,
+ p->proximity_domain,
+ (p->flags & ACPI_SRAT_GICC_ENABLED) ?
+ "enabled" : "disabled");
}
-#endif /* ACPI_DEBUG_OUTPUT */
break;
+
default:
- printk(KERN_WARNING PREFIX
- "Found unsupported SRAT entry (type = 0x%x)\n",
- header->type);
+ pr_warn("Found unsupported SRAT entry (type = 0x%x)\n",
+ header->type);
break;
}
}
@@ -217,12 +209,42 @@ static int __init slit_valid(struct acpi_table_slit *slit)
return 1;
}
+/*
+ * Callback for SLIT parsing. It will get the distance information
+ * presented by SLIT and init the distance matrix of numa nodes
+ */
+void __init __weak acpi_numa_slit_init(struct acpi_table_slit *slit)
+{
+ int i, j;
+
+ for (i = 0; i < slit->locality_count; i++) {
+ const int from_node = pxm_to_node(i);
+
+ if (from_node == NUMA_NO_NODE)
+ continue;
+
+ for (j = 0; j < slit->locality_count; j++) {
+ const int to_node = pxm_to_node(j);
+
+ if (to_node == NUMA_NO_NODE)
+ continue;
+
+ numa_set_distance(from_node, to_node,
+ slit->entry[slit->locality_count * i + j]);
+
+ pr_debug("SLIT: Distance[%d][%d] = %d\n",
+ from_node, to_node,
+ slit->entry[slit->locality_count * i + j]);
+ }
+ }
+}
+
static int __init acpi_parse_slit(struct acpi_table_header *table)
{
struct acpi_table_slit *slit = (struct acpi_table_slit *)table;
if (!slit_valid(slit)) {
- printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n");
+ pr_info("SLIT table looks invalid. Not used.\n");
return -EINVAL;
}
acpi_numa_slit_init(slit);
@@ -233,12 +255,9 @@ static int __init acpi_parse_slit(struct acpi_table_header *table)
void __init __weak
acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
{
- printk(KERN_WARNING PREFIX
- "Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id);
- return;
+ pr_warn("Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id);
}
-
static int __init
acpi_parse_x2apic_affinity(struct acpi_subtable_header *header,
const unsigned long end)
@@ -275,6 +294,24 @@ acpi_parse_processor_affinity(struct acpi_subtable_header *header,
return 0;
}
+static int __init
+acpi_parse_gicc_affinity(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_srat_gicc_affinity *processor_affinity;
+
+ processor_affinity = (struct acpi_srat_gicc_affinity *)header;
+ if (!processor_affinity)
+ return -EINVAL;
+
+ acpi_table_print_srat_entry(header);
+
+ /* let architecture-dependent part to do it */
+ acpi_numa_gicc_affinity_init(processor_affinity);
+
+ return 0;
+}
+
static int __initdata parsed_numa_memblks;
static int __init
@@ -319,6 +356,9 @@ int __init acpi_numa_init(void)
{
int cnt = 0;
+ if (acpi_disabled)
+ return -EINVAL;
+
/*
* Should not limit number with cpu num that is from NR_CPUS or nr_cpus=
* SRAT cpu entries could have different order with that in MADT.
@@ -331,6 +371,8 @@ int __init acpi_numa_init(void)
acpi_parse_x2apic_affinity, 0);
acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY,
acpi_parse_processor_affinity, 0);
+ acpi_table_parse_srat(ACPI_SRAT_TYPE_GICC_AFFINITY,
+ acpi_parse_gicc_affinity, 0);
cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY,
acpi_parse_memory_affinity,
NR_NODE_MEMBLKS);
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index ae3fe4e64203..010216bea084 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -24,6 +24,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/mutex.h>
+#include <linux/of_address.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pci.h>
@@ -33,7 +34,6 @@
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/dmi.h>
-#include <acpi/apei.h> /* for acpi_hest_init() */
#include "internal.h"
@@ -514,6 +514,136 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm)
}
}
+#ifdef CONFIG_ACPI_PCI_HOST_GENERIC
+static int pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ if (pci_dev_msi_enabled(dev))
+ return 0;
+
+ acpi_pci_irq_enable(dev);
+ return dev->irq;
+}
+
+static void pci_mcfg_release_info(struct acpi_pci_root_info *ci)
+{
+ pci_mmcfg_teardown_map(ci);
+ kfree(ci);
+}
+
+static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
+{
+ struct list_head *list = &ci->resources;
+ struct acpi_device *device = ci->bridge;
+ struct resource_entry *entry, *tmp;
+ unsigned long flags;
+ int ret;
+
+ flags = IORESOURCE_IO | IORESOURCE_MEM;
+ ret = acpi_dev_get_resources(device, list,
+ acpi_dev_filter_resource_type_cb,
+ (void *)flags);
+ if (ret < 0) {
+ dev_warn(&device->dev,
+ "failed to parse _CRS method, error code %d\n", ret);
+ return ret;
+ } else if (ret == 0)
+ dev_dbg(&device->dev,
+ "no IO and memory resources present in _CRS\n");
+
+ resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
+ resource_size_t cpu_addr, length;
+ struct resource *res = entry->res;
+
+ if (entry->res->flags & IORESOURCE_DISABLED)
+ resource_list_destroy_entry(entry);
+ else
+ res->name = ci->name;
+
+ /* PCI -> CPU space translation */
+ cpu_addr = res->start + entry->offset;
+ length = res->end - res->start + 1;
+
+ if (res->flags & IORESOURCE_MEM) {
+ res->start = cpu_addr;
+ res->end = cpu_addr + length - 1;
+ } else if (res->flags & IORESOURCE_IO) {
+ resource_size_t pci_addr = res->start;
+ unsigned long port;
+
+ if (pci_register_io_range(cpu_addr, length)) {
+ resource_list_destroy_entry(entry);
+ continue;
+ }
+
+ port = pci_address_to_pio(cpu_addr);
+ if (port == (unsigned long)-1) {
+ resource_list_destroy_entry(entry);
+ continue;
+ }
+
+ res->start = port;
+ res->end = port + length - 1;
+ entry->offset = port - pci_addr;
+
+ if (pci_remap_iospace(res, cpu_addr) < 0)
+ resource_list_destroy_entry(entry);
+ }
+ }
+ return ret;
+}
+
+static struct acpi_pci_root_ops acpi_pci_root_ops = {
+ .init_info = pci_mmcfg_setup_map,
+ .release_info = pci_mcfg_release_info,
+ .prepare_resources = pci_acpi_root_prepare_resources,
+};
+
+/* Root bridge scanning */
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+{
+ int node = acpi_get_node(root->device->handle);
+ int domain = root->segment;
+ int busnum = root->secondary.start;
+ struct acpi_pci_root_info *info;
+ struct pci_host_bridge *bridge;
+ struct pci_bus *bus, *child;
+
+ if (domain && !pci_domains_supported) {
+ pr_warn("PCI %04x:%02x: multiple domains not supported.\n",
+ domain, busnum);
+ return NULL;
+ }
+
+ info = kzalloc_node(sizeof(*info), GFP_KERNEL, node);
+ if (!info) {
+ dev_err(&root->device->dev,
+ "pci_bus %04x:%02x: ignored (out of memory)\n",
+ domain, busnum);
+ return NULL;
+ }
+
+ acpi_pci_root_ops.pci_ops = pci_mcfg_get_ops(root);
+ bus = acpi_pci_root_create(root, &acpi_pci_root_ops, info, root);
+ if (!bus)
+ return NULL;
+
+ bridge = pci_find_host_bridge(bus);
+ bridge->map_irq = pcibios_map_irq;
+
+ pci_bus_size_bridges(bus);
+ pci_bus_assign_resources(bus);
+
+ /*
+ * After the PCI-E bus has been walked and all devices discovered,
+ * configure any settings of the fabric that might be necessary.
+ */
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
+
+ return bus;
+}
+#endif /* CONFIG_ARCH_PCI_HOST_GENERIC_ACPI */
+
static int acpi_pci_root_add(struct acpi_device *device,
const struct acpi_device_id *not_used)
{
@@ -846,7 +976,10 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
pci_acpi_root_add_resources(info);
pci_add_resource(&info->resources, &root->secondary);
- bus = pci_create_root_bus(NULL, busnum, ops->pci_ops,
+
+ /* Root bridge device needs to be sure of parent ACPI type */
+ ACPI_COMPANION_SET(&device->dev, device);
+ bus = pci_create_root_bus(&device->dev, busnum, ops->pci_ops,
sysdata, &info->resources);
if (!bus)
goto out_release_info;
@@ -865,7 +998,6 @@ out_release_info:
void __init acpi_pci_root_init(void)
{
- acpi_hest_init();
if (acpi_pci_disabled)
return;
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index cdc5c2599beb..6578f68b17be 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -190,8 +190,7 @@ static bool acpi_decode_space(struct resource_win *win,
{
u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16;
bool wp = addr->info.mem.write_protect;
- u64 len = attr->address_length;
- u64 start, end, offset = 0;
+ u64 len = attr->address_length, offset = 0;
struct resource *res = &win->res;
/*
@@ -215,14 +214,13 @@ static bool acpi_decode_space(struct resource_win *win,
else if (attr->translation_offset)
pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n",
attr->translation_offset);
- start = attr->minimum + offset;
- end = attr->maximum + offset;
win->offset = offset;
- res->start = start;
- res->end = end;
+ res->start = attr->minimum;
+ res->end = attr->maximum;
if (sizeof(resource_size_t) < sizeof(u64) &&
- (offset != win->offset || start != res->start || end != res->end)) {
+ (offset != win->offset || attr->minimum != res->start ||
+ attr->maximum != res->end)) {
pr_warn("acpi resource window ([%#llx-%#llx] ignored, not CPU addressable)\n",
attr->minimum, attr->maximum);
return false;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 78d5f02a073b..96721e28e49a 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -13,6 +13,8 @@
#include <linux/nls.h>
#include <linux/dma-mapping.h>
+#include <acpi/apei.h> /* for acpi_hest_init() */
+
#include <asm/pgtable.h>
#include "internal.h"
@@ -1912,6 +1914,7 @@ int __init acpi_scan_init(void)
{
int result;
+ acpi_hest_init();
acpi_pci_root_init();
acpi_pci_link_init();
acpi_processor_init();
@@ -1922,6 +1925,7 @@ int __init acpi_scan_init(void)
acpi_memory_hotplug_init();
acpi_pnp_init();
acpi_int340x_thermal_init();
+ acpi_amba_init();
acpi_scan_add_handler(&generic_device_handler);
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
new file mode 100644
index 000000000000..ccb19a0d4bc2
--- /dev/null
+++ b/drivers/acpi/spcr.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ * Copyright (c) 2015, Red Hat, Inc.
+ * Copyright (c) 2015, 2016 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) "ACPI: SPCR: " fmt
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/kernel.h>
+
+static struct acpi_table_spcr *spcr_table;
+
+int console_acpi_match(struct console *c, char **options)
+{
+ int err;
+
+ if (!c->acpi_match)
+ return -ENODEV;
+
+ if (!spcr_table)
+ return -EAGAIN;
+
+ err = c->acpi_match(c, spcr_table);
+ if (err < 0)
+ return err;
+
+ if (options) {
+ switch (spcr_table->baud_rate) {
+ case 3:
+ *options = "9600";
+ break;
+ case 4:
+ *options = "19200";
+ break;
+ case 6:
+ *options = "57600";
+ break;
+ case 7:
+ *options = "115200";
+ break;
+ default:
+ *options = "";
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int __init spcr_table_detect(void)
+{
+ struct acpi_table_header *table;
+ acpi_status status;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ status = acpi_get_table(ACPI_SIG_SPCR, 0, &table);
+ if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+
+ pr_err("Failed to get table, %s\n", msg);
+ return -EINVAL;
+ }
+
+ if (table->revision < 2)
+ return -EOPNOTSUPP;
+
+ spcr_table = (struct acpi_table_spcr *)table;
+
+ pr_info("Console at 0x%016llx\n", spcr_table->serial_port.address);
+
+ acpi_register_consoles_try_again();
+
+ return 0;
+}
+
+arch_initcall(spcr_table_detect);
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index c64d543d64bf..bff4f73c9f28 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -40,8 +40,6 @@
#define CNTV_TVAL 0x38
#define CNTV_CTL 0x3c
-#define ARCH_CP15_TIMER BIT(0)
-#define ARCH_MEM_TIMER BIT(1)
static unsigned arch_timers_present __initdata;
static void __iomem *arch_counter_base;
@@ -668,6 +666,15 @@ arch_timer_needs_probing(int type, const struct of_device_id *matches)
needs_probing = true;
of_node_put(dn);
+#ifdef CONFIG_ACPI
+ /*
+ * Check if we have timer in GTDT table
+ */
+ if (!acpi_disabled && gtdt_timer_is_available(type) &&
+ !(arch_timers_present & type))
+ needs_probing = true;
+#endif
+
return needs_probing;
}
@@ -809,60 +816,159 @@ CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
arch_timer_mem_init);
#ifdef CONFIG_ACPI
-static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
+/* Initialize per-processor generic timer */
+static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
- int trigger, polarity;
+ struct arch_timer_data data;
- if (!interrupt)
- return 0;
+ if (arch_timers_present & ARCH_CP15_TIMER) {
+ pr_warn("arch_timer: already initialized, skipping\n");
+ return -EINVAL;
+ }
+
+ arch_timers_present |= ARCH_CP15_TIMER;
- trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
- : ACPI_LEVEL_SENSITIVE;
+ if (gtdt_arch_timer_data_init(table, &data))
+ return -EINVAL;
+
+ arch_timer_ppi[PHYS_SECURE_PPI] = data.phys_secure_ppi;
+ arch_timer_ppi[PHYS_NONSECURE_PPI] = data.phys_nonsecure_ppi;
+ arch_timer_ppi[VIRT_PPI] = data.virt_ppi;
+ arch_timer_ppi[HYP_PPI] = data.hyp_ppi;
+ arch_timer_c3stop = data.c3stop; /* Always-on capability */
- polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
- : ACPI_ACTIVE_HIGH;
+ /* Get the frequency from CNTFRQ */
+ arch_timer_detect_rate(NULL, NULL);
+ arch_timer_init();
- return acpi_register_gsi(NULL, interrupt, trigger, polarity);
+ return 0;
}
+CLOCKSOURCE_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init);
-/* Initialize per-processor generic timer */
-static int __init arch_timer_acpi_init(struct acpi_table_header *table)
+static u32 __init arch_timer_mem_cnttidr(struct acpi_gtdt_timer_block *gt_block)
{
- struct acpi_table_gtdt *gtdt;
+ phys_addr_t cntctlbase_phy;
+ void __iomem *cntctlbase;
+ u32 cnttidr;
- if (arch_timers_present & ARCH_CP15_TIMER) {
- pr_warn("arch_timer: already initialized, skipping\n");
+ cntctlbase_phy = (phys_addr_t)gtdt_gt_cntctlbase(gt_block);
+ if (!cntctlbase_phy) {
+ pr_err("arch_timer: Can't find CNTCTLBase.\n");
+ return 0;
+ }
+
+ /*
+ * According to ARMv8 Architecture Reference Manual(ARM),
+ * the size of CNTCTLBase frame of memory-mapped timer
+ * is SZ_4K(Offset 0x000 – 0xFFF).
+ */
+ cntctlbase = ioremap(cntctlbase_phy, SZ_4K);
+ if (!cntctlbase) {
+ pr_err("arch_timer: Can't map CNTCTLBase\n");
+ return 0;
+ }
+ cnttidr = readl_relaxed(cntctlbase + CNTTIDR);
+ iounmap(cntctlbase);
+
+ return cnttidr;
+}
+
+static int __init arch_timer_mem_best_frame(struct acpi_table_header *table,
+ struct arch_timer_mem_data *data)
+{
+ struct acpi_gtdt_timer_block *gt_block;
+ u32 frame_number, timer_count, cnttidr;
+ int i;
+
+ gt_block = gtdt_gt_block(table, 0);
+ if (!gt_block) {
+ pr_err("arch_timer: Can't find GT Block.\n");
return -EINVAL;
}
- gtdt = container_of(table, struct acpi_table_gtdt, header);
+ timer_count = gtdt_gt_timer_count(gt_block);
+ if (!timer_count) {
+ pr_err("arch_timer: Can't find GT frame number.\n");
+ return -EINVAL;
+ }
- arch_timers_present |= ARCH_CP15_TIMER;
+ if (gtdt_gt_timer_data(gt_block, 0, false, data)) {
+ pr_err("arch_timer: Can't get first phy timer.\n");
+ return -EINVAL;
+ }
- arch_timer_ppi[PHYS_SECURE_PPI] =
- map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
- gtdt->secure_el1_flags);
+ /*
+ * Get Generic Timer Counter-timer Timer ID Register
+ * for Virtual Timer Capability info
+ */
+ cnttidr = arch_timer_mem_cnttidr(gt_block);
- arch_timer_ppi[PHYS_NONSECURE_PPI] =
- map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
- gtdt->non_secure_el1_flags);
+ /*
+ * Try to find a virtual capable frame.
+ * Otherwise fall back to the first physical capable frame.
+ */
+ for (i = 0; i < timer_count; i++) {
+ frame_number = gtdt_gt_frame_number(gt_block, i);
+ if (frame_number < ARCH_TIMER_MEM_MAX_FRAME &&
+ cnttidr & CNTTIDR_VIRT(frame_number)) {
+ if (!gtdt_gt_timer_data(gt_block, i, true, data)) {
+ arch_timer_mem_use_virtual = true;
+ return 0;
+ }
+ pr_warn("arch_timer: Can't get virt timer.\n");
+ }
+ }
- arch_timer_ppi[VIRT_PPI] =
- map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
- gtdt->virtual_timer_flags);
+ return 0;
+}
- arch_timer_ppi[HYP_PPI] =
- map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
- gtdt->non_secure_el2_flags);
+/* Initialize memory-mapped timer(wake-up timer) */
+static int __init arch_timer_mem_acpi_init(struct acpi_table_header *table)
+{
+ struct arch_timer_mem_data data;
+ void __iomem *cntbase;
- /* Get the frequency from CNTFRQ */
+ if (arch_timers_present & ARCH_MEM_TIMER) {
+ pr_warn("arch_timer_mem: already initialized, skipping\n");
+ return -EINVAL;
+ }
+ arch_timers_present |= ARCH_MEM_TIMER;
+
+ if (arch_timer_mem_best_frame(table, &data))
+ return -EINVAL;
+
+ /*
+ * According to ARMv8 Architecture Reference Manual(ARM),
+ * the size of CNTBaseN frames of memory-mapped timer
+ * is SZ_4K(Offset 0x000 – 0xFFF).
+ */
+ cntbase = ioremap(data.cntbase_phy, SZ_4K);
+ if (!cntbase) {
+ pr_err("arch_timer: Can't map CntBase.\n");
+ return -EINVAL;
+ }
+ arch_counter_base = cntbase;
+
+ if (!data.irq) {
+ pr_err("arch_timer: Frame missing %s irq",
+ arch_timer_mem_use_virtual ? "virt" : "phys");
+ return -EINVAL;
+ }
+
+ /*
+ * Because in a system that implements both Secure and
+ * Non-secure states, CNTFRQ is only accessible in Secure state.
+ * So we try to get the system counter frequency from cntfrq_el0
+ * (system coprocessor register) here just like arch_timer.
+ */
arch_timer_detect_rate(NULL, NULL);
- /* Always-on capability */
- arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
+ arch_timer_mem_register(cntbase, data.irq);
+ arch_timer_common_init();
- arch_timer_init();
return 0;
}
-CLOCKSOURCE_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init);
+
+CLOCKSOURCE_ACPI_DECLARE(arch_timer_mem, ACPI_SIG_GTDT,
+ arch_timer_mem_acpi_init);
#endif
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 4d7294e5d982..90f00b123746 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -26,6 +26,7 @@ config ARM_GIC_V3
config ARM_GIC_V3_ITS
bool
select PCI_MSI_IRQ_DOMAIN
+ select IORT_TABLE if ACPI
config ARM_NVIC
bool
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index 87f8d104acab..d9334765f8c3 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -15,9 +15,11 @@
#define pr_fmt(fmt) "GICv2m: " fmt
+#include <linux/acpi.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
+#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/slab.h>
@@ -55,7 +57,7 @@ static DEFINE_SPINLOCK(v2m_lock);
struct v2m_data {
struct list_head entry;
- struct device_node *node;
+ struct fwnode_handle *fwnode;
struct resource res; /* GICv2m resource */
void __iomem *base; /* GICv2m virt address */
u32 spi_start; /* The SPI number that MSIs start */
@@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
fwspec.param[0] = 0;
fwspec.param[1] = hwirq - 32;
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+ } else if (is_fwnode_irqchip(domain->parent->fwnode)) {
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = hwirq;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
} else {
return -EINVAL;
}
@@ -254,7 +261,9 @@ static void gicv2m_teardown(void)
list_del(&v2m->entry);
kfree(v2m->bm);
iounmap(v2m->base);
- of_node_put(v2m->node);
+ of_node_put(to_of_node(v2m->fwnode));
+ if (is_fwnode_irqchip(v2m->fwnode))
+ irq_domain_free_fwnode(v2m->fwnode);
kfree(v2m);
}
}
@@ -268,7 +277,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
if (!v2m)
return 0;
- inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node),
+ inner_domain = irq_domain_create_tree(v2m->fwnode,
&gicv2m_domain_ops, v2m);
if (!inner_domain) {
pr_err("Failed to create GICv2m domain\n");
@@ -277,10 +286,10 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
inner_domain->bus_token = DOMAIN_BUS_NEXUS;
inner_domain->parent = parent;
- pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+ pci_domain = pci_msi_create_irq_domain(v2m->fwnode,
&gicv2m_msi_domain_info,
inner_domain);
- plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+ plat_domain = platform_msi_create_irq_domain(v2m->fwnode,
&gicv2m_pmsi_domain_info,
inner_domain);
if (!pci_domain || !plat_domain) {
@@ -296,8 +305,9 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
return 0;
}
-static int __init gicv2m_init_one(struct device_node *node,
- struct irq_domain *parent)
+static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
+ u32 spi_start, u32 nr_spis,
+ struct resource *res)
{
int ret;
struct v2m_data *v2m;
@@ -309,13 +319,9 @@ static int __init gicv2m_init_one(struct device_node *node,
}
INIT_LIST_HEAD(&v2m->entry);
- v2m->node = node;
+ v2m->fwnode = fwnode;
- ret = of_address_to_resource(node, 0, &v2m->res);
- if (ret) {
- pr_err("Failed to allocate v2m resource.\n");
- goto err_free_v2m;
- }
+ memcpy(&v2m->res, res, sizeof(struct resource));
v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res));
if (!v2m->base) {
@@ -324,10 +330,9 @@ static int __init gicv2m_init_one(struct device_node *node,
goto err_free_v2m;
}
- if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) &&
- !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) {
- pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n",
- v2m->spi_start, v2m->nr_spis);
+ if (spi_start && nr_spis) {
+ v2m->spi_start = spi_start;
+ v2m->nr_spis = nr_spis;
} else {
u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
@@ -359,10 +364,10 @@ static int __init gicv2m_init_one(struct device_node *node,
}
list_add_tail(&v2m->entry, &v2m_nodes);
- pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name,
- (unsigned long)v2m->res.start, (unsigned long)v2m->res.end,
- v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
+ pr_info("range[%#lx:%#lx], SPI[%d:%d]\n",
+ (unsigned long)res->start, (unsigned long)res->end,
+ v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
return 0;
err_iounmap:
@@ -377,17 +382,34 @@ static struct of_device_id gicv2m_device_id[] = {
{},
};
-int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
+static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
+ struct irq_domain *parent)
{
int ret = 0;
+ struct device_node *node = to_of_node(parent_handle);
struct device_node *child;
for (child = of_find_matching_node(node, gicv2m_device_id); child;
child = of_find_matching_node(child, gicv2m_device_id)) {
+ u32 spi_start = 0, nr_spis = 0;
+ struct resource res;
+
if (!of_find_property(child, "msi-controller", NULL))
continue;
- ret = gicv2m_init_one(child, parent);
+ ret = of_address_to_resource(child, 0, &res);
+ if (ret) {
+ pr_err("Failed to allocate v2m resource.\n");
+ break;
+ }
+
+ if (!of_property_read_u32(child, "arm,msi-base-spi",
+ &spi_start) &&
+ !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis))
+ pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
+ spi_start, nr_spis);
+
+ ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res);
if (ret) {
of_node_put(node);
break;
@@ -400,3 +422,97 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
gicv2m_teardown();
return ret;
}
+
+#ifdef CONFIG_ACPI
+static int acpi_num_msi;
+
+static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
+{
+ struct v2m_data *data;
+
+ if (WARN_ON(acpi_num_msi <= 0))
+ return NULL;
+
+ /* We only return the fwnode of the first MSI frame. */
+ data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
+ if (!data)
+ return NULL;
+
+ return data->fwnode;
+}
+
+static int __init
+acpi_parse_madt_msi(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ int ret;
+ struct resource res;
+ u32 spi_start = 0, nr_spis = 0;
+ struct acpi_madt_generic_msi_frame *m;
+ struct fwnode_handle *fwnode;
+
+ m = (struct acpi_madt_generic_msi_frame *)header;
+ res.start = m->base_address;
+ res.end = m->base_address + SZ_4K;
+
+ if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
+ spi_start = m->spi_base;
+ nr_spis = m->spi_count;
+
+ pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
+ spi_start, nr_spis);
+ }
+
+ fwnode = irq_domain_alloc_fwnode((void *)m->base_address);
+ if (!fwnode) {
+ pr_err("Unable to allocate GICv2m domain token\n");
+ return -EINVAL;
+ }
+
+ ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res);
+ if (ret)
+ irq_domain_free_fwnode(fwnode);
+
+ return ret;
+}
+
+static int __init gicv2m_acpi_init(struct irq_domain *parent)
+{
+ int ret;
+
+ if (acpi_num_msi > 0)
+ return 0;
+
+ acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME,
+ acpi_parse_madt_msi, 0);
+
+ if (acpi_num_msi <= 0)
+ goto err_out;
+
+ ret = gicv2m_allocate_domains(parent);
+ if (ret)
+ goto err_out;
+
+ pci_msi_register_fwnode_provider(&gicv2m_get_fwnode);
+
+ return 0;
+
+err_out:
+ gicv2m_teardown();
+ return -EINVAL;
+}
+#else /* CONFIG_ACPI */
+static int __init gicv2m_acpi_init(struct irq_domain *parent)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_ACPI */
+
+int __init gicv2m_init(struct fwnode_handle *parent_handle,
+ struct irq_domain *parent)
+{
+ if (is_of_node(parent_handle))
+ return gicv2m_of_init(parent_handle, parent);
+
+ return gicv2m_acpi_init(parent);
+}
diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
index aee60ed025dc..7f0a95849b26 100644
--- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c
+++ b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
@@ -15,6 +15,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/acpi.h>
+#include <linux/iort.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -106,34 +108,87 @@ static struct of_device_id its_device_id[] = {
{},
};
-static int __init its_pci_msi_init(void)
+static int __init its_pci_msi_init_one(struct fwnode_handle *handle)
{
- struct device_node *np;
struct irq_domain *parent;
+ parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
+ if (!parent || !msi_get_domain_info(parent)) {
+ pr_err("Unable to locate ITS domain\n");
+ return -ENXIO;
+ }
+
+ if (!pci_msi_create_irq_domain(handle, &its_pci_msi_domain_info,
+ parent)) {
+ pr_err("Unable to create PCI domain\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int __init its_pci_of_msi_init(void)
+{
+ struct device_node *np;
+
for (np = of_find_matching_node(NULL, its_device_id); np;
np = of_find_matching_node(np, its_device_id)) {
if (!of_property_read_bool(np, "msi-controller"))
continue;
- parent = irq_find_matching_host(np, DOMAIN_BUS_NEXUS);
- if (!parent || !msi_get_domain_info(parent)) {
- pr_err("%s: unable to locate ITS domain\n",
- np->full_name);
- continue;
- }
-
- if (!pci_msi_create_irq_domain(of_node_to_fwnode(np),
- &its_pci_msi_domain_info,
- parent)) {
- pr_err("%s: unable to create PCI domain\n",
- np->full_name);
+ if (its_pci_msi_init_one(of_node_to_fwnode(np)))
continue;
- }
pr_info("PCI/MSI: %s domain created\n", np->full_name);
}
return 0;
}
+
+#ifdef CONFIG_ACPI
+
+static int __init
+its_pci_msi_parse_madt(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_translator *its_entry;
+ struct fwnode_handle *domain_handle;
+
+ its_entry = (struct acpi_madt_generic_translator *)header;
+ domain_handle = iort_find_its_domain_token(its_entry->translation_id);
+ if (!domain_handle) {
+ pr_err("ITS@0x%lx: Unable to locate ITS domain handle\n",
+ (long)its_entry->base_address);
+ return 0;
+ }
+
+ if (its_pci_msi_init_one(domain_handle))
+ return 0;
+
+ pci_msi_register_fwnode_provider(&iort_find_pci_domain_token);
+ pr_info("PCI/MSI: ITS@0x%lx domain created\n",
+ (long)its_entry->base_address);
+ return 0;
+}
+
+static int __init its_pci_acpi_msi_init(void)
+{
+ acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
+ its_pci_msi_parse_madt, 0);
+ return 0;
+}
+#else
+inline static int __init its_pci_acpi_msi_init(void)
+{
+ return 0;
+}
+#endif
+
+static int __init its_pci_msi_init(void)
+{
+ its_pci_of_msi_init();
+ its_pci_acpi_msi_init();
+
+ return 0;
+}
early_initcall(its_pci_msi_init);
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index e23d1d18f9d6..42f378a795f2 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -15,10 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/iort.h>
#include <linux/log2.h>
#include <linux/mm.h>
#include <linux/msi.h>
@@ -97,7 +100,6 @@ struct its_device {
static LIST_HEAD(its_nodes);
static DEFINE_SPINLOCK(its_lock);
-static struct device_node *gic_root_node;
static struct rdists *gic_rdists;
#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist))
@@ -670,7 +672,7 @@ static int its_chunk_to_lpi(int chunk)
return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192;
}
-static int its_lpi_init(u32 id_bits)
+static int __init its_lpi_init(u32 id_bits)
{
lpi_chunks = its_lpi_to_chunk(1UL << id_bits);
@@ -814,7 +816,7 @@ static void its_free_tables(struct its_node *its)
}
}
-static int its_alloc_tables(const char *node_name, struct its_node *its)
+static int its_alloc_tables(struct its_node *its)
{
int err;
int i;
@@ -869,8 +871,8 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
order);
if (order >= MAX_ORDER) {
order = MAX_ORDER - 1;
- pr_warn("%s: Device Table too large, reduce its page order to %u\n",
- node_name, order);
+ pr_warn("ITS@0x%lx: Device Table too large, reduce its page order to %u\n",
+ its->phys_base, order);
}
}
@@ -879,8 +881,8 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
if (alloc_pages > GITS_BASER_PAGES_MAX) {
alloc_pages = GITS_BASER_PAGES_MAX;
order = get_order(GITS_BASER_PAGES_MAX * psz);
- pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n",
- node_name, order, alloc_pages);
+ pr_warn("ITS@0x%lx: Device Table too large, reduce its page order to %u (%u pages)\n",
+ its->phys_base, order, alloc_pages);
}
base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
@@ -949,8 +951,8 @@ retry_baser:
}
if (val != tmp) {
- pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n",
- node_name, i,
+ pr_err("ITS@0x%lx: GITS_BASER%d doesn't stick: %lx %lx\n",
+ its->phys_base, i,
(unsigned long) val, (unsigned long) tmp);
err = -ENXIO;
goto out_free;
@@ -1273,6 +1275,11 @@ static int its_irq_gic_domain_alloc(struct irq_domain *domain,
fwspec.param[0] = GIC_IRQ_TYPE_LPI;
fwspec.param[1] = hwirq;
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+ } else if (is_fwnode_irqchip(domain->parent->fwnode)) {
+ fwspec.fwnode = domain->parent->fwnode;
+ fwspec.param_count = 2;
+ fwspec.param[0] = hwirq;
+ fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
} else {
return -EINVAL;
}
@@ -1425,9 +1432,11 @@ static void its_enable_quirks(struct its_node *its)
gic_enable_quirks(iidr, its_quirks, its);
}
-static int its_probe(struct device_node *node, struct irq_domain *parent)
+static int __init its_probe_one(phys_addr_t phys_base, unsigned long size,
+ struct irq_domain *parent,
+ bool is_msi_controller,
+ struct fwnode_handle *handler)
{
- struct resource res;
struct its_node *its;
void __iomem *its_base;
struct irq_domain *inner_domain;
@@ -1435,33 +1444,26 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
u64 baser, tmp;
int err;
- err = of_address_to_resource(node, 0, &res);
- if (err) {
- pr_warn("%s: no regs?\n", node->full_name);
- return -ENXIO;
- }
-
- its_base = ioremap(res.start, resource_size(&res));
+ its_base = ioremap(phys_base, size);
if (!its_base) {
- pr_warn("%s: unable to map registers\n", node->full_name);
+ pr_warn("Unable to map ITS registers\n");
return -ENOMEM;
}
val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK;
if (val != 0x30 && val != 0x40) {
- pr_warn("%s: no ITS detected, giving up\n", node->full_name);
+ pr_warn("No ITS detected, giving up\n");
err = -ENODEV;
goto out_unmap;
}
err = its_force_quiescent(its_base);
if (err) {
- pr_warn("%s: failed to quiesce, giving up\n",
- node->full_name);
+ pr_warn("Failed to quiesce, giving up\n");
goto out_unmap;
}
- pr_info("ITS: %s\n", node->full_name);
+ pr_info("ITS@0x%lx\n", (long)phys_base);
its = kzalloc(sizeof(*its), GFP_KERNEL);
if (!its) {
@@ -1473,7 +1475,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
INIT_LIST_HEAD(&its->entry);
INIT_LIST_HEAD(&its->its_device_list);
its->base = its_base;
- its->phys_base = res.start;
+ its->phys_base = phys_base;
its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL);
@@ -1485,7 +1487,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
its_enable_quirks(its);
- err = its_alloc_tables(node->full_name, its);
+ err = its_alloc_tables(its);
if (err)
goto out_free_cmd;
@@ -1521,7 +1523,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
writeq_relaxed(0, its->base + GITS_CWRITER);
writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
- if (of_property_read_bool(node, "msi-controller")) {
+ if (is_msi_controller) {
struct msi_domain_info *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -1530,7 +1532,8 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
goto out_free_tables;
}
- inner_domain = irq_domain_add_tree(node, &its_domain_ops, its);
+ inner_domain = irq_domain_create_tree(handler, &its_domain_ops,
+ its);
if (!inner_domain) {
err = -ENOMEM;
kfree(info);
@@ -1558,10 +1561,28 @@ out_free_its:
kfree(its);
out_unmap:
iounmap(its_base);
- pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
+ pr_err("ITS@0x%lx: failed probing (%d)\n", (long)phys_base, err);
return err;
}
+static int __init
+its_of_probe(struct device_node *node, struct irq_domain *parent)
+{
+ struct resource res;
+ bool is_msi_controller = false;
+
+ if (of_address_to_resource(node, 0, &res)) {
+ pr_warn("%s: no regs?\n", node->full_name);
+ return -ENXIO;
+ }
+
+ if (of_property_read_bool(node, "msi-controller"))
+ is_msi_controller = true;
+
+ return its_probe_one(res.start, resource_size(&res), parent,
+ is_msi_controller, &node->fwnode);
+}
+
static bool gic_rdists_supports_plpis(void)
{
return !!(readl_relaxed(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS);
@@ -1581,20 +1602,70 @@ int its_cpu_init(void)
return 0;
}
+#ifdef CONFIG_ACPI
+
+#define ACPI_GICV3_ITS_MEM_SIZE (2 * SZ_64K)
+
+static struct irq_domain *its_parent __initdata;
+
+static int __init
+gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_translator *its_entry;
+ struct fwnode_handle *domain_handle;
+ int err;
+
+ its_entry = (struct acpi_madt_generic_translator *)header;
+ domain_handle = irq_domain_alloc_fwnode((void *)its_entry->base_address);
+ if (!domain_handle) {
+ pr_err("Unable to allocate GICv2m domain token\n");
+ return -ENOMEM;
+ }
+
+ /* ITS works as msi controller in ACPI case */
+ err = its_probe_one(its_entry->base_address, ACPI_GICV3_ITS_MEM_SIZE,
+ its_parent, true, domain_handle);
+ if (err) {
+ irq_domain_free_fwnode(domain_handle);
+ return err;
+ }
+ iort_register_domain_token(its_entry->translation_id, domain_handle);
+ return 0;
+}
+
+void __init its_acpi_probe(struct irq_domain *parent_domain)
+{
+ int count;
+
+ its_parent = parent_domain;
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
+ gic_acpi_parse_madt_its, 0);
+ if (count <= 0)
+ pr_info("No valid GIC ITS entries exist\n");
+}
+#else
+static inline void __init its_acpi_probe(struct irq_domain *parent_domain) { }
+#endif
+
static struct of_device_id its_device_id[] = {
{ .compatible = "arm,gic-v3-its", },
{},
};
-int its_init(struct device_node *node, struct rdists *rdists,
- struct irq_domain *parent_domain)
+int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
+ struct irq_domain *parent_domain)
{
- struct device_node *np;
+ struct device_node *np, *of_node;
- for (np = of_find_matching_node(node, its_device_id); np;
- np = of_find_matching_node(np, its_device_id)) {
- its_probe(np, parent_domain);
- }
+ of_node = to_of_node(handle);
+ if (of_node) {
+ for (np = of_find_matching_node(of_node, its_device_id); np;
+ np = of_find_matching_node(np, its_device_id)) {
+ its_of_probe(np, parent_domain);
+ }
+ } else
+ its_acpi_probe(parent_domain);
if (list_empty(&its_nodes)) {
pr_warn("ITS: No ITS available, not enabling LPIs\n");
@@ -1602,8 +1673,6 @@ int its_init(struct device_node *node, struct rdists *rdists,
}
gic_rdists = rdists;
- gic_root_node = node;
-
its_alloc_lpi_tables();
its_lpi_init(rdists->id_bits);
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index d7be6ddc34f6..b08f83bc9a49 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -15,10 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/acpi.h>
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
@@ -38,6 +40,7 @@
struct redist_region {
void __iomem *redist_base;
phys_addr_t phys_base;
+ bool single_redist;
};
struct gic_chip_data {
@@ -434,6 +437,9 @@ static int gic_populate_rdist(void)
return 0;
}
+ if (gic_data.redist_regions[i].single_redist)
+ break;
+
if (gic_data.redist_stride) {
ptr += gic_data.redist_stride;
} else {
@@ -764,6 +770,15 @@ static int gic_irq_domain_translate(struct irq_domain *d,
return 0;
}
+ if (is_fwnode_irqchip(fwspec->fwnode)) {
+ if(fwspec->param_count != 2)
+ return -EINVAL;
+
+ *hwirq = fwspec->param[0];
+ *type = fwspec->param[1];
+ return 0;
+ }
+
return -EINVAL;
}
@@ -811,17 +826,85 @@ static void gicv3_enable_quirks(void)
#endif
}
+static int __init gic_init_bases(void __iomem *dist_base,
+ struct redist_region *rdist_regs,
+ u32 nr_redist_regions,
+ u64 redist_stride,
+ struct fwnode_handle *handle)
+{
+ u32 typer;
+ int gic_irqs;
+ int err;
+
+ if (!is_hyp_mode_available())
+ static_key_slow_dec(&supports_deactivate);
+
+ if (static_key_true(&supports_deactivate))
+ pr_info("GIC: Using split EOI/Deactivate mode\n");
+
+ gic_data.dist_base = dist_base;
+ gic_data.redist_regions = rdist_regs;
+ gic_data.nr_redist_regions = nr_redist_regions;
+ gic_data.redist_stride = redist_stride;
+
+ gicv3_enable_quirks();
+
+ /*
+ * Find out how many interrupts are supported.
+ * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
+ */
+ typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
+ gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer);
+ gic_irqs = GICD_TYPER_IRQS(typer);
+ if (gic_irqs > 1020)
+ gic_irqs = 1020;
+ gic_data.irq_nr = gic_irqs;
+
+ gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,
+ &gic_data);
+ gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
+
+ if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+
+ set_handle_irq(gic_handle_irq);
+
+ if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
+ its_init(handle, &gic_data.rdists, gic_data.domain);
+
+ gic_smp_init();
+ gic_dist_init();
+ gic_cpu_init();
+ gic_cpu_pm_init();
+
+ return 0;
+
+out_free:
+ if (gic_data.domain)
+ irq_domain_remove(gic_data.domain);
+ free_percpu(gic_data.rdists.rdist);
+ return err;
+}
+
+static int __init gic_validate_dist_version(void __iomem *dist_base)
+{
+ u32 reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
+
+ if (reg != GIC_PIDR2_ARCH_GICv3 && reg != GIC_PIDR2_ARCH_GICv4)
+ return -ENODEV;
+
+ return 0;
+}
+
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *dist_base;
struct redist_region *rdist_regs;
u64 redist_stride;
u32 nr_redist_regions;
- u32 typer;
- u32 reg;
- int gic_irqs;
- int err;
- int i;
+ int err, i;
dist_base = of_iomap(node, 0);
if (!dist_base) {
@@ -830,11 +913,10 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
return -ENXIO;
}
- reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
- if (reg != GIC_PIDR2_ARCH_GICv3 && reg != GIC_PIDR2_ARCH_GICv4) {
+ err = gic_validate_dist_version(dist_base);
+ if (err) {
pr_err("%s: no distributor detected, giving up\n",
node->full_name);
- err = -ENODEV;
goto out_unmap_dist;
}
@@ -865,63 +947,233 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
redist_stride = 0;
- if (!is_hyp_mode_available())
- static_key_slow_dec(&supports_deactivate);
+ err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
+ redist_stride, &node->fwnode);
+ if (!err)
+ return 0;
- if (static_key_true(&supports_deactivate))
- pr_info("GIC: Using split EOI/Deactivate mode\n");
+out_unmap_rdist:
+ for (i = 0; i < nr_redist_regions; i++)
+ if (rdist_regs[i].redist_base)
+ iounmap(rdist_regs[i].redist_base);
+ kfree(rdist_regs);
+out_unmap_dist:
+ iounmap(dist_base);
+ return err;
+}
- gic_data.dist_base = dist_base;
- gic_data.redist_regions = rdist_regs;
- gic_data.nr_redist_regions = nr_redist_regions;
- gic_data.redist_stride = redist_stride;
+IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
- gicv3_enable_quirks();
+#ifdef CONFIG_ACPI
+static struct redist_region *redist_regs __initdata;
+static u32 nr_redist_regions __initdata;
+static bool single_redist;
+
+static int __init
+gic_acpi_register_redist(phys_addr_t phys_base, void __iomem *redist_base)
+{
+ static int count = 0;
+
+ redist_regs[count].phys_base = phys_base;
+ redist_regs[count].redist_base = redist_base;
+ redist_regs[count].single_redist = single_redist;
+ count++;
+ return 0;
+}
+
+static int __init
+gic_acpi_parse_madt_redist(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_redistributor *redist =
+ (struct acpi_madt_generic_redistributor *)header;
+ void __iomem *redist_base;
+
+ redist_base = ioremap(redist->base_address, redist->length);
+ if (!redist_base) {
+ pr_err("Couldn't map GICR region @%llx\n", redist->base_address);
+ return -ENOMEM;
+ }
+
+ return gic_acpi_register_redist(redist->base_address, redist_base);
+}
+
+static int __init
+gic_acpi_parse_madt_gicc(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_interrupt *gicc;
+ void __iomem *redist_base;
+ u64 typer;
+ u32 size = SZ_64K * 2;
+
+ gicc = (struct acpi_madt_generic_interrupt *)header;
+ redist_base = ioremap(gicc->gicr_base_address, size);
+ if (!redist_base)
+ return -ENOMEM;
+
+ typer = readq_relaxed(redist_base + GICR_TYPER);
+ if (typer & GICR_TYPER_VLPIS) {
+ iounmap(redist_base);
+ size += SZ_64K * 2;
+ redist_base = ioremap(gicc->gicr_base_address, size);
+ if (!redist_base)
+ return -ENOMEM;
+ }
+
+ return gic_acpi_register_redist(gicc->gicr_base_address, redist_base);
+}
+
+static int __init gic_acpi_collect_gicr_base(void)
+{
+ acpi_tbl_entry_handler redist_parser;
+ enum acpi_madt_type type;
+
+ if (single_redist) {
+ type = ACPI_MADT_TYPE_GENERIC_INTERRUPT;
+ redist_parser = gic_acpi_parse_madt_gicc;
+ } else {
+ type = ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR;
+ redist_parser = gic_acpi_parse_madt_redist;
+ }
+
+ /* Collect redistributor base addresses in GICR entries */
+ if (acpi_table_parse_madt(type, redist_parser, 0) > 0)
+ return 0;
+
+ pr_info("No valid GICR entries exist\n");
+ return -ENODEV;
+}
+
+static int __init gic_acpi_match_gicr(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ /* Subtable presence means that redist exists, that's it */
+ return 0;
+}
+
+static int __init gic_acpi_match_gicc(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_interrupt *gicc =
+ (struct acpi_madt_generic_interrupt *)header;
/*
- * Find out how many interrupts are supported.
- * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
+ * If GICC is enabled and has valid gicr base address, then it means
+ * GICR base is presented via GICC
*/
- typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
- gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer);
- gic_irqs = GICD_TYPER_IRQS(typer);
- if (gic_irqs > 1020)
- gic_irqs = 1020;
- gic_data.irq_nr = gic_irqs;
+ if ((gicc->flags & ACPI_MADT_ENABLED) && gicc->gicr_base_address)
+ return 0;
- gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops,
- &gic_data);
- gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
+ return -ENODEV;
+}
- if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {
+static int __init gic_acpi_count_gicr_regions(void)
+{
+ int count;
+
+ /* Count how many redistributor regions we have */
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
+ gic_acpi_match_gicr, 0);
+ if (count > 0) {
+ single_redist = false;
+ return count;
+ }
+
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ gic_acpi_match_gicc, 0);
+ if (count > 0)
+ single_redist = true;
+
+ return count;
+}
+
+static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
+ struct acpi_probe_entry *ape)
+{
+ struct acpi_madt_generic_distributor *dist;
+ int count;
+
+ dist = (struct acpi_madt_generic_distributor *)header;
+ if (dist->version != ape->driver_data)
+ return false;
+
+ /* We need to do that exercise anyway, the sooner the better */
+ count = gic_acpi_count_gicr_regions();
+ if (count <= 0)
+ return false;
+
+ nr_redist_regions = count;
+ return true;
+}
+
+#define ACPI_GICV3_DIST_MEM_SIZE (SZ_64K)
+
+static int __init
+gic_acpi_init(struct acpi_subtable_header *header, const unsigned long end)
+{
+ struct acpi_madt_generic_distributor *dist;
+ struct fwnode_handle *domain_handle;
+ void __iomem *dist_base;
+ int i, err;
+
+ /* Get distributor base address */
+ dist = (struct acpi_madt_generic_distributor *)header;
+ dist_base = ioremap(dist->base_address, ACPI_GICV3_DIST_MEM_SIZE);
+ if (!dist_base) {
+ pr_err("Unable to map GICD registers\n");
+ return -ENOMEM;
+ }
+
+ err = gic_validate_dist_version(dist_base);
+ if (err) {
+ pr_err("No distributor detected at @%p, giving up", dist_base);
+ goto out_dist_unmap;
+ }
+
+ redist_regs = kzalloc(sizeof(*redist_regs) * nr_redist_regions,
+ GFP_KERNEL);
+ if (!redist_regs) {
err = -ENOMEM;
- goto out_free;
+ goto out_dist_unmap;
}
- set_handle_irq(gic_handle_irq);
+ err = gic_acpi_collect_gicr_base();
+ if (err)
+ goto out_redist_unmap;
- if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis())
- its_init(node, &gic_data.rdists, gic_data.domain);
+ domain_handle = irq_domain_alloc_fwnode(dist_base);
+ if (!domain_handle) {
+ err = -ENOMEM;
+ goto out_redist_unmap;
+ }
- gic_smp_init();
- gic_dist_init();
- gic_cpu_init();
- gic_cpu_pm_init();
+ err = gic_init_bases(dist_base, redist_regs, nr_redist_regions, 0,
+ domain_handle);
+ if (err)
+ goto out_fwhandle_free;
+ acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
return 0;
-out_free:
- if (gic_data.domain)
- irq_domain_remove(gic_data.domain);
- free_percpu(gic_data.rdists.rdist);
-out_unmap_rdist:
+out_fwhandle_free:
+ irq_domain_free_fwnode(domain_handle);
+out_redist_unmap:
for (i = 0; i < nr_redist_regions; i++)
- if (rdist_regs[i].redist_base)
- iounmap(rdist_regs[i].redist_base);
- kfree(rdist_regs);
-out_unmap_dist:
+ if (redist_regs[i].redist_base)
+ iounmap(redist_regs[i].redist_base);
+ kfree(redist_regs);
+out_dist_unmap:
iounmap(dist_base);
return err;
}
-
-IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
+IRQCHIP_ACPI_DECLARE(gic_v3, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V3,
+ gic_acpi_init);
+IRQCHIP_ACPI_DECLARE(gic_v3_maybe, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_NONE,
+ gic_acpi_init);
+IRQCHIP_ACPI_DECLARE(gic_v4, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V4,
+ gic_acpi_init);
+#endif
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index abf2ffaed392..644e8bbe130c 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -972,7 +972,7 @@ static int gic_irq_domain_translate(struct irq_domain *d,
return 0;
}
- if (fwspec->fwnode->type == FWNODE_IRQCHIP) {
+ if (is_fwnode_irqchip(fwspec->fwnode)) {
if(fwspec->param_count != 2)
return -EINVAL;
@@ -1234,7 +1234,7 @@ gic_of_init(struct device_node *node, struct device_node *parent)
}
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
- gicv2m_of_init(node, gic_data[gic_cnt].domain);
+ gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain);
gic_cnt++;
return 0;
@@ -1359,6 +1359,10 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
__gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
+
+ if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
+ gicv2m_init(NULL, gic_data[0].domain);
+
return 0;
}
IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 0e2fc1a844ab..9c2178252bff 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -82,6 +82,7 @@ static const char version[] =
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
+#include <linux/acpi.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
@@ -2472,6 +2473,14 @@ static struct dev_pm_ops smc_drv_pm_ops = {
.resume = smc_drv_resume,
};
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id smc91x_acpi_match[] = {
+ { "LNRO0003", },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, smc91x_acpi_match);
+#endif
+
static struct platform_driver smc_driver = {
.probe = smc_drv_probe,
.remove = smc_drv_remove,
@@ -2479,6 +2488,7 @@ static struct platform_driver smc_driver = {
.name = CARDNAME,
.pm = &smc_drv_pm_ops,
.of_match_table = of_match_ptr(smc91x_match),
+ .acpi_match_table = ACPI_PTR(smc91x_acpi_match),
},
};
diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index 2306313c0029..a742447e3a04 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -105,7 +105,7 @@ EXPORT_SYMBOL_GPL(of_irq_parse_pci);
* @pin: PCI irq pin number; passed when used as map_irq callback. Unused
*
* @slot and @pin are unused, but included in the function so that this
- * function can be used directly as the map_irq callback to pci_fixup_irqs().
+ * function can be used directly as the map_irq callback to pci_assign_irq().
*/
int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
{
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 73de4efcbe6e..b2e27c8d0d87 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -26,6 +26,16 @@ config PCI_MSI_IRQ_DOMAIN
depends on PCI_MSI
select GENERIC_MSI_IRQ_DOMAIN
+config PCI_ECAM
+ bool "Enhanced Configuration Access Mechanism (ECAM)"
+ depends on PCI && HAVE_PCI_ECAM
+
+config HAVE_PCI_ECAM
+ bool
+
+config ARCH_HAS_CUSTOM_PCI_ECAM
+ bool
+
config PCI_DEBUG
bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index be3f631c3f75..c41acf1ebd03 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -4,7 +4,8 @@
obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
- irq.o vpd.o setup-bus.o vc.o
+ irq.o vpd.o setup-bus.o vc.o setup-irq.o
+
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSFS) += slot.o
@@ -29,17 +30,9 @@ obj-$(CONFIG_PCI_ATS) += ats.o
obj-$(CONFIG_PCI_IOV) += iov.o
#
-# Some architectures use the generic PCI setup functions
+# Enhanced Configuration Access Mechanism (ECAM)
#
-obj-$(CONFIG_ALPHA) += setup-irq.o
-obj-$(CONFIG_ARM) += setup-irq.o
-obj-$(CONFIG_ARM64) += setup-irq.o
-obj-$(CONFIG_UNICORE32) += setup-irq.o
-obj-$(CONFIG_SUPERH) += setup-irq.o
-obj-$(CONFIG_MIPS) += setup-irq.o
-obj-$(CONFIG_TILE) += setup-irq.o
-obj-$(CONFIG_SPARC_LEON) += setup-irq.o
-obj-$(CONFIG_M68K) += setup-irq.o
+obj-$(CONFIG_PCI_ECAM) += ecam.o
#
# ACPI Related PCI FW Functions
diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c
new file mode 100644
index 000000000000..da35b4cba795
--- /dev/null
+++ b/drivers/pci/ecam.c
@@ -0,0 +1,234 @@
+/*
+ * Arch agnostic direct PCI config space access via
+ * ECAM (Enhanced Configuration Access Mechanism)
+ *
+ * Per-architecture code takes care of the mappings, region validation and
+ * accesses themselves.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+#include <linux/ecam.h>
+
+#include <asm/io.h>
+
+#define PREFIX "PCI: "
+
+static DEFINE_MUTEX(pci_mmcfg_lock);
+
+LIST_HEAD(pci_mmcfg_list);
+
+#ifndef CONFIG_ARCH_HAS_CUSTOM_PCI_ECAM
+
+static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
+{
+ void __iomem *addr;
+ u64 start, size;
+ int num_buses;
+
+ start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
+ num_buses = cfg->end_bus - cfg->start_bus + 1;
+ size = PCI_MMCFG_BUS_OFFSET(num_buses);
+ addr = ioremap_nocache(start, size);
+ if (addr)
+ addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
+ return addr;
+}
+
+int __init pci_mmcfg_arch_init(void)
+{
+ struct pci_mmcfg_region *cfg;
+
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
+ if (pci_mmcfg_arch_map(cfg)) {
+ pci_mmcfg_arch_free();
+ return 0;
+ }
+
+ return 1;
+}
+
+void __init pci_mmcfg_arch_free(void)
+{
+ struct pci_mmcfg_region *cfg;
+
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
+ pci_mmcfg_arch_unmap(cfg);
+}
+
+int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
+{
+ cfg->virt = mcfg_ioremap(cfg);
+ if (!cfg->virt) {
+ pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
+{
+ if (cfg && cfg->virt) {
+ iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
+ cfg->virt = NULL;
+ }
+}
+#endif
+
+static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
+{
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ list_del(&cfg->list);
+ kfree(cfg);
+}
+
+void __init free_all_mmcfg(void)
+{
+ struct pci_mmcfg_region *cfg, *tmp;
+
+ pci_mmcfg_arch_free();
+ list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
+ pci_mmconfig_remove(cfg);
+}
+
+void list_add_sorted(struct pci_mmcfg_region *new)
+{
+ struct pci_mmcfg_region *cfg;
+
+ /* keep list sorted by segment and starting bus number */
+ list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
+ if (cfg->segment > new->segment ||
+ (cfg->segment == new->segment &&
+ cfg->start_bus >= new->start_bus)) {
+ list_add_tail_rcu(&new->list, &cfg->list);
+ return;
+ }
+ }
+ list_add_tail_rcu(&new->list, &pci_mmcfg_list);
+}
+
+struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
+ int end, u64 addr)
+{
+ struct pci_mmcfg_region *new;
+ struct resource *res;
+
+ if (addr == 0)
+ return NULL;
+
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return NULL;
+
+ new->address = addr;
+ new->segment = segment;
+ new->start_bus = start;
+ new->end_bus = end;
+ new->hot_added = false;
+
+ res = &new->res;
+ res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
+ res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+ "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
+ res->name = new->name;
+
+ return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+ int end, u64 addr)
+{
+ struct pci_mmcfg_region *new;
+
+ new = pci_mmconfig_alloc(segment, start, end, addr);
+ if (new) {
+ mutex_lock(&pci_mmcfg_lock);
+ list_add_sorted(new);
+ mutex_unlock(&pci_mmcfg_lock);
+
+ pr_info(PREFIX
+ "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
+ "(base %#lx)\n",
+ segment, start, end, &new->res, (unsigned long)addr);
+ }
+
+ return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+ struct pci_mmcfg_region *cfg;
+
+ list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+ if (cfg->segment == segment &&
+ cfg->start_bus <= bus && bus <= cfg->end_bus)
+ return cfg;
+
+ return NULL;
+}
+
+/* Delete MMCFG information for host bridges */
+int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+{
+ struct pci_mmcfg_region *cfg;
+
+ mutex_lock(&pci_mmcfg_lock);
+ list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
+ if (cfg->segment == seg && cfg->start_bus == start &&
+ cfg->end_bus == end) {
+ list_del_rcu(&cfg->list);
+ synchronize_rcu();
+ pci_mmcfg_arch_unmap(cfg);
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ mutex_unlock(&pci_mmcfg_lock);
+ kfree(cfg);
+ return 0;
+ }
+ mutex_unlock(&pci_mmcfg_lock);
+
+ return -ENOENT;
+}
+
+int pci_mmconfig_inject(struct pci_mmcfg_region *cfg)
+{
+ struct pci_mmcfg_region *cfg_conflict;
+ int err = 0;
+
+ mutex_lock(&pci_mmcfg_lock);
+ cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus);
+ if (cfg_conflict) {
+ if (cfg_conflict->end_bus < cfg->end_bus)
+ pr_info(FW_INFO "MMCONFIG for "
+ "domain %04x [bus %02x-%02x] "
+ "only partially covers this bridge\n",
+ cfg_conflict->segment, cfg_conflict->start_bus,
+ cfg_conflict->end_bus);
+ err = -EEXIST;
+ goto out;
+ }
+
+ if (pci_mmcfg_arch_map(cfg)) {
+ pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res);
+ err = -ENOMEM;
+ goto out;
+ } else {
+ cfg->hot_added = true;
+ list_add_sorted(cfg);
+ pr_info("MMCONFIG at %pR (base %#lx)\n",
+ &cfg->res, (unsigned long)cfg->address);
+
+ }
+out:
+ mutex_unlock(&pci_mmcfg_lock);
+ return err;
+}
diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c
index 5434c90db243..d329e421d83c 100644
--- a/drivers/pci/host/pci-host-generic.c
+++ b/drivers/pci/host/pci-host-generic.c
@@ -215,11 +215,10 @@ static int gen_pci_probe(struct platform_device *pdev)
const struct of_device_id *of_id;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
- struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ struct gen_pci *pci;
struct pci_bus *bus, *child;
+ struct pci_host_bridge *bridge;
- if (!pci)
- return -ENOMEM;
type = of_get_property(np, "device_type", NULL);
if (!type || strcmp(type, "pci")) {
@@ -230,6 +229,11 @@ static int gen_pci_probe(struct platform_device *pdev)
of_pci_check_probe_only();
of_id = of_match_node(gen_pci_of_match, np);
+ set_dev_node(dev, of_node_to_nid(np));
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
pci->host.dev.parent = dev;
INIT_LIST_HEAD(&pci->host.windows);
@@ -259,7 +263,9 @@ static int gen_pci_probe(struct platform_device *pdev)
return -ENODEV;
}
- pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+ bridge = pci_find_host_bridge(bus);
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = of_irq_parse_and_map_pci;
if (!pci_has_flag(PCI_PROBE_ONLY)) {
pci_bus_size_bridges(bus);
diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c
index 0863d9cc25f8..7f98f723dc0f 100644
--- a/drivers/pci/host/pci-versatile.c
+++ b/drivers/pci/host/pci-versatile.c
@@ -135,6 +135,7 @@ static int versatile_pci_probe(struct platform_device *pdev)
u32 val;
void __iomem *local_pci_cfg_base;
struct pci_bus *bus;
+ struct pci_host_bridge *bridge;
LIST_HEAD(pci_res);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -211,8 +212,10 @@ static int versatile_pci_probe(struct platform_device *pdev)
bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, &sys, &pci_res);
if (!bus)
return -ENOMEM;
-
- pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+
+ bridge = pci_find_host_bridge(bus);
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = of_irq_parse_and_map_pci;
pci_assign_unassigned_bus_resources(bus);
pci_bus_add_devices(bus);
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index eac719af16aa..05dd782f3b31 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -324,6 +324,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
int ret;
void *sysdata;
struct pci_bus *bus;
+ struct pci_host_bridge *bridge;
if (!pcie || !pcie->dev || !pcie->base)
return -EINVAL;
@@ -372,10 +373,11 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
}
iproc_pcie_enable(pcie);
-
+ bridge = pci_find_host_bridge(bus);
+ bridge->swizzle_irq = pci_common_swizzle;
+ bridge->map_irq = pcie->map_irq;
pci_scan_child_bus(bus);
pci_assign_unassigned_bus_resources(bus);
- pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
pci_bus_add_devices(bus);
return 0;
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 7eaa4c87fec7..6ced37b53fbf 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -18,6 +18,7 @@
#include <linux/smp.h>
#include <linux/errno.h>
#include <linux/io.h>
+#include <linux/iort.h>
#include <linux/slab.h>
#include <linux/irqdomain.h>
#include <linux/of_irq.h>
@@ -1366,6 +1367,8 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
of_node = irq_domain_get_of_node(domain);
if (of_node)
rid = of_msi_map_rid(&pdev->dev, of_node, rid);
+ else
+ iort_find_pci_id(pdev, rid, &rid);
return rid;
}
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index a32ba753e413..d3f32d6417ef 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -9,7 +9,9 @@
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/irqdomain.h>
#include <linux/pci.h>
+#include <linux/msi.h>
#include <linux/pci_hotplug.h>
#include <linux/module.h>
#include <linux/pci-aspm.h>
@@ -689,6 +691,46 @@ static struct acpi_bus_type acpi_pci_bus = {
.cleanup = pci_acpi_cleanup,
};
+
+static struct fwnode_handle *(*pci_msi_get_fwnode_cb)(struct device *dev);
+
+/**
+ * pci_msi_register_fwnode_provider - Register callback to retrieve fwnode
+ * @fn: Callback matching a device to a fwnode that identifies a PCI
+ * MSI domain.
+ *
+ * This should be called by irqchip driver, which is the parent of
+ * the MSI domain to provide callback interface to query fwnode.
+ */
+void
+pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *))
+{
+ pci_msi_get_fwnode_cb = fn;
+}
+
+/**
+ * pci_host_bridge_acpi_msi_domain - Retrieve MSI domain of a PCI host bridge
+ * @bus: The PCI host bridge bus.
+ *
+ * This function uses the callback function registered by
+ * pci_msi_register_fwnode_provider() to retrieve the irq_domain with
+ * type DOMAIN_BUS_PCI_MSI of the specified host bridge bus.
+ * This returns NULL on error or when the domain is not found.
+ */
+struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus)
+{
+ struct fwnode_handle *fwnode;
+
+ if (!pci_msi_get_fwnode_cb)
+ return NULL;
+
+ fwnode = pci_msi_get_fwnode_cb(&bus->dev);
+ if (!fwnode)
+ return NULL;
+
+ return irq_find_matching_fwnode(fwnode, DOMAIN_BUS_PCI_MSI);
+}
+
static int __init acpi_pci_init(void)
{
int ret;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 314db8c1047a..39a985b6034f 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -25,6 +25,7 @@
#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/pci_hotplug.h>
+#include <linux/acpi.h>
#include <asm-generic/pci-bridge.h>
#include <asm/setup.h>
#include <linux/aer.h>
@@ -1257,6 +1258,8 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars)
if (err < 0 && err != -EIO)
return err;
+ pci_assign_irq(dev);
+
bridge = pci_upstream_bridge(dev);
if (bridge)
pcie_aspm_powersave_config_link(bridge);
@@ -4794,14 +4797,34 @@ void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
* API and update the use_dt_domains value to keep track of method we
* are using to assign domain numbers (use_dt_domains = 0).
*
+ * IF ACPI, we expect non-DT method (use_dt_domains == -1)
+ * and call _SEG method for corresponding host bridge device.
+ * If _SEG method does not exist, following ACPI spec (6.5.6)
+ * all PCI buses belong to domain 0.
+ *
* All other combinations imply we have a platform that is trying
- * to mix domain numbers obtained from DT and pci_get_new_domain_nr(),
- * which is a recipe for domain mishandling and it is prevented by
- * invalidating the domain value (domain = -1) and printing a
- * corresponding error.
+ * to mix domain numbers obtained from DT, ACPI and
+ * pci_get_new_domain_nr(), which is a recipe for domain mishandling and
+ * it is prevented by invalidating the domain value (domain = -1) and
+ * printing a corresponding error.
*/
+
if (domain >= 0 && use_dt_domains) {
use_dt_domains = 1;
+#ifdef CONFIG_ACPI
+ } else if (!acpi_disabled && use_dt_domains == -1) {
+ struct acpi_device *acpi_dev = to_acpi_device(parent);
+ unsigned long long segment = 0;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(acpi_dev->handle,
+ METHOD_NAME__SEG, NULL,
+ &segment);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
+ dev_err(&acpi_dev->dev, "can't evaluate _SEG\n");
+
+ domain = segment;
+#endif
} else if (domain < 0 && use_dt_domains != 1) {
use_dt_domains = 0;
domain = pci_get_new_domain_nr();
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index d390fc1475ec..ac7e0a3c81a8 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -82,6 +82,7 @@ void pci_pm_init(struct pci_dev *dev);
void pci_ea_init(struct pci_dev *dev);
void pci_allocate_cap_save_buffers(struct pci_dev *dev);
void pci_free_cap_save_buffers(struct pci_dev *dev);
+struct pci_host_bridge *pci_find_host_bridge(struct pci_bus *bus);
static inline void pci_wakeup_event(struct pci_dev *dev)
{
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index edb1984201e9..2fbf84020a03 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/cpumask.h>
+#include <linux/pci-acpi.h>
#include <linux/pci-aspm.h>
#include <linux/aer.h>
#include <linux/acpi.h>
@@ -672,6 +673,8 @@ static struct irq_domain *pci_host_bridge_msi_domain(struct pci_bus *bus)
* should be called from here.
*/
d = pci_host_bridge_of_msi_domain(bus);
+ if (!d)
+ d = pci_host_bridge_acpi_msi_domain(bus);
return d;
}
@@ -2065,10 +2068,12 @@ int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
void __weak pcibios_add_bus(struct pci_bus *bus)
{
+ acpi_pci_add_bus(bus);
}
void __weak pcibios_remove_bus(struct pci_bus *bus)
{
+ acpi_pci_remove_bus(bus);
}
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
@@ -2105,6 +2110,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
bridge->dev.parent = parent;
bridge->dev.release = pci_release_host_bridge_dev;
dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus);
+ ACPI_COMPANION_SET(&bridge->dev,
+ parent ? to_acpi_device_node(parent->fwnode) : NULL);
error = pcibios_root_bridge_prepare(bridge);
if (error) {
kfree(bridge);
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index 95c225be49d1..0d4b83af99ed 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -15,6 +15,7 @@
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
+#include "pci.h"
void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
{
@@ -22,12 +23,17 @@ void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
}
-static void pdev_fixup_irq(struct pci_dev *dev,
- u8 (*swizzle)(struct pci_dev *, u8 *),
- int (*map_irq)(const struct pci_dev *, u8, u8))
+void pci_assign_irq(struct pci_dev *dev)
{
- u8 pin, slot;
+ u8 pin;
+ u8 slot = -1;
int irq = 0;
+ struct pci_host_bridge *hbrg = pci_find_host_bridge(dev->bus);
+
+ if (!(hbrg->map_irq)) {
+ dev_dbg(&dev->dev, "runtime irq mapping not provided by arch\n");
+ return;
+ }
/* If this device is not on the primary bus, we need to figure out
which interrupt pin it will come in on. We know which slot it
@@ -40,29 +46,21 @@ static void pdev_fixup_irq(struct pci_dev *dev,
if (pin > 4)
pin = 1;
- if (pin != 0) {
+ if (pin) {
/* Follow the chain of bridges, swizzling as we go. */
- slot = (*swizzle)(dev, &pin);
+ if(hbrg->swizzle_irq)
+ slot = (*(hbrg->swizzle_irq))(dev, &pin);
- irq = (*map_irq)(dev, slot, pin);
+ /* If a swizzling function is not used map_irq must ignore slot */
+ irq = (*(hbrg->map_irq))(dev, slot, pin);
if (irq == -1)
irq = 0;
}
dev->irq = irq;
- dev_dbg(&dev->dev, "fixup irq: got %d\n", dev->irq);
+ dev_dbg(&dev->dev, "assign irq: got %d\n", dev->irq);
/* Always tell the device, so the driver knows what is
the real IRQ to use; the device does not use it. */
pcibios_update_irq(dev, irq);
}
-
-void pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
- int (*map_irq)(const struct pci_dev *, u8, u8))
-{
- struct pci_dev *dev = NULL;
-
- for_each_pci_dev(dev)
- pdev_fixup_irq(dev, swizzle, map_irq);
-}
-EXPORT_SYMBOL_GPL(pci_fixup_irqs);
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 899a77187bde..3f4aa1ba9482 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -2189,12 +2189,26 @@ static int __init pl011_console_setup(struct console *co, char *options)
return uart_set_options(&uap->port, co, baud, parity, bits, flow);
}
+static int __init pl011_console_acpi_match(struct console *co,
+ struct acpi_table_spcr *spcr)
+{
+ struct uart_amba_port *uap = amba_ports[co->index];
+
+ if (spcr->interface_type == ACPI_DBG2_ARM_PL011 &&
+ spcr->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY &&
+ spcr->serial_port.address == (u64)uap->port.mapbase)
+ return 0;
+
+ return -ENODEV;
+}
+
static struct uart_driver amba_reg;
static struct console amba_console = {
.name = "ttyAMA",
.write = pl011_console_write,
.device = uart_console_device,
.setup = pl011_console_setup,
+ .acpi_match = pl011_console_acpi_match,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &amba_reg,
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1c427beffadd..f738576afc0b 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -173,6 +173,20 @@ config ARM_SP805_WATCHDOG
ARM Primecell SP805 Watchdog timer. This will reboot your system when
the timeout is reached.
+config ARM_SBSA_WATCHDOG
+ tristate "ARM SBSA Generic Watchdog"
+ depends on ARM64
+ depends on ARM_ARCH_TIMER
+ select WATCHDOG_CORE
+ help
+ ARM SBSA Generic Watchdog. This watchdog has two Watchdog timeouts.
+ The first timeout will trigger a panic; the second timeout will
+ trigger a system reset.
+ More details: ARM DEN0029B - Server Base System Architecture (SBSA)
+
+ To compile this driver as module, choose M here: The module
+ will be called sbsa_gwdt.
+
config AT91RM9200_WATCHDOG
tristate "AT91RM9200 watchdog"
depends on SOC_AT91RM9200 && MFD_SYSCON
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 53d4827ddfe1..66b2062114dc 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture
obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
+obj-$(CONFIG_ARM_SBSA_WATCHDOG) += sbsa_gwdt.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c
new file mode 100644
index 000000000000..6fd1c63462b8
--- /dev/null
+++ b/drivers/watchdog/sbsa_gwdt.c
@@ -0,0 +1,459 @@
+/*
+ * SBSA(Server Base System Architecture) Generic Watchdog driver
+ *
+ * Copyright (c) 2015, Linaro Ltd.
+ * Author: Fu Wei <fu.wei@linaro.org>
+ * Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * The SBSA Generic watchdog driver is compatible with the pretimeout
+ * concept of Linux kernel.
+ * The timeout and pretimeout are determined by WCV or WOR.
+ * The first watch period is set by writing WCV directly, that can
+ * support more than 10s timeout at the maximum system counter
+ * frequency (400MHz).
+ * When WS0 is triggered, the second watch period (pretimeout) is
+ * determined by one of these registers:
+ * (1)WOR: 32bit register, this gives a maximum watch period of
+ * around 10s at the maximum system counter frequency. It's loaded
+ * automatically by hardware.
+ * (2)WCV: If the pretimeout value is greater then "max_wor_timeout",
+ * it will be loaded in WS0 interrupt routine. If system is in
+ * ws0_mode (reboot with watchdog enabled and WS0 == true), the ping
+ * operation will only reload WCV.
+ * More details about the hardware specification of this device:
+ * ARM DEN0029B - Server Base System Architecture (SBSA)
+ *
+ * Kernel/API: P------------------| pretimeout
+ * |----------------------------------------T timeout
+ * SBSA GWDT: P---WOR (or WCV)---WS1 pretimeout
+ * |-------WCV----------WS0~~~(ws0_mode)~~~~T timeout
+ */
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/watchdog.h>
+#include <asm/arch_timer.h>
+
+/* SBSA Generic Watchdog register definitions */
+/* refresh frame */
+#define SBSA_GWDT_WRR 0x000
+
+/* control frame */
+#define SBSA_GWDT_WCS 0x000
+#define SBSA_GWDT_WOR 0x008
+#define SBSA_GWDT_WCV_LO 0x010
+#define SBSA_GWDT_WCV_HI 0x014
+
+/* refresh/control frame */
+#define SBSA_GWDT_W_IIDR 0xfcc
+#define SBSA_GWDT_IDR 0xfd0
+
+/* Watchdog Control and Status Register */
+#define SBSA_GWDT_WCS_EN BIT(0)
+#define SBSA_GWDT_WCS_WS0 BIT(1)
+#define SBSA_GWDT_WCS_WS1 BIT(2)
+
+/**
+ * struct sbsa_gwdt - Internal representation of the SBSA GWDT
+ * @wdd: kernel watchdog_device structure
+ * @clk: store the System Counter clock frequency, in Hz.
+ * @ws0_mode: indicate the system boot in the second stage timeout.
+ * @max_wor_timeout: the maximum timeout value for WOR (in seconds).
+ * @refresh_base: Virtual address of the watchdog refresh frame
+ * @control_base: Virtual address of the watchdog control frame
+ */
+struct sbsa_gwdt {
+ struct watchdog_device wdd;
+ u32 clk;
+ bool ws0_mode;
+ int max_wor_timeout;
+ void __iomem *refresh_base;
+ void __iomem *control_base;
+};
+
+#define to_sbsa_gwdt(e) container_of(e, struct sbsa_gwdt, wdd)
+
+#define DEFAULT_TIMEOUT 60 /* seconds, the 1st + 2nd watch periods*/
+#define DEFAULT_PRETIMEOUT 30 /* seconds, the 2nd watch period*/
+
+static unsigned int timeout;
+module_param(timeout, uint, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. (>=0, default="
+ __MODULE_STRING(DEFAULT_TIMEOUT) ")");
+
+static unsigned int pretimeout;
+module_param(pretimeout, uint, 0);
+MODULE_PARM_DESC(pretimeout,
+ "Watchdog pretimeout in seconds. (>=0, default="
+ __MODULE_STRING(DEFAULT_PRETIMEOUT) ")");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, S_IRUGO);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/*
+ * help functions for accessing 64bit WCV register
+ */
+static u64 sbsa_gwdt_get_wcv(struct watchdog_device *wdd)
+{
+ u32 wcv_lo, wcv_hi;
+ struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd);
+
+ do {
+ wcv_hi = readl_relaxed(gwdt->control_base + SBSA_GWDT_WCV_HI);
+ wcv_lo = readl_relaxed(gwdt->control_base + SBSA_GWDT_WCV_LO);
+ } while (wcv_hi != readl_relaxed(gwdt->control_base +
+ SBSA_GWDT_WCV_HI));
+
+ return (((u64)wcv_hi << 32) | wcv_lo);
+}
+
+static void sbsa_gwdt_set_wcv(struct watchdog_device *wdd, unsigned int t)
+{
+ struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd);
+ u64 wcv;
+
+ wcv = arch_counter_get_cntvct() + (u64)t * gwdt->clk;
+
+ writel_relaxed(upper_32_bits(wcv),
+ gwdt->control_base + SBSA_GWDT_WCV_HI);
+ writel_relaxed(lower_32_bits(wcv),
+ gwdt->control_base + SBSA_GWDT_WCV_LO);
+}
+
+/*
+ * inline functions for reloading 64bit WCV register
+ */
+static inline void reload_pretimeout_to_wcv(struct watchdog_device *wdd)
+{
+ sbsa_gwdt_set_wcv(wdd, wdd->pretimeout);
+}
+
+static inline void reload_first_stage_to_wcv(struct watchdog_device *wdd)
+{
+ sbsa_gwdt_set_wcv(wdd, wdd->timeout - wdd->pretimeout);
+}
+
+/*
+ * watchdog operation functions
+ */
+static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ wdd->timeout = timeout;
+
+ return 0;
+}
+
+static int sbsa_gwdt_set_pretimeout(struct watchdog_device *wdd,
+ unsigned int pretimeout)
+{
+ struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd);
+ u32 wor;
+
+ wdd->pretimeout = pretimeout;
+
+ /* If ws0_mode == true, we won't touch WOR */
+ if (!gwdt->ws0_mode) {
+ if (!pretimeout)
+ /*
+ * If pretimeout is 0, it gives driver a timeslot (1s)
+ * to update WCV after an explicit refresh
+ * (sbsa_gwdt_start)
+ */
+ wor = gwdt->clk;
+ else
+ if (pretimeout > gwdt->max_wor_timeout)
+ wor = U32_MAX;
+ else
+ wor = pretimeout * gwdt->clk;
+
+ /* wtite WOR, that will cause an explicit watchdog refresh */
+ writel_relaxed(wor, gwdt->control_base + SBSA_GWDT_WOR);
+ }
+
+ return 0;
+}
+
+static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd)
+{
+ struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd);
+ u64 timeleft = sbsa_gwdt_get_wcv(wdd) - arch_counter_get_cntvct();
+
+ do_div(timeleft, gwdt->clk);
+
+ return timeleft;
+}
+
+static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
+{
+ struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd);
+
+ if (gwdt->ws0_mode)
+ reload_pretimeout_to_wcv(wdd);
+ else
+ reload_first_stage_to_wcv(wdd);
+
+ return 0;
+}
+
+static int sbsa_gwdt_start(struct watchdog_device *wdd)
+{
+ struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd);
+
+ /* If ws0_mode == true, the watchdog is enabled */
+ if (!gwdt->ws0_mode)
+ /* writing WCS will cause an explicit watchdog refresh */
+ writel_relaxed(SBSA_GWDT_WCS_EN,
+ gwdt->control_base + SBSA_GWDT_WCS);
+
+ return sbsa_gwdt_keepalive(wdd);
+}
+
+static int sbsa_gwdt_stop(struct watchdog_device *wdd)
+{
+ struct sbsa_gwdt *gwdt = to_sbsa_gwdt(wdd);
+
+ writel_relaxed(0, gwdt->control_base + SBSA_GWDT_WCS);
+ /*
+ * Writing WCS has caused an explicit watchdog refresh.
+ * Both watchdog signals are deasserted, so clean ws0_mode flag.
+ */
+ gwdt->ws0_mode = false;
+
+ return 0;
+}
+
+static irqreturn_t sbsa_gwdt_interrupt(int irq, void *dev_id)
+{
+ struct sbsa_gwdt *gwdt = (struct sbsa_gwdt *)dev_id;
+ struct watchdog_device *wdd = &gwdt->wdd;
+
+ /* We don't use pretimeout, trigger WS1 now */
+ if (!wdd->pretimeout)
+ sbsa_gwdt_set_wcv(wdd, 0);
+
+ /*
+ * The pretimeout is valid, go panic
+ * If pretimeout is greater then "max_wor_timeout",
+ * reload the right value to WCV, then panic
+ */
+ if (wdd->pretimeout > gwdt->max_wor_timeout)
+ reload_pretimeout_to_wcv(wdd);
+ panic("SBSA Watchdog pre-timeout");
+
+ return IRQ_HANDLED;
+}
+
+static struct watchdog_info sbsa_gwdt_info = {
+ .identity = "SBSA Generic Watchdog",
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE |
+ WDIOF_PRETIMEOUT |
+ WDIOF_CARDRESET,
+};
+
+static struct watchdog_ops sbsa_gwdt_ops = {
+ .owner = THIS_MODULE,
+ .start = sbsa_gwdt_start,
+ .stop = sbsa_gwdt_stop,
+ .ping = sbsa_gwdt_keepalive,
+ .set_timeout = sbsa_gwdt_set_timeout,
+ .set_pretimeout = sbsa_gwdt_set_pretimeout,
+ .get_timeleft = sbsa_gwdt_get_timeleft,
+};
+
+static int sbsa_gwdt_probe(struct platform_device *pdev)
+{
+ void __iomem *rf_base, *cf_base;
+ struct device *dev = &pdev->dev;
+ struct watchdog_device *wdd;
+ struct sbsa_gwdt *gwdt;
+ struct resource *res;
+ int ret, irq;
+ u32 status;
+
+ gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
+ if (!gwdt)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, gwdt);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ cf_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cf_base))
+ return PTR_ERR(cf_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ rf_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(rf_base))
+ return PTR_ERR(rf_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "unable to get ws0 interrupt.\n");
+ return irq;
+ }
+
+ /*
+ * Get the frequency of system counter from the cp15 interface of ARM
+ * Generic timer. We don't need to check it, because if it returns "0",
+ * system would panic in very early stage.
+ */
+ gwdt->clk = arch_timer_get_cntfrq();
+ gwdt->refresh_base = rf_base;
+ gwdt->control_base = cf_base;
+ gwdt->max_wor_timeout = U32_MAX / gwdt->clk;
+ gwdt->ws0_mode = false;
+
+ wdd = &gwdt->wdd;
+ wdd->parent = dev;
+ wdd->info = &sbsa_gwdt_info;
+ wdd->ops = &sbsa_gwdt_ops;
+ watchdog_set_drvdata(wdd, gwdt);
+ watchdog_set_nowayout(wdd, nowayout);
+
+ wdd->min_pretimeout = 0;
+ wdd->min_timeout = 1;
+
+ /*
+ * Because the maximum of gwdt->clk is 400MHz and the maximum of WCV is
+ * U64_MAX, so the result of (U64_MAX / gwdt->clk) is always greater
+ * than U32_MAX. And the maximum of "unsigned int" is U32_MAX on ARM64.
+ * So we set the maximum value of pretimeout and timeout below.
+ */
+ wdd->max_pretimeout = U32_MAX - 1;
+ wdd->max_timeout = U32_MAX;
+
+ wdd->pretimeout = DEFAULT_PRETIMEOUT;
+ wdd->timeout = DEFAULT_TIMEOUT;
+ watchdog_init_timeouts(wdd, pretimeout, timeout, dev);
+
+ status = readl_relaxed(gwdt->control_base + SBSA_GWDT_WCS);
+ if (status & SBSA_GWDT_WCS_WS1) {
+ dev_warn(dev, "System reset by WDT.\n");
+ wdd->bootstatus |= WDIOF_CARDRESET;
+ } else if (status == (SBSA_GWDT_WCS_WS0 | SBSA_GWDT_WCS_EN)) {
+ gwdt->ws0_mode = true;
+ }
+
+ ret = devm_request_irq(dev, irq, sbsa_gwdt_interrupt, 0,
+ pdev->name, gwdt);
+ if (ret) {
+ dev_err(dev, "unable to request IRQ %d\n", irq);
+ return ret;
+ }
+
+ ret = watchdog_register_device(wdd);
+ if (ret)
+ return ret;
+
+ /* If ws0_mode == true, the line won't update WOR */
+ sbsa_gwdt_set_pretimeout(wdd, wdd->pretimeout);
+
+ /*
+ * If watchdog is already enabled, do a ping operation
+ * to keep system running
+ */
+ if (status & SBSA_GWDT_WCS_EN)
+ sbsa_gwdt_keepalive(wdd);
+
+ dev_info(dev, "Initialized with %ds timeout, %ds pretimeout @ %u Hz%s\n",
+ wdd->timeout, wdd->pretimeout, gwdt->clk,
+ status & SBSA_GWDT_WCS_EN ?
+ gwdt->ws0_mode ? " [second stage]" : " [enabled]" :
+ "");
+
+ return 0;
+}
+
+static void sbsa_gwdt_shutdown(struct platform_device *pdev)
+{
+ struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev);
+
+ sbsa_gwdt_stop(&gwdt->wdd);
+}
+
+static int sbsa_gwdt_remove(struct platform_device *pdev)
+{
+ struct sbsa_gwdt *gwdt = platform_get_drvdata(pdev);
+
+ watchdog_unregister_device(&gwdt->wdd);
+
+ return 0;
+}
+
+/* Disable watchdog if it is active during suspend */
+static int __maybe_unused sbsa_gwdt_suspend(struct device *dev)
+{
+ struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&gwdt->wdd))
+ sbsa_gwdt_stop(&gwdt->wdd);
+
+ return 0;
+}
+
+/* Enable watchdog and configure it if necessary */
+static int __maybe_unused sbsa_gwdt_resume(struct device *dev)
+{
+ struct sbsa_gwdt *gwdt = dev_get_drvdata(dev);
+
+ if (watchdog_active(&gwdt->wdd))
+ sbsa_gwdt_start(&gwdt->wdd);
+
+ return 0;
+}
+
+static const struct dev_pm_ops sbsa_gwdt_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sbsa_gwdt_suspend, sbsa_gwdt_resume)
+};
+
+static const struct of_device_id sbsa_gwdt_of_match[] = {
+ { .compatible = "arm,sbsa-gwdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sbsa_gwdt_of_match);
+
+static const struct platform_device_id sbsa_gwdt_pdev_match[] = {
+ { .name = "sbsa-gwdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, sbsa_gwdt_pdev_match);
+
+static struct platform_driver sbsa_gwdt_driver = {
+ .driver = {
+ .name = "sbsa-gwdt",
+ .pm = &sbsa_gwdt_pm_ops,
+ .of_match_table = sbsa_gwdt_of_match,
+ },
+ .probe = sbsa_gwdt_probe,
+ .remove = sbsa_gwdt_remove,
+ .shutdown = sbsa_gwdt_shutdown,
+ .id_table = sbsa_gwdt_pdev_match,
+};
+
+module_platform_driver(sbsa_gwdt_driver);
+
+MODULE_DESCRIPTION("SBSA Generic Watchdog Driver");
+MODULE_AUTHOR("Fu Wei <fu.wei@linaro.org>");
+MODULE_AUTHOR("Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 873f13972cf4..924f911590fb 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -85,57 +85,126 @@ static void watchdog_deferred_registration_del(struct watchdog_device *wdd)
static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
{
/*
- * Check that we have valid min and max timeout values, if
- * not reset them both to 0 (=not used or unknown)
+ * Check that we have valid min and max pretimeout and timeout values,
+ * if not, reset them all to 0 (=not used or unknown)
*/
- if (wdd->min_timeout > wdd->max_timeout) {
- pr_info("Invalid min and max timeout values, resetting to 0!\n");
+ if (wdd->min_pretimeout > wdd->max_pretimeout ||
+ wdd->min_timeout > wdd->max_timeout ||
+ wdd->min_timeout < wdd->min_pretimeout ||
+ wdd->max_timeout < wdd->max_pretimeout) {
+ pr_info("Invalid min or max timeouts, resetting to 0\n");
+ wdd->min_pretimeout = 0;
+ wdd->max_pretimeout = 0;
wdd->min_timeout = 0;
wdd->max_timeout = 0;
}
}
/**
- * watchdog_init_timeout() - initialize the timeout field
+ * watchdog_init_timeouts() - initialize the pretimeout and timeout field
+ * @pretimeout_parm: pretimeout module parameter
* @timeout_parm: timeout module parameter
* @dev: Device that stores the timeout-sec property
*
- * Initialize the timeout field of the watchdog_device struct with either the
- * timeout module parameter (if it is valid value) or the timeout-sec property
- * (only if it is a valid value and the timeout_parm is out of bounds).
- * If none of them are valid then we keep the old value (which should normally
- * be the default timeout value.
+ * Initialize the pretimeout and timeout field of the watchdog_device struct
+ * with both the pretimeout and timeout module parameters (if they are valid) or
+ * the timeout-sec property (only if they are valid and the pretimeout_parm or
+ * timeout_parm is out of bounds). If one of them is invalid, then we keep
+ * the old value (which should normally be the default timeout value).
*
* A zero is returned on success and -EINVAL for failure.
*/
-int watchdog_init_timeout(struct watchdog_device *wdd,
- unsigned int timeout_parm, struct device *dev)
+int watchdog_init_timeouts(struct watchdog_device *wdd,
+ unsigned int pretimeout_parm,
+ unsigned int timeout_parm,
+ struct device *dev)
{
- unsigned int t = 0;
- int ret = 0;
+ int ret = 0, length = 0;
+ u32 timeouts[2] = {0};
+ struct property *prop;
watchdog_check_min_max_timeout(wdd);
- /* try to get the timeout module parameter first */
- if (!watchdog_timeout_invalid(wdd, timeout_parm) && timeout_parm) {
- wdd->timeout = timeout_parm;
- return ret;
- }
- if (timeout_parm)
+ /*
+ * Backup the timeouts of wdd, and set them to the parameters,
+ * because watchdog_pretimeout_invalid uses wdd->timeout to validate
+ * the pretimeout_parm, and watchdog_timeout_invalid uses
+ * wdd->pretimeout to validate timeout_parm.
+ * if any of parameters is wrong, restore the default values before
+ * return.
+ */
+ timeouts[0] = wdd->timeout;
+ timeouts[1] = wdd->pretimeout;
+ wdd->timeout = timeout_parm;
+ wdd->pretimeout = pretimeout_parm;
+
+ /*
+ * Try to get the pretimeout module parameter first.
+ * Note: zero is a valid value for pretimeout.
+ */
+ if (watchdog_pretimeout_invalid(wdd, pretimeout_parm))
ret = -EINVAL;
- /* try to get the timeout_sec property */
- if (dev == NULL || dev->of_node == NULL)
- return ret;
- of_property_read_u32(dev->of_node, "timeout-sec", &t);
- if (!watchdog_timeout_invalid(wdd, t) && t)
- wdd->timeout = t;
- else
+ /*
+ * Try to get the timeout module parameter,
+ * if it's valid and pretimeout is valid(ret == 0),
+ * assignment and return zero. Otherwise, try dtb.
+ */
+ if (timeout_parm && !ret) {
+ if (!watchdog_timeout_invalid(wdd, timeout_parm))
+ return 0;
ret = -EINVAL;
+ }
- return ret;
+ /*
+ * Either at least one of the module parameters is invalid,
+ * or timeout_parm is 0. Try to get the timeout_sec property.
+ */
+ if (!dev || !dev->of_node) {
+ wdd->timeout = timeouts[0];
+ wdd->pretimeout = timeouts[1];
+ return ret;
+ }
+
+ /*
+ * Backup default values to *_parms,
+ * timeouts[] will be used by of_property_read_u32_array.
+ */
+ timeout_parm = timeouts[0];
+ pretimeout_parm = timeouts[1];
+
+ prop = of_find_property(dev->of_node, "timeout-sec", &length);
+ if (prop && length > 0 && length <= sizeof(u32) * 2) {
+ of_property_read_u32_array(dev->of_node,
+ "timeout-sec", timeouts,
+ length / sizeof(u32));
+ wdd->timeout = timeouts[0];
+ wdd->pretimeout = timeouts[1];
+
+ if (length == sizeof(u32) * 2) {
+ if (watchdog_pretimeout_invalid(wdd, timeouts[1]))
+ goto error;
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ if (!watchdog_timeout_invalid(wdd, timeouts[0]) &&
+ timeouts[0]) {
+ if (ret) /* Only one value in "timeout-sec" */
+ wdd->pretimeout = pretimeout_parm;
+ return 0;
+ }
+ }
+
+error:
+ /* restore default values */
+ wdd->timeout = timeout_parm;
+ wdd->pretimeout = pretimeout_parm;
+
+ return -EINVAL;
}
-EXPORT_SYMBOL_GPL(watchdog_init_timeout);
+EXPORT_SYMBOL_GPL(watchdog_init_timeouts);
static int __watchdog_register_device(struct watchdog_device *wdd)
{
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 56a649e66eb2..13cca7142842 100644
--- a/drivers/watchdog/watchdog_dev.c
+++ b/drivers/watchdog/watchdog_dev.c
@@ -217,6 +217,38 @@ out_timeout:
}
/*
+ * watchdog_set_pretimeout: set the watchdog timer pretimeout
+ * @wdd: the watchdog device to set the timeout for
+ * @pretimeout: pretimeout to set in seconds
+ */
+
+static int watchdog_set_pretimeout(struct watchdog_device *wdd,
+ unsigned int pretimeout)
+{
+ int err;
+
+ if (!wdd->ops->set_pretimeout ||
+ !(wdd->info->options & WDIOF_PRETIMEOUT))
+ return -EOPNOTSUPP;
+
+ if (watchdog_pretimeout_invalid(wdd, pretimeout))
+ return -EINVAL;
+
+ mutex_lock(&wdd->lock);
+
+ if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
+ err = -ENODEV;
+ goto out_pretimeout;
+ }
+
+ err = wdd->ops->set_pretimeout(wdd, pretimeout);
+
+out_pretimeout:
+ mutex_unlock(&wdd->lock);
+ return err;
+}
+
+/*
* watchdog_get_timeleft: wrapper to get the time left before a reboot
* @wdd: the watchdog device to get the remaining time from
* @timeleft: the time that's left
@@ -391,6 +423,29 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
if (wdd->timeout == 0)
return -EOPNOTSUPP;
return put_user(wdd->timeout, p);
+ case WDIOC_SETPRETIMEOUT:
+ /* check if we support the pretimeout */
+ if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+ return -EOPNOTSUPP;
+ if (get_user(val, p))
+ return -EFAULT;
+ err = watchdog_set_pretimeout(wdd, val);
+ if (err < 0)
+ return err;
+ /*
+ * If the watchdog is active then we send a keepalive ping
+ * to make sure that the watchdog keeps running (and if
+ * possible that it takes the new pretimeout)
+ */
+ err = watchdog_ping(wdd);
+ if (err < 0)
+ return err;
+ /* Fall */
+ case WDIOC_GETPRETIMEOUT:
+ /* check if we support the pretimeout */
+ if (wdd->info->options & WDIOF_PRETIMEOUT)
+ return put_user(wdd->pretimeout, p);
+ return -EOPNOTSUPP;
case WDIOC_GETTIMELEFT:
err = watchdog_get_timeleft(wdd, &val);
if (err)
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
index 7494dbeb4409..9a8dbe39c701 100644
--- a/drivers/xen/pci.c
+++ b/drivers/xen/pci.c
@@ -20,6 +20,7 @@
#include <linux/pci.h>
#include <linux/acpi.h>
#include <linux/pci-acpi.h>
+#include <linux/ecam.h>
#include <xen/xen.h>
#include <xen/interface/physdev.h>
#include <xen/interface/xen.h>
@@ -27,9 +28,6 @@
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
#include "../pci/pci.h"
-#ifdef CONFIG_PCI_MMCONFIG
-#include <asm/pci_x86.h>
-#endif
static bool __read_mostly pci_seg_supported = true;
@@ -221,9 +219,6 @@ static int __init xen_mcfg_late(void)
if (!xen_initial_domain())
return 0;
- if ((pci_probe & PCI_PROBE_MMCONF) == 0)
- return 0;
-
if (list_empty(&pci_mmcfg_list))
return 0;
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index ad0a5ff3d4cd..ea4d2b5ab9ec 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -554,6 +554,7 @@ struct acpi_pci_root {
struct pci_bus *bus;
u16 segment;
struct resource secondary; /* downstream bus range */
+ void *sysdata;
u32 osc_support_set; /* _OSC state of support bits */
u32 osc_control_set; /* _OSC state of control bits */
diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index 6e28f544b7b2..ce4cb3723c0a 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -371,6 +371,10 @@ struct acpi_dbg2_device {
#define ACPI_DBG2_16550_COMPATIBLE 0x0000
#define ACPI_DBG2_16550_SUBSET 0x0001
+#define ACPI_DBG2_ARM_PL011 0x0003
+#define ACPI_DBG2_ARM_SBSA_GENERIC 0x000e
+#define ACPI_DBG2_ARM_DCC 0x000f
+#define ACPI_DBG2_DCM2835 0x0010
#define ACPI_DBG2_1394_STANDARD 0x0000
diff --git a/include/asm-generic/topology.h b/include/asm-generic/topology.h
index fc824e2828f3..ee305f10bed4 100644
--- a/include/asm-generic/topology.h
+++ b/include/asm-generic/topology.h
@@ -54,14 +54,14 @@
#define pcibus_to_node(bus) ((void)(bus), -1)
#endif
+#endif /* !CONFIG_NUMA */
+
#ifndef cpumask_of_pcibus
#define cpumask_of_pcibus(bus) (pcibus_to_node(bus) == -1 ? \
cpu_all_mask : \
cpumask_of_node(pcibus_to_node(bus)))
#endif
-#endif /* CONFIG_NUMA */
-
#if !defined(CONFIG_NUMA) || !defined(CONFIG_HAVE_MEMORYLESS_NODES)
#ifndef set_numa_mem
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index c4bd0e2c173c..c93fc97c9697 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -298,6 +298,13 @@
VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \
} \
\
+ /* ACPI MCFG quirks */ \
+ .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \
+ VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \
+ *(.acpi_fixup_mcfg) \
+ VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \
+ } \
+ \
/* Built-in firmware blobs */ \
.builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_builtin_fw) = .; \
diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h
index 9916d0e4eff5..7a6d6dcfb736 100644
--- a/include/clocksource/arm_arch_timer.h
+++ b/include/clocksource/arm_arch_timer.h
@@ -19,6 +19,10 @@
#include <linux/timecounter.h>
#include <linux/types.h>
+#define ARCH_CP15_TIMER BIT(0)
+#define ARCH_MEM_TIMER BIT(1)
+#define ARCH_WD_TIMER BIT(2)
+
#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
#define ARCH_TIMER_CTRL_IT_STAT (1 << 2)
@@ -33,6 +37,8 @@ enum arch_timer_reg {
#define ARCH_TIMER_MEM_PHYS_ACCESS 2
#define ARCH_TIMER_MEM_VIRT_ACCESS 3
+#define ARCH_TIMER_MEM_MAX_FRAME 8
+
#define ARCH_TIMER_USR_PCT_ACCESS_EN (1 << 0) /* physical counter */
#define ARCH_TIMER_USR_VCT_ACCESS_EN (1 << 1) /* virtual counter */
#define ARCH_TIMER_VIRT_EVT_EN (1 << 2)
@@ -43,6 +49,19 @@ enum arch_timer_reg {
#define ARCH_TIMER_EVT_STREAM_FREQ 10000 /* 100us */
+struct arch_timer_data {
+ int phys_secure_ppi;
+ int phys_nonsecure_ppi;
+ int virt_ppi;
+ int hyp_ppi;
+ bool c3stop;
+};
+
+struct arch_timer_mem_data {
+ phys_addr_t cntbase_phy;
+ int irq;
+};
+
#ifdef CONFIG_ARM_ARCH_TIMER
extern u32 arch_timer_get_rate(void);
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index d2f41477f8ae..1798b483dc08 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -24,6 +24,7 @@
#include <linux/irqreturn.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+#include <linux/acpi.h>
#include <kvm/iodev.h>
#define VGIC_NR_IRQS_LEGACY 256
@@ -351,20 +352,37 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, struct irq_phys_map *map);
#define vgic_initialized(k) (!!((k)->arch.vgic.nr_cpus))
#define vgic_ready(k) ((k)->arch.vgic.ready)
-int vgic_v2_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params);
+int vgic_v2_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
+#ifdef CONFIG_ACPI
+int vgic_v2_acpi_probe(struct acpi_madt_generic_interrupt *,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
+#endif /* CONFIG_ACPI */
#ifdef CONFIG_KVM_ARM_VGIC_V3
-int vgic_v3_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params);
+int vgic_v3_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
+int vgic_v3_acpi_probe(struct acpi_madt_generic_interrupt *,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params);
#else
-static inline int vgic_v3_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params)
+static inline int vgic_v3_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
{
return -ENODEV;
}
+
+#ifdef CONFIG_ACPI
+int vgic_v3_acpi_probe(struct acpi_madt_generic_interrupt *vgic_acpi,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_ACPI */
#endif
#endif
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 1991aea2ec4c..8d97405372a4 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -166,14 +166,36 @@ int acpi_table_parse_madt(enum acpi_madt_type id,
acpi_tbl_entry_handler handler,
unsigned int max_entries);
int acpi_parse_mcfg (struct acpi_table_header *header);
+int acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+ struct acpi_mcfg_allocation *cfg);
void acpi_table_print_madt_entry (struct acpi_subtable_header *madt);
-/* the following four functions are architecture-dependent */
+/* the following numa functions are architecture-dependent */
void acpi_numa_slit_init (struct acpi_table_slit *slit);
+
+#if defined(CONFIG_X86) || defined(CONFIG_IA64)
void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa);
+#else
+static inline void
+acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) { }
+#endif
+
void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa);
+
+#ifdef CONFIG_ARM64
+void acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa);
+#else
+static inline void
+acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa) { }
+#endif
+
int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma);
+
+#ifdef CONFIG_ACPI_HAS_NUMA_ARCH_FIXUP
void acpi_numa_arch_fixup(void);
+#else
+static inline void acpi_numa_arch_fixup(void) { }
+#endif
#ifndef PHYS_CPUID_INVALID
typedef u32 phys_cpuid_t;
@@ -468,6 +490,23 @@ void acpi_walk_dep_device_list(acpi_handle handle);
struct platform_device *acpi_create_platform_device(struct acpi_device *);
#define ACPI_PTR(_ptr) (_ptr)
+#ifdef CONFIG_ACPI_GTDT
+bool __init gtdt_timer_is_available(int type);
+
+struct arch_timer_data;
+int __init gtdt_arch_timer_data_init(struct acpi_table_header *table,
+ struct arch_timer_data *data);
+struct arch_timer_mem_data;
+void __init *gtdt_gt_block(struct acpi_table_header *table, int index);
+u32 __init gtdt_gt_timer_count(struct acpi_gtdt_timer_block *gt_block);
+void __init *gtdt_gt_cntctlbase(struct acpi_gtdt_timer_block *gt_block);
+u32 __init gtdt_gt_frame_number(struct acpi_gtdt_timer_block *gt_block,
+ int index);
+int __init gtdt_gt_timer_data(struct acpi_gtdt_timer_block *gt_block,
+ int index, bool virt,
+ struct arch_timer_mem_data *data);
+#endif
+
#else /* !CONFIG_ACPI */
#define acpi_disabled 1
diff --git a/include/linux/console.h b/include/linux/console.h
index bd194343c346..cb9ed402cfc1 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -14,6 +14,7 @@
#ifndef _LINUX_CONSOLE_H_
#define _LINUX_CONSOLE_H_ 1
+#include <linux/errno.h>
#include <linux/types.h>
struct vc_data;
@@ -117,6 +118,7 @@ static inline int con_debug_leave(void)
#define CON_BRL (32) /* Used for a braille device */
#define CON_EXTENDED (64) /* Use the extended output format a la /dev/kmsg */
+struct acpi_table_spcr;
struct console {
char name[16];
void (*write)(struct console *, const char *, unsigned);
@@ -125,6 +127,7 @@ struct console {
void (*unblank)(void);
int (*setup)(struct console *, char *);
int (*match)(struct console *, char *name, int idx, char *options);
+ int (*acpi_match)(struct console *, struct acpi_table_spcr *);
short flags;
short index;
int cflag;
@@ -132,6 +135,16 @@ struct console {
struct console *next;
};
+#ifdef CONFIG_ACPI_SPCR_TABLE
+int console_acpi_match(struct console *c, char **options);
+#else
+static inline int console_acpi_match(struct console *c, char **options)
+{
+ return -ENODEV;
+}
+#endif
+void acpi_register_consoles_try_again(void);
+
/*
* for_each_console() allows you to iterate on each console
*/
diff --git a/include/linux/ecam.h b/include/linux/ecam.h
new file mode 100644
index 000000000000..21215be0df5e
--- /dev/null
+++ b/include/linux/ecam.h
@@ -0,0 +1,62 @@
+#ifndef __ECAM_H
+#define __ECAM_H
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/acpi.h>
+
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_mmcfg_region {
+ struct list_head list;
+ struct resource res;
+ u64 address;
+ char __iomem *virt;
+ u16 segment;
+ u8 start_bus;
+ u8 end_bus;
+ char name[PCI_MMCFG_RESOURCE_NAME_LEN];
+ bool hot_added;
+};
+
+struct pci_mcfg_fixup {
+ const struct dmi_system_id *system;
+ int (*match)(struct pci_mcfg_fixup *, struct acpi_pci_root *);
+ struct pci_ops *ops;
+ int domain;
+ int bus_num;
+};
+
+#define PCI_MCFG_DOMAIN_ANY -1
+#define PCI_MCFG_BUS_ANY -1
+
+/* Designate a routine to fix up buggy MCFG */
+#define DECLARE_ACPI_MCFG_FIXUP(system, match, ops, dom, bus) \
+ static const struct pci_mcfg_fixup __mcfg_fixup_##system##dom##bus\
+ __used __attribute__((__section__(".acpi_fixup_mcfg"), \
+ aligned((sizeof(void *))))) = \
+ { system, match, ops, dom, bus };
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
+struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
+ int end, u64 addr);
+int pci_mmconfig_inject(struct pci_mmcfg_region *cfg);
+struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+ int end, u64 addr);
+void list_add_sorted(struct pci_mmcfg_region *new);
+void free_all_mmcfg(void);
+int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+
+/* Arch specific calls */
+int pci_mmcfg_arch_init(void);
+void pci_mmcfg_arch_free(void);
+int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
+void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
+
+extern struct list_head pci_mmcfg_list;
+
+#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
+
+#endif /* __KERNEL__ */
+#endif /* __ECAM_H */
diff --git a/include/linux/iort.h b/include/linux/iort.h
new file mode 100644
index 000000000000..92cde45c949a
--- /dev/null
+++ b/include/linux/iort.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015, Linaro Ltd.
+ * Author: Tomasz Nowicki <tomasz.nowicki@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef __IORT_H__
+#define __IORT_H__
+
+#include <linux/acpi.h>
+
+#ifdef CONFIG_IORT_TABLE
+
+struct fwnode_handle;
+
+int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
+struct fwnode_handle *iort_find_its_domain_token(int trans_id);
+struct fwnode_handle *iort_find_pci_domain_token(struct device *dev);
+int iort_find_pci_id(struct pci_dev *pdev, u32 req_id, u32 *dev_id);
+#else /* CONFIG_IORT_TABLE */
+static inline int
+iort_find_pci_id(struct pci_dev *pdev, u32 req_id, u32 *dev_id)
+{ return -ENXIO; }
+#endif /* CONFIG_IORT_TABLE */
+
+#endif /* __IORT_H__ */
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index d5d798b35c1f..a40ed7d156a8 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -332,7 +332,7 @@ struct rdists {
struct irq_domain;
struct device_node;
int its_cpu_init(void);
-int its_init(struct device_node *node, struct rdists *rdists,
+int its_init(struct fwnode_handle *handle, struct rdists *rdists,
struct irq_domain *domain);
static inline bool gic_enable_sre(void)
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index bae69e5d693c..febc6c312e37 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -106,7 +106,8 @@ int gic_cpu_if_down(unsigned int gic_nr);
void gic_init(unsigned int nr, int start,
void __iomem *dist , void __iomem *cpu);
-int gicv2m_of_init(struct device_node *node, struct irq_domain *parent);
+int gicv2m_init(struct fwnode_handle *parent_handle,
+ struct irq_domain *parent);
void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
int gic_get_cpu_id(unsigned int cpu);
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index d5e5c5bef28c..d72fabc04437 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -211,6 +211,11 @@ static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
return node ? &node->fwnode : NULL;
}
+static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode)
+{
+ return fwnode && fwnode->type == FWNODE_IRQCHIP;
+}
+
static inline struct irq_domain *irq_find_matching_host(struct device_node *node,
enum irq_domain_bus_token bus_token)
{
@@ -410,6 +415,11 @@ static inline bool irq_domain_is_hierarchy(struct irq_domain *domain)
static inline void irq_dispose_mapping(unsigned int virq) { }
static inline void irq_domain_activate_irq(struct irq_data *data) { }
static inline void irq_domain_deactivate_irq(struct irq_data *data) { }
+static inline struct irq_domain *irq_find_matching_fwnode(
+ struct fwnode_handle *fwnode, enum irq_domain_bus_token bus_token)
+{
+ return NULL;
+}
#endif /* !CONFIG_IRQ_DOMAIN */
#endif /* _LINUX_IRQDOMAIN_H */
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 89ab0572dbc6..45807a690942 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -79,6 +79,23 @@ extern struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
void acpi_pci_add_bus(struct pci_bus *bus);
void acpi_pci_remove_bus(struct pci_bus *bus);
+#ifdef CONFIG_PCI_MMCONFIG
+int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci);
+void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci);
+struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root);
+void __iomem *
+pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset);
+#else
+static inline int pci_mmcfg_setup_map(struct acpi_pci_root_info *ci)
+{ return 0; }
+static inline void pci_mmcfg_teardown_map(struct acpi_pci_root_info *ci) { }
+static inline struct pci_ops *pci_mcfg_get_ops(struct acpi_pci_root *root)
+{ return NULL; }
+static inline void __iomem *
+pci_mcfg_dev_base(struct pci_bus *bus, unsigned int devfn, int offset)
+{ return NULL; }
+#endif
+
#ifdef CONFIG_ACPI_PCI_SLOT
void acpi_pci_slot_init(void);
void acpi_pci_slot_enumerate(struct pci_bus *bus);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 6ae25aae88fd..9adbb5b25786 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -409,6 +409,8 @@ struct pci_host_bridge {
struct device dev;
struct pci_bus *bus; /* root bus */
struct list_head windows; /* resource_entry */
+ u8 (*swizzle_irq)(struct pci_dev *, u8 *); /* platform irq swizzler */
+ int (*map_irq)(struct pci_dev *, u8, u8);
void (*release_fn)(struct pci_host_bridge *);
void *release_data;
unsigned int ignore_reset_delay:1; /* for entire hierarchy */
@@ -1128,8 +1130,7 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus);
void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus);
void pdev_enable_device(struct pci_dev *);
int pci_enable_resources(struct pci_dev *, int mask);
-void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
- int (*)(const struct pci_dev *, u8, u8));
+void pci_assign_irq(struct pci_dev *dev);
#define HAVE_PCI_REQ_REGIONS 2
int __must_check pci_request_regions(struct pci_dev *, const char *);
int __must_check pci_request_regions_exclusive(struct pci_dev *, const char *);
@@ -1946,6 +1947,16 @@ static inline struct irq_domain *
pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
#endif /* CONFIG_OF */
+#ifdef CONFIG_ACPI
+struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus);
+
+void
+pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *));
+#else
+static inline struct irq_domain *
+pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; }
+#endif
+
#ifdef CONFIG_EEH
static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
{
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
index 027b1f43f12d..befac2ec82f8 100644
--- a/include/linux/watchdog.h
+++ b/include/linux/watchdog.h
@@ -25,6 +25,7 @@ struct watchdog_device;
* @ping: The routine that sends a keepalive ping to the watchdog device.
* @status: The routine that shows the status of the watchdog device.
* @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
+ * @set_pretimeout:The routine for setting the watchdog devices pretimeout value (in seconds).
* @get_timeleft:The routine that gets the time left before a reset (in seconds).
* @ref: The ref operation for dyn. allocated watchdog_device structs
* @unref: The unref operation for dyn. allocated watchdog_device structs
@@ -44,6 +45,7 @@ struct watchdog_ops {
int (*ping)(struct watchdog_device *);
unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int);
+ int (*set_pretimeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *);
void (*ref)(struct watchdog_device *);
void (*unref)(struct watchdog_device *);
@@ -62,6 +64,9 @@ struct watchdog_ops {
* @timeout: The watchdog devices timeout value (in seconds).
* @min_timeout:The watchdog devices minimum timeout value (in seconds).
* @max_timeout:The watchdog devices maximum timeout value (in seconds).
+ * @pretimeout: The watchdog devices pretimeout value.
+ * @min_pretimeout:The watchdog devices minimum pretimeout value.
+ * @max_pretimeout:The watchdog devices maximum pretimeout value.
* @driver-data:Pointer to the drivers private data.
* @lock: Lock for watchdog core internal use only.
* @status: Field that contains the devices internal status bits.
@@ -88,6 +93,9 @@ struct watchdog_device {
unsigned int timeout;
unsigned int min_timeout;
unsigned int max_timeout;
+ unsigned int pretimeout;
+ unsigned int min_pretimeout;
+ unsigned int max_pretimeout;
void *driver_data;
struct mutex lock;
unsigned long status;
@@ -127,7 +135,21 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
* than the maximum timeout.
*/
return t < wdd->min_timeout ||
- (wdd->max_timeout && t > wdd->max_timeout);
+ (wdd->max_timeout && t > wdd->max_timeout) ||
+ (wdd->pretimeout && t <= wdd->pretimeout);
+}
+
+/*
+ * Use the following function to check if a pretimeout value is invalid.
+ * It can be "0", that means we don't use pretimeout.
+ * This function returns false, when pretimeout is 0.
+ */
+static inline bool watchdog_pretimeout_invalid(struct watchdog_device *wdd,
+ unsigned int t)
+{
+ return t && (t < wdd->min_pretimeout ||
+ (wdd->max_pretimeout && t > wdd->max_pretimeout) ||
+ (wdd->timeout && t >= wdd->timeout));
}
/* Use the following functions to manipulate watchdog driver specific data */
@@ -142,8 +164,17 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
}
/* drivers/watchdog/watchdog_core.c */
-extern int watchdog_init_timeout(struct watchdog_device *wdd,
- unsigned int timeout_parm, struct device *dev);
+int watchdog_init_timeouts(struct watchdog_device *wdd,
+ unsigned int pretimeout_parm,
+ unsigned int timeout_parm,
+ struct device *dev);
+static inline int watchdog_init_timeout(struct watchdog_device *wdd,
+ unsigned int timeout_parm,
+ struct device *dev)
+{
+ return watchdog_init_timeouts(wdd, 0, timeout_parm, dev);
+}
+
extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 22aa9612ef7c..7f34d98ebfc4 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -70,7 +70,7 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
{
struct irqchip_fwid *fwid;
- if (WARN_ON(fwnode->type != FWNODE_IRQCHIP))
+ if (WARN_ON(!is_fwnode_irqchip(fwnode)))
return;
fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2ce8826f1053..3cf8cbae7b7c 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -143,7 +143,6 @@ static struct console *exclusive_console;
static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
static int selected_console = -1;
-static int preferred_console = -1;
int console_set_on_cmdline;
EXPORT_SYMBOL(console_set_on_cmdline);
@@ -2431,6 +2430,25 @@ static int __init keep_bootcon_setup(char *str)
early_param("keep_bootcon", keep_bootcon_setup);
+static DEFINE_MUTEX(acpi_consoles_delayed_mutex);
+static struct console *acpi_consoles_delayed;
+
+void acpi_register_consoles_try_again(void)
+{
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ while (acpi_consoles_delayed) {
+
+ struct console *c = acpi_consoles_delayed;
+
+ acpi_consoles_delayed = acpi_consoles_delayed->next;
+
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+ register_console(c);
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ }
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+}
+
/*
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
@@ -2456,6 +2474,7 @@ void register_console(struct console *newcon)
unsigned long flags;
struct console *bcon = NULL;
struct console_cmdline *c;
+ static bool preferred_console;
if (console_drivers)
for_each_console(bcon)
@@ -2482,15 +2501,15 @@ void register_console(struct console *newcon)
if (console_drivers && console_drivers->flags & CON_BOOT)
bcon = console_drivers;
- if (preferred_console < 0 || bcon || !console_drivers)
- preferred_console = selected_console;
+ if (!preferred_console || bcon || !console_drivers)
+ preferred_console = selected_console >= 0;
/*
* See if we want to use this console driver. If we
* didn't select a console we take the first one
* that registers here.
*/
- if (preferred_console < 0) {
+ if (!preferred_console) {
if (newcon->index < 0)
newcon->index = 0;
if (newcon->setup == NULL ||
@@ -2498,7 +2517,7 @@ void register_console(struct console *newcon)
newcon->flags |= CON_ENABLED;
if (newcon->device) {
newcon->flags |= CON_CONSDEV;
- preferred_console = 0;
+ preferred_console = true;
}
}
}
@@ -2533,13 +2552,35 @@ void register_console(struct console *newcon)
newcon->flags |= CON_ENABLED;
if (i == selected_console) {
newcon->flags |= CON_CONSDEV;
- preferred_console = selected_console;
+ preferred_console = true;
}
break;
}
- if (!(newcon->flags & CON_ENABLED))
- return;
+ if (!(newcon->flags & CON_ENABLED)) {
+ char *opts;
+ int err;
+
+ if (newcon->index < 0)
+ newcon->index = 0;
+
+ err = console_acpi_match(newcon, &opts);
+
+ if (err == -EAGAIN) {
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ newcon->next = acpi_consoles_delayed;
+ acpi_consoles_delayed = newcon;
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+ return;
+ } else if (err < 0) {
+ return;
+ } else {
+ if (newcon->setup && newcon->setup(newcon, opts) != 0)
+ return;
+ newcon->flags |= CON_ENABLED | CON_CONSDEV;
+ preferred_console = true;
+ }
+ }
/*
* If we have a bootconsole, and are switching to a real console,
@@ -2612,34 +2653,41 @@ void register_console(struct console *newcon)
}
EXPORT_SYMBOL(register_console);
+static int delete_from_console_list(struct console **list, struct console *c)
+{
+ while (*list) {
+ struct console *cur = *list;
+
+ if (cur == c) {
+ *list = cur->next;
+ return 0;
+ }
+ list = &cur->next;
+ }
+ return 1;
+}
+
int unregister_console(struct console *console)
{
- struct console *a, *b;
int res;
pr_info("%sconsole [%s%d] disabled\n",
(console->flags & CON_BOOT) ? "boot" : "" ,
console->name, console->index);
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ res = delete_from_console_list(&acpi_consoles_delayed, console);
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+ if (res == 0)
+ return res;
+
res = _braille_unregister_console(console);
if (res)
return res;
- res = 1;
console_lock();
- if (console_drivers == console) {
- console_drivers=console->next;
- res = 0;
- } else if (console_drivers) {
- for (a=console_drivers->next, b=console_drivers ;
- a; b=a, a=b->next) {
- if (a == console) {
- b->next = a->next;
- res = 0;
- break;
- }
- }
- }
+
+ res = delete_from_console_list(&console_drivers, console);
if (!res && (console->flags & CON_EXTENDED))
nr_ext_console_drivers--;
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index 69bca185c471..b02e49e1981f 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -21,6 +21,7 @@
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <linux/interrupt.h>
+#include <linux/acpi.h>
#include <clocksource/arm_arch_timer.h>
#include <asm/arch_timer.h>
@@ -381,9 +382,53 @@ static const struct of_device_id arch_timer_of_match[] = {
{},
};
-int kvm_timer_hyp_init(void)
+static int kvm_timer_ppi_dt_parse(unsigned int *ppi)
{
struct device_node *np;
+
+ np = of_find_matching_node(NULL, arch_timer_of_match);
+ if (!np)
+ return -ENODEV;
+
+ *ppi = irq_of_parse_and_map(np, 2);
+ if (*ppi == 0) {
+ of_node_put(np);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+struct acpi_table_gtdt *gtdt_acpi;
+static void arch_timer_acpi_parse(struct acpi_table_header *table)
+{
+ gtdt_acpi = container_of(table, struct acpi_table_gtdt, header);
+}
+
+static int kvm_timer_ppi_acpi_parse(unsigned int *ppi)
+{
+ int gsi, trigger, polarity;
+
+ /* Get the interrupt number from the GTDT table */
+ acpi_table_parse(ACPI_SIG_GTDT,
+ (acpi_tbl_table_handler)arch_timer_acpi_parse);
+
+ if (!gtdt_acpi->virtual_timer_interrupt)
+ return -EINVAL;
+
+ gsi = gtdt_acpi->virtual_timer_interrupt;
+ trigger = gtdt_acpi->virtual_timer_flags & ACPI_GTDT_INTERRUPT_MODE;
+ polarity = (gtdt_acpi->virtual_timer_flags &
+ ACPI_GTDT_INTERRUPT_POLARITY) >> 1;
+ *ppi = acpi_register_gsi(NULL, gsi, trigger, polarity);
+
+ return 0;
+}
+#endif
+
+int kvm_timer_hyp_init(void)
+{
unsigned int ppi;
int err;
@@ -391,19 +436,20 @@ int kvm_timer_hyp_init(void)
if (!timecounter)
return -ENODEV;
- np = of_find_matching_node(NULL, arch_timer_of_match);
- if (!np) {
- kvm_err("kvm_arch_timer: can't find DT node\n");
- return -ENODEV;
- }
+ /* PPI parsing: try DT first, then ACPI */
+ err = kvm_timer_ppi_dt_parse(&ppi);
+#ifdef CONFIG_ACPI
+ if (err && !acpi_disabled)
+ err = kvm_timer_ppi_acpi_parse(&ppi);
+#endif
- ppi = irq_of_parse_and_map(np, 2);
- if (!ppi) {
- kvm_err("kvm_arch_timer: no virtual timer interrupt\n");
- err = -EINVAL;
- goto out;
+ if (err) {
+ kvm_err("kvm_arch_timer: can't find virtual timer info or "
+ "config virtual timer interrupt\n");
+ return err;
}
+ /* configure IRQ handler */
err = request_percpu_irq(ppi, kvm_arch_timer_handler,
"kvm guest timer", kvm_get_running_vcpus());
if (err) {
@@ -426,14 +472,13 @@ int kvm_timer_hyp_init(void)
goto out_free;
}
- kvm_info("%s IRQ%d\n", np->name, ppi);
+ kvm_info("timer IRQ%d\n", ppi);
on_each_cpu(kvm_timer_init_interrupt, NULL, 1);
goto out;
out_free:
free_percpu_irq(ppi, kvm_get_running_vcpus());
out:
- of_node_put(np);
return err;
}
diff --git a/virt/kvm/arm/vgic-v2.c b/virt/kvm/arm/vgic-v2.c
index ff02f08df74d..d89955c20477 100644
--- a/virt/kvm/arm/vgic-v2.c
+++ b/virt/kvm/arm/vgic-v2.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#include <linux/irqchip/arm-gic.h>
@@ -177,7 +178,7 @@ static const struct vgic_ops vgic_v2_ops = {
static struct vgic_params vgic_v2_params;
/**
- * vgic_v2_probe - probe for a GICv2 compatible interrupt controller in DT
+ * vgic_v2_dt_probe - probe for a GICv2 compatible interrupt controller in DT
* @node: pointer to the DT node
* @ops: address of a pointer to the GICv2 operations
* @params: address of a pointer to HW-specific parameters
@@ -186,9 +187,9 @@ static struct vgic_params vgic_v2_params;
* in *ops and the HW parameters in *params. Returns an error code
* otherwise.
*/
-int vgic_v2_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params)
+int vgic_v2_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
{
int ret;
struct resource vctrl_res;
@@ -267,3 +268,59 @@ out:
of_node_put(vgic_node);
return ret;
}
+
+#ifdef CONFIG_ACPI
+int vgic_v2_acpi_probe(struct acpi_madt_generic_interrupt *vgic_acpi,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
+{
+ struct vgic_params *vgic = &vgic_v2_params;
+ int irq_mode, ret;
+
+ /* IRQ trigger mode */
+ irq_mode = (vgic_acpi->flags & ACPI_MADT_VGIC_IRQ_MODE) ?
+ ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE;
+ vgic->maint_irq = acpi_register_gsi(NULL, vgic_acpi->vgic_interrupt,
+ irq_mode, ACPI_ACTIVE_HIGH);
+ if (!vgic->maint_irq) {
+ kvm_err("Cannot register VGIC ACPI maintenance irq\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ /* GICH resource */
+ vgic->vctrl_base = ioremap(vgic_acpi->gich_base_address, SZ_8K);
+ if (!vgic->vctrl_base) {
+ kvm_err("cannot ioremap GICH memory\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ vgic->nr_lr = readl_relaxed(vgic->vctrl_base + GICH_VTR);
+ vgic->nr_lr = (vgic->nr_lr & 0x3f) + 1;
+
+ ret = create_hyp_io_mappings(vgic->vctrl_base,
+ vgic->vctrl_base + SZ_8K,
+ vgic_acpi->gich_base_address);
+ if (ret) {
+ kvm_err("Cannot map GICH into hyp\n");
+ goto out;
+ }
+
+ vgic->vcpu_base = vgic_acpi->gicv_base_address;
+ vgic->can_emulate_gicv2 = true;
+ kvm_register_device_ops(&kvm_arm_vgic_v2_ops, KVM_DEV_TYPE_ARM_VGIC_V2);
+
+ kvm_info("GICH base=0x%llx, GICV base=0x%llx, IRQ=%d\n",
+ (unsigned long long)vgic_acpi->gich_base_address,
+ (unsigned long long)vgic_acpi->gicv_base_address,
+ vgic->maint_irq);
+
+ vgic->type = VGIC_V2;
+ *ops = &vgic_v2_ops;
+ *params = vgic;
+
+out:
+ return ret;
+}
+#endif /* CONFIG_ACPI */
diff --git a/virt/kvm/arm/vgic-v3.c b/virt/kvm/arm/vgic-v3.c
index 487d6357b7e7..a184a6975dee 100644
--- a/virt/kvm/arm/vgic-v3.c
+++ b/virt/kvm/arm/vgic-v3.c
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#include <linux/irqchip/arm-gic-v3.h>
@@ -222,7 +223,7 @@ static const struct vgic_ops vgic_v3_ops = {
static struct vgic_params vgic_v3_params;
/**
- * vgic_v3_probe - probe for a GICv3 compatible interrupt controller in DT
+ * vgic_v3_dt_probe - probe for a GICv3 compatible interrupt controller in DT
* @node: pointer to the DT node
* @ops: address of a pointer to the GICv3 operations
* @params: address of a pointer to HW-specific parameters
@@ -231,9 +232,9 @@ static struct vgic_params vgic_v3_params;
* in *ops and the HW parameters in *params. Returns an error code
* otherwise.
*/
-int vgic_v3_probe(struct device_node *vgic_node,
- const struct vgic_ops **ops,
- const struct vgic_params **params)
+int vgic_v3_dt_probe(struct device_node *vgic_node,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
{
int ret = 0;
u32 gicv_idx;
@@ -296,3 +297,50 @@ out:
of_node_put(vgic_node);
return ret;
}
+
+#ifdef CONFIG_ACPI
+int vgic_v3_acpi_probe(struct acpi_madt_generic_interrupt *vgic_acpi,
+ const struct vgic_ops **ops,
+ const struct vgic_params **params)
+{
+ int ret = 0;
+ struct vgic_params *vgic = &vgic_v3_params;
+ int irq_mode;
+
+ /* IRQ trigger mode */
+ irq_mode = (vgic_acpi->flags & ACPI_MADT_VGIC_IRQ_MODE) ?
+ ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE;
+ vgic->maint_irq = acpi_register_gsi(NULL, vgic_acpi->vgic_interrupt,
+ irq_mode, ACPI_ACTIVE_HIGH);
+ if (!vgic->maint_irq) {
+ kvm_err("Cannot register VGIC ACPI maintenance irq\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
+ vgic->nr_lr = (ich_vtr_el2 & 0xf) + 1;
+ vgic->can_emulate_gicv2 = false;
+
+ vgic->vcpu_base = vgic_acpi->gicv_base_address;
+
+ if (vgic->vcpu_base == 0)
+ kvm_info("disabling GICv2 emulation\n");
+ else {
+ vgic->can_emulate_gicv2 = true;
+ kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
+ KVM_DEV_TYPE_ARM_VGIC_V2);
+ }
+
+ kvm_register_device_ops(&kvm_arm_vgic_v3_ops, KVM_DEV_TYPE_ARM_VGIC_V3);
+
+ vgic->vctrl_base = NULL;
+ vgic->type = VGIC_V3;
+ vgic->max_gic_vcpus = KVM_MAX_VCPUS;
+
+ *ops = &vgic_v3_ops;
+ *params = vgic;
+out:
+ return ret;
+}
+#endif /* CONFIG_ACPI */
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index 7a2f449bd85d..4841bc2967df 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -26,6 +26,7 @@
#include <linux/of_irq.h>
#include <linux/rculist.h>
#include <linux/uaccess.h>
+#include <linux/acpi.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_arm.h>
@@ -33,6 +34,7 @@
#include <trace/events/kvm.h>
#include <asm/kvm.h>
#include <kvm/iodev.h>
+#include <linux/irqchip/arm-gic-v3.h>
#define CREATE_TRACE_POINTS
#include "trace.h"
@@ -2390,32 +2392,148 @@ static struct notifier_block vgic_cpu_nb = {
};
static const struct of_device_id vgic_ids[] = {
- { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_probe, },
- { .compatible = "arm,cortex-a7-gic", .data = vgic_v2_probe, },
- { .compatible = "arm,gic-400", .data = vgic_v2_probe, },
- { .compatible = "arm,gic-v3", .data = vgic_v3_probe, },
+ { .compatible = "arm,cortex-a15-gic", .data = vgic_v2_dt_probe, },
+ { .compatible = "arm,cortex-a7-gic", .data = vgic_v2_dt_probe, },
+ { .compatible = "arm,gic-400", .data = vgic_v2_dt_probe, },
+ { .compatible = "arm,gic-v3", .data = vgic_v3_dt_probe, },
{},
};
-int kvm_vgic_hyp_init(void)
+static int kvm_vgic_dt_probe(void)
{
const struct of_device_id *matched_id;
const int (*vgic_probe)(struct device_node *,const struct vgic_ops **,
const struct vgic_params **);
struct device_node *vgic_node;
- int ret;
vgic_node = of_find_matching_node_and_match(NULL,
vgic_ids, &matched_id);
- if (!vgic_node) {
- kvm_err("error: no compatible GIC node found\n");
+ if (!vgic_node)
return -ENODEV;
- }
vgic_probe = matched_id->data;
- ret = vgic_probe(vgic_node, &vgic_ops, &vgic);
- if (ret)
+
+ return vgic_probe(vgic_node, &vgic_ops, &vgic);
+}
+
+#ifdef CONFIG_ACPI
+u8 gic_version = ACPI_MADT_GIC_VERSION_NONE;
+phys_addr_t dist_phy_base;
+static struct acpi_madt_generic_interrupt *vgic_acpi;
+
+static void gic_get_acpi_header(struct acpi_subtable_header *header)
+{
+ vgic_acpi = (struct acpi_madt_generic_interrupt *)header;
+}
+
+static int gic_parse_distributor(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_distributor *dist;
+
+ dist = (struct acpi_madt_generic_distributor *)header;
+
+ if (BAD_MADT_ENTRY(dist, end))
+ return -EINVAL;
+
+ gic_version = dist->version;
+ dist_phy_base = dist->base_address;
+
+ return 0;
+}
+
+static int gic_match_redist(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ return 0;
+}
+
+static bool gic_redist_is_present(void)
+{
+ int count;
+
+ /* scan MADT table to find if we have redistributor entries */
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR,
+ gic_match_redist, 0);
+
+ return (count > 0) ? true : false;
+}
+
+static int kvm_vgic_acpi_probe(void)
+{
+ u32 reg;
+ int count;
+ void __iomem *dist_base;
+ int ret;
+
+ /* MADT table */
+ ret = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
+ (acpi_tbl_entry_handler)gic_get_acpi_header, 0);
+ if (!ret) {
+ pr_err("Failed to get MADT VGIC CPU entry\n");
+ return -ENODEV;
+ }
+
+ /* detect GIC version */
+ count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
+ gic_parse_distributor, 0);
+ if (count <= 0) {
+ pr_err("No valid GIC distributor entry exists\n");
+ return -ENODEV;
+ }
+ if (gic_version >= ACPI_MADT_GIC_VERSION_RESERVED) {
+ pr_err("Invalid GIC version %d in MADT\n", gic_version);
+ return -EINVAL;
+ }
+
+ /* falls back to manual hardware discovery under ACPI 5.1 */
+ if (gic_version == ACPI_MADT_GIC_VERSION_NONE) {
+ if (gic_redist_is_present()) {
+ dist_base = ioremap(dist_phy_base, SZ_64K);
+ if (!dist_base)
+ return -ENOMEM;
+
+ reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
+ if (reg == GIC_PIDR2_ARCH_GICv3)
+ gic_version = ACPI_MADT_GIC_VERSION_V3;
+ else
+ gic_version = ACPI_MADT_GIC_VERSION_V4;
+
+ iounmap(dist_base);
+ } else {
+ gic_version = ACPI_MADT_GIC_VERSION_V2;
+ }
+ }
+
+ switch (gic_version) {
+ case ACPI_MADT_GIC_VERSION_V2:
+ ret = vgic_v2_acpi_probe(vgic_acpi, &vgic_ops, &vgic);
+ break;
+ case ACPI_MADT_GIC_VERSION_V3:
+ ret = vgic_v3_acpi_probe(vgic_acpi, &vgic_ops, &vgic);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_ACPI */
+
+int kvm_vgic_hyp_init(void)
+{
+ int ret;
+
+ ret = kvm_vgic_dt_probe();
+#ifdef CONFIG_ACPI
+ if (ret && !acpi_disabled)
+ ret = kvm_vgic_acpi_probe();
+#endif
+
+ if (ret) {
+ kvm_err("error: KVM vGIC probing failed\n");
return ret;
+ }
ret = request_percpu_irq(vgic->maint_irq, vgic_maintenance_handler,
"vgic", kvm_get_running_vcpus());